Crystal では何でもかんでも配列にしてあれこれするのは良くないぜ!
Rake のコードを Crystal に移植していたら、配列を関数の引数として展開できないせいでまずいことが起こった。
def standard_gazel_options(options) [ [ "-A", "--all", "Show all tasks, even uncommented ones (in combination with -T or -D)", ->(value : String) { options.show_all_tasks = value } ], [ "--backtrace=[OUT]", "Enable full backtrace. OUT can be stderr (default) or stdout", ->(value : String) { options.backtrace = true #select_trace_output options, "backtrace", value value } ], [ "-B", "--build-all", "Build all prerequisites, including those which are up-to-date.", ->(value : String) { options.build_all = true value } ]#, # [] ] end parse = OptionParser.new do |opts| opts.banner = "gazel [-f <gazel file>] {options} targets..." opts.separator "" opts.separator "Options are ..." opts.on "-h", "--help", "Display this help message" do puts opts exit end standard_gazel_options(options).each do |args| opts.on *args # エラー!!! # Array を引数として展開することはできない! end end
Crystal には Tuple
があるので、それだったら展開できるんだけど、Array(T)
から Tuple
に変換する便利な関数が無いっぽい。
それに Crystal では、standard_gazel_options
関数が返す配列の型は Array(Array(String | String -> String))
なので、
要素が String
なのか、String -> String
なのかは確かめる必要がある。
それは激しくめんどくさいので、こういう場合は、Struct
を作って、それの配列として standard_gazel_options
関数が返すことにしよう。
こんな風に、OptionDefine
という構造体(Struct)を定義して:
struct OptionDefine property short_flag property long_flag property description property block def initialize(@short_flag, @long_flag, @description, @block) end end
standard_gazel_options
関数をこういう風に書き換えよう:
def standard_gazel_options(options) [ OptionDefine.new( "-A", "--all", "Show all tasks, even uncommented ones (in combination with -T or -D)", ->(value : String) { options.show_all_tasks = value } ), OptionDefine.new( nil, "--backtrace=[OUT]", "Enable full backtrace. OUT can be stderr (default) or stdout", ->(value : String) { options.backtrace = true #select_trace_output options, "backtrace", value value } ), OptionDefine.new( "-B", "--build-all", "Build all prerequisites, including those which are up-to-date.", ->(value : String) { options.build_all = true value } ) ] end
OptionDefine
のメンバーを渡すところはこういう風に書くかもしれない:
standard_gazel_options(options).each do |args| if args.short_flag.nil? then opts.on args.long_flag, args.description, &args.block else opts.on args.short_flag, args.long_flag, args.description, &args.block end end
(๑´ڡ`๑) いいかも