I have a set of values, and I want to make sure each case is handled. I am curious what the Crystal way is to do this. At first I tried enums, but I don’t think there’s a compiler mechanism forcing me to address each enum value. So then I tried separate classes for each value, and method overloading appears to do the trick. I want to share these to get some feedback from more experienced folks.
Is the class + method overload approach the “crystal way”?
note: the OptionParser functionality isn’t important here, it’s just a mechanism to let me select the value at runtime
enum approach
While the enum definition looks very nice, I don’t think this quite does what I want. I can delete any of the case conditions, and it will happily compile. If I remove the case statement, there’s just no output, which is expected. Is there a way for the compiler to check whether I’ve covered all cases? I don’t think there’s a way for methods to pattern match on values, is there? Methods only pattern match on types I think.
# using enums appears to let some values be missed
# (i.e. no else statement is required)
require "option_parser"
enum Command
Get
Put
Delete
Unknown
end
command = Command::Unknown
OptionParser.parse do |parser|
parser.on("get", "get") { command = Command::Get }
parser.on("put", "put") { command = Command::Put }
parser.on("del", "delete") { command = Command::Delete }
end
case command
when Command::Get
puts "get"
when Command::Put
puts "put"
when Command::Delete
puts "delete"
else
STDERR.puts "E: unknown"
exit 1
end
class + method overload approach
This seems to do what I want. My only question is if this is the cleanest way to get this behavior. If I comment out any of the run(...)
definitions, it fails to compile - so I can be confident that I’m covering each case.
30 | run(command)
^------
Error: expected argument #1 to 'run' to be Delete, Get or Unknown, not (Delete | Get | Put | Unknown)
Overloads are:
- run(c : Get)
- run(c : Delete)
- run(c : Unknown)
*** Error code 1
code:
# using classes appears to require that all types are handled
require "option_parser"
class Get; end
class Put; end
class Delete; end
class Unknown; end
command = Unknown.new
OptionParser.parse do |parser|
parser.on("get", "get") { command = Get.new }
parser.on("put", "put") { command = Put.new }
parser.on("del", "delete") { command = Delete.new }
end
def run(c : Get)
puts "get"
end
def run(c : Put)
puts "put"
end
def run(c : Delete)
puts "delete"
end
def run(c : Unknown)
STDERR.puts "E: unknown"
end
run(command)