lib/method_parser.rb
# Parses method code
class MethodParser
attr_reader :method,
:name,
:text,
:parameters,
:param_tags, # All method parameters tags
:option_tags, # Only options tags
:trollop_opts,
:default_values,
:required_parameters
attr_accessor :cmd_opts
TYPES_MAPPINGS = {
'String' => :string,
'Integer' => :int,
'Fixnum' => :int,
'Float' => :float,
'Boolean' => :boolean,
'Array(String)' => :strings,
'Array(Integer)' => :ints,
'Array(Fixnum)' => :ints,
'Array(Float)' => :floats,
'Array(Boolean)' => :booleans
}.freeze
# @param [YARD::CodeObjects::MethodObject] method YARD method object to be parsed
def initialize(method)
@method = method
@name = @method.name
@text = FileParser.select_runnable_tags(@method).map(&:text).join("\n")
@parameters = @method.parameters
@default_values = default_params
@param_tags = FileParser.select_param_tags @method
@option_tags = FileParser.select_option_tags @method
@required_parameters = @param_tags.select { |t| t.tag_name == 'param' }.map(&:name)
@cmd_opts = nil
same_params = param_tags_names & option_tags_names
unless same_params.count.zero?
raise(
RubyFireCLIError,
"You have the same name for @param and @option attribute(s): #{same_params.join(', ')}.
Use different names to `ruby_fire_cli` be able to run #{@name} method."
)
end
@trollop_opts = prepare_opts_for_trollop
end
def prepare_opts_for_trollop
result = []
param_tags.each do |tag|
tag_name = tag.name
tag_text = tag.text
tag_type = tag.type
if tag_type == "Hash"
options = option_tags.select { |t| t.name == tag.name }
if options.count > 0
options.each do |option|
option_name = option.pair.name.delete(':')
option_text = option.pair.text
option_type = option.pair.type
result << [
option_name.to_sym,
"(Ruby class: #{option_type}) " + option_text.to_s,
type: parse_type(option_type)
]
end
else
result << [
tag_name.to_sym,
"(Ruby class: #{tag_type}) " + tag_text.to_s,
type: parse_type(tag_type)
]
end
else
result << [
tag_name.to_sym,
"(Ruby class: #{tag_type}) " + tag_text.to_s,
type: parse_type(tag_type)
]
end
end
result
end
def params_array
options_groups = {}
get_params = {}
@parameters.map(&:first).map.with_index { |p, i| [i, p] }.to_h.each do |index, name|
if options_group?(name)
options_groups[index] = name
get_params[index] = option_as_hash(name)
else
get_params[index] = @cmd_opts[name.to_sym]
end
end
get_params = get_params.to_a.sort_by { |a| a[0] }.reverse
stop_delete = false
get_params.delete_if do |a|
index = a[0]
value = a[1]
result = value.nil?
result = value == {} if options_groups[index]
stop_delete = true unless result
next if stop_delete
end
get_params.sort_by { |a| a[0] }.map { |a| a[1] }
end
# @return [Array(String)] Names of parameters
def param_tags_names
param_tags.map(&:name)
end
# @return [Array(String)] Names of options
def option_tags_names
option_tags.map { |t| t.pair.name.delete(':') }
end
# Check if the name is an option
#
# @param [String] param_name name of parameter to be verified
# @return [Boolean] true if current parameter name is an option key
def options_group?(param_name)
option_tags.any? { |t| t.name == param_name }
end
private
def parse_type(yard_type)
result = TYPES_MAPPINGS[yard_type]
raise RubyFireCLIError, "Unsupported YARD type: #{yard_type}" unless result
result
end
# @return [Hash] default values for parameters
def default_params
@parameters.to_a.map do |array|
array.map do |a|
if a
['"', "'"].include?(a[0]) && ['"', "'"].include?(a[-1]) ? a[1..-2] : a
else
a
end
end
end.to_h
end
# @return [Hash] options parameter as Hash
def option_as_hash(options_group_name)
result = {}
option_tags.select { |t| t.name == options_group_name }.each do |t|
option_name = t.pair.name.delete(':')
option_value = @cmd_opts[option_name.to_sym]
result[option_name] = option_value if option_value
end
result
end
end