Михаил обнови решението на 02.11.2016 02:18 (преди около 8 години)
+module CommandLineArgumentMatchers
+ def argument?(arg)
+ arg.match /\A[^-]\S+\z/
+ end
+
+ def option?(arg)
+ arg.match(/\A-\w\S+?\z/) || arg.match(/\A--\w+(=\S+)?\z/)
+ end
+
+ def split_option_from_parameter(arg)
+ match_data = arg.match(/\A(?<name>-\w)(?<value>\S+?)\z/)
+ match_data ||= arg.match(/\A(?<name>--\w+)(=(?<value>\S+))?\z/)
+ [match_data[:name], match_data[:value]]
+ end
+end
+
+class CommandParser
+ def initialize(command_name)
+ @command_name = command_name
+ @arguments = []
+ @options = []
+ end
+
+ def argument(name, &block)
+ @arguments.push name: name, block: block
+ end
+
+ def option(short_name, long_name, description, &block)
+ @options.push short_name: short_name, long_name: long_name,
+ description: description, block: block
+ end
+
+ def option_with_parameter(short_name, long_name, description, param, &block)
+ @options.push short_name: short_name, long_name: long_name,
+ parameter: param, description: description,
+ block: block
+ end
+
+ def parse(command_runner, argv)
+ parse_arguments command_runner, argv
+ parse_options command_runner, argv
+ end
+
+ def help
+ arguments = @arguments.map { |arg| arg[:name] }
+ message = "Usage: #{@command_name} [#{arguments.join(' ')}]\n"
+ @options.each do |o|
+ message += " -#{o[:short_name]}, --#{o[:long_name]}"
+ message += '=' + o[:parameter] if o[:parameter]
+ message += ' ' + o[:description] + "\n"
+ end
+ message
+ end
+
+ private
+
+ def parse_arguments(command_runner, argv)
+ arguments = argv.select { |arg| argument? arg }
+ arguments.each_with_index do |value, i|
+ @arguments[i][:block].call(command_runner, value)
+ end
+ end
+
+ def parse_options(command_runner, argv)
+ options = argv.select { |arg| option? arg }
+ options.each do |option|
+ name, value = split_option_from_parameter option
+ match = @options.find { |o| option_matches? o, name }
+ match[:block].call(command_runner, value || true) if match
+ end
+ end
+
+ def option_matches?(option, argument)
+ matches_short_name = '-' + option[:short_name] == argument
+ matches_long_name = '--' + option[:long_name] == argument
+ matches_short_name || matches_long_name
+ end
+
+ include CommandLineArgumentMatchers
+end