2

First of all, this question is related but not solved by

Then about the question itself : when programming in Java with IDEs like Eclipse, it's possible to have a warning when we implement a switch statement on an enum, and we forgot some cases in the switch statement (very useful after adding an extra possible value to the enum and we forget to edit all switches based on this enum)

Is it possible to have the same kind of static analysis in Ruby ? Is there a way to implement enums so that we'd get a warning (maybe after running rubocop or something) if we forget to implement a case ?

EDIT

This "enum" I'm talking about could be any type of Set like object with a finite number of values, the most simplest form being an array of symbols, but maybe it is not enough/convenient to perform analysis with it hence why I am starting this question

On of my use case involve checking all possible errors after performing Policy checks

class CanShowArticlePolicy
  def call
    list_of_exceptions = [:unpublished, :deleted, 
  :offensive_content_detected]
    # business logic that returns either true or false and add exception information exception, can be mocked as
    @error = list_of_exceptions.sample
    false
  end
end

# in another file like a controller or service
article = Article.find(id)
policy = CanShowArticlePolicy.new(article)
if policy.call
  render_article
else
  # Where I'm trying to be exhaustive
  case policy.error # <== Goal : detect here we are swithing on an "enum" with finite values and we should be exhaustive
  when :unpublished
    render_unpublished_error
  when :deleted
    render_gone
  # <<= Here I would like to get a rubocop error because we've forgotten to handle the `:offensive_content_detected` case
  end

Maybe a solution would be to have instead something like an annotation

case enum_value # @exhaustive-case with ::CanShowArticlePolicy::ErrorEnum

and the annotation would have for effect of the static analysis trying to find a ::CanShowArticlePolicy::ErrorEnum array containing the symbols, and making sure there are as many when statements as number of items in the frozen ErrorEnum

Cyril Duchon-Doris
  • 12,964
  • 9
  • 77
  • 164
  • 4
    Can you clarify enums? Rails has enums, there's a couple ruby gems for enums. Are you talking about a custom made enum class or an existing implementation? – Jay Dorsey Aug 07 '19 at 14:42
  • 2
    If we define the word "enum" (not defined yet by OP) to mean "array of symbols" (or any other literal value), then yes, we absolutely can do this kind of static analysis with AST-based tools like rubocop, because the enum values are static (they appear as literal nodes in the AST). It's not possible though, to statically analyze certain expressions in case statements, however. For example: `when /regex/`. So, our analysis is limited to case statements that use simple equality comparison. – Jared Beck Aug 07 '19 at 15:48
  • It would be helpful to have an example. In Ruby an enum is an instance of the class [Enumerator](https://ruby-doc.org/core-2.6.3/Enumerator.html) and a "switch statement" is called a "case statement" and uses `===` methods for comparisons when `case` takes an argument (e.g., [String#===](https://ruby-doc.org/core-2.6.3/String.html#method-i-3D-3D-3D)). – Cary Swoveland Aug 07 '19 at 17:32
  • Thanks for your feedback, I have added an example, does this help ? I was really talking about an enum as a finite set, and we want to make sure our case statement will have as many `when` clauses as the number of items in the enum set. Then this set could be an `Array`, a `Set` containing just `Symbols` or even other types of custom `Object`s, whatever is more appropriate to tackling the problem of exhaustiveness – Cyril Duchon-Doris Aug 07 '19 at 23:25
  • If you're making a "Java-like" enum (which mean all the value are object) you can do the following : - Define a method in ErrorEnum that calls the correct method from sender (`render_gone` for example) using a instance variable of ErrorEnum. - Instead of using a `case when` (where you're 100% sure you can't check if all the enum value are checked without making a mock) you'll just call `policy.call_error_render_of(self)`. This way all the value should have a "error_render" and if you forgot to define one, it'll raise an error. – user11659763 Aug 08 '19 at 06:40
  • Okay so basically you're suggesting an inversion of control where the Enum/policy calls the render method of the controller ? This doesn't seem sound clean from an archi point of view : the Enum does not need to know how the error will be rendered, just to provide basic metadata regarding the error and then the controller renders an appropriate html/json response to add the relevant http status code, etc. – Cyril Duchon-Doris Aug 08 '19 at 13:46

0 Answers0