# encoding: UTF-8 # frozen_string_literal: true # Refinements # ======================================================================= using NRSER using NRSER::Types # Definitions # ======================================================================= module NRSER::Rash::CLI # Call a Rash function. # # @param [String] name # Name of the function. # # @param [Array] argv # The shell arguments. # def self.call name, argv logger.trace "calling #{ name.inspect }, argv: #{ argv.inspect }" # look for options options = {} args = argv.reject do |arg| # long options of form '--=' if m = arg.match(/\A--([^=]*)(=?)(.*)/) options[m[1].to_sym] = if m[2] == '' true else m[3] end true # short options of form '-' elsif arg.start_with? '-' arg[1..-1].each_char do |char| options[char] = true end true end end logger.debug "options: #{ options.inspect }" # options are the last arg, unless we didn't find any # # NOTE: this causes functions without an optional `options` arg # on the end to raise an exception when called with any options. # # i think this is the correct behavior: the function can't handle # options, and should error out. args << options unless options.empty? NRSER::Rash.load_functions if name.include? '.' namespace, method_name = name.split '.' namespace = namespace.split '::' else namespace = [] method_name = name end mod = NRSER::Rash::Functions namespace.each do |submod_name| mod = mod.const_get(submod_name.capitalize) end begin rtn = mod.method(method_name).call *args rescue Exception => error if NRSER::Rash.config( 'PRINT_ERRORS' ).truthy? if NRSER::Rash.config( 'STACKTRACE' ).truthy? raise error else logger.fatal error # this is the error code that ruby seems to exit with when you # don't check the exception exit 1 end end end logger.debug "return value: #{ rtn.inspect }" if formatted = format(rtn) puts formatted end exit 0 end # formats an object to write to `$stdout`. # returns `nil` if there's nothing to write (different than returning '', # indicating the empty string should be written). def self.format(obj) # TODO: should formatter be overridable with a env var? # formatter = begin config('FORMATTER'); rescue NameError => e; nil; end # logger.debug "formatter: #{ formatter.inspect }" if obj.rash_formatter # there is some sort of formatting info # even if the value is a string, we want to follow the instructions # hell, maybe we want to write the string out as json or something :/ if obj.rash_formatter.respond_to? :call # the value responds to `call`, so call it to get the value # TODO: should we check that it returns a `String`? obj.rash_formatter.call(obj) # NOTE: `Object.singleton_methods` returns an array of strings in 1.8 # and an array of symbols in 1.9, so use strings to work in # both. elsif NRSER::Rash::Formatters. singleton_methods. map {|_| _.to_s }. include? obj.rash_formatter.to_s # it's a symbol that identifies on of the {NRSER::Rash::Formatters} # class methods, so call that NRSER::Rash::Formatters.send obj.rash_formatter, obj else # whatever, call the default NRSER::Rash::Formatters.send config('DEFAULT_FORMATTER'), obj end else # there is no formatting info if obj.is_a? String # in the case that a method passed back a `String`, i think we should # consider it formatted and just print it obj elsif obj.nil? # the result is nil and there has been no attempt to format it in # a specific way that some program might need to recongize or # something. at this point, i'm going to say that the function # took some successful action and doesn't have anyhting to say about # it, which i think means we shouldn't print anything else # otherwise, send it to the default formatter NRSER::Rash::Formatters.send config('DEFAULT_FORMATTER'), obj end end end end # module NRSER::Rash::CLI