Module: Rubycom

Defined in:
lib/rubycom.rb,
lib/rubycom/version.rb,
lib/rubycom/commands.rb,
lib/rubycom/arguments.rb,
lib/rubycom/documentation.rb

Overview

Upon inclusion in another Module, Rubycom will attempt to call a method in the including module by parsing ARGV for a method name and a list of arguments. If found Rubycom will call the method specified in ARGV with the parameters parsed from the remaining arguments If a Method match can not be made, Rubycom will print help instead by parsing source comments from the including module or it’s included modules.

Defined Under Namespace

Modules: Arguments, Commands, Documentation Classes: CLIError

Constant Summary collapse

VERSION =
"0.3.0"

Class Method Summary collapse

Class Method Details

.included(base) ⇒ Object

Detects that Rubycom was included in another module and calls Rubycom#run

Raises:



20
21
22
23
24
25
26
27
28
# File 'lib/rubycom.rb', line 20

def self.included(base)
  raise CLIError, 'base must be a module' if base.class != Module
  base_file_path = caller.first.gsub(/:\d+:.+/, '')
  if base_file_path == $0
    base.module_eval {
      Rubycom.run(base, ARGV)
    }
  end
end

.register_completions(base) ⇒ String

Inserts a tab completion into the current user’s .bash_profile with a command entry to register the function for the current running ruby file



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/rubycom.rb', line 151

def self.register_completions(base)
  completion_function = "\n  _\#{base}_complete() {\n    COMPREPLY=()\n    local completions=\"$(ruby \#{File.absolute_path($0)} tab_complete ${COMP_WORDS[*]} 2>/dev/null)\"\n    COMPREPLY=( $(compgen -W \"$completions\") )\n  }\n  complete -o bashdefault -o default -o nospace -F _\#{base}_complete \#{$0.split('/').last}\n  END\n\n  already_registered = File.readlines(\"\#{Dir.home}/.bash_profile\").map { |line| line.include?(\"_\#{base}_complete()\") }.reduce(:|) rescue false\n  if already_registered\n    \"Completion function for \#{base} already registered.\"\n  else\n    File.open(\"\#{Dir.home}/.bash_profile\", 'a+') { |file|\n      file.write(completion_function)\n    }\n    \"Registration complete, run 'source \#{Dir.home}/.bash_profile' to enable auto-completion.\"\n  end\nend\n".gsub(/^ {4}/, '')

.run(base, args = []) ⇒ Object

Looks up the command specified in the first arg and executes with the rest of the args



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/rubycom.rb', line 34

def self.run(base, args=[])
  begin
    raise CLIError, "Invalid base class invocation: #{base}" if base.nil?
    command = args[0] || nil
    arguments = args[1..-1] || []

    case command
      when 'register_completions'
        puts self.register_completions(base)
      when 'tab_complete'
        puts self.tab_complete(base, args)
      when 'help'
        help_topic = arguments[0]
        if help_topic.nil?
          usage = Documentation.get_usage(base)
          default_usage = Documentation.get_default_commands_usage
          puts usage
          puts default_usage
          return usage+"\n"+default_usage
        elsif help_topic == 'job'
          usage = Documentation.get_job_usage(base)
          puts usage
          return usage
        elsif help_topic == 'register_completions'
          usage = Documentation.get_register_completions_usage(base)
          puts usage
          return usage
        elsif help_topic == 'tab_complete'
          usage = Documentation.get_tab_complete_usage(base)
          puts usage
          return usage
        else
          cmd_usage = Documentation.get_command_usage(base, help_topic, arguments[1..-1])
          puts cmd_usage
          return cmd_usage
        end
      when 'job'
        begin
          raise CLIError, 'No job specified' if arguments[0].nil? || arguments[0].empty?
          job_hash = YAML.load_file(arguments[0])
          job_hash = {} if job_hash.nil?
          STDOUT.sync = true
          if arguments.delete('-test') || arguments.delete('--test')
            puts "[Test Job #{arguments[0]}]"
            job_hash['steps'].each { |step, step_hash|
              step = "[Step: #{step}/#{job_hash['steps'].length}]"
              context = step_hash.select { |key| key!="cmd" }.map { |key, val| "[#{key}: #{val}]" }.join(' ')
              env = job_hash['env'] || {}
              env.each { |key, val| step_hash['cmd'].gsub!("env[#{key}]", "#{((val.class == String)&&(val.match(/\w+/))) ? "\"#{val}\"" : val}") }
              cmd = "[cmd: #{step_hash['cmd']}]"
              puts "#{[step, context, cmd].join(' ')}"
            }
          else
            puts "[Job #{arguments[0]}]"
            job_hash['steps'].each { |step, step_hash|
              step = "[Step: #{step}/#{job_hash['steps'].length}]"
              context = step_hash.select { |key| key!="cmd" }.map { |key, val| "[#{key}: #{val}]" }.join(' ')
              env = job_hash['env'] || {}
              env.each { |key, val| step_hash['cmd'].gsub!("env[#{key}]", "#{((val.class == String)&&(val.match(/\w+/))) ? "\"#{val}\"" : val}") }
              cmd = "[cmd: #{step_hash['cmd']}]"
              puts "#{[step, context, cmd].join(' ')}"
              system(step_hash['cmd'])
            }
          end
        rescue CLIError => e
          $stderr.puts e
        end
      else
        output = self.run_command(base, command, arguments)
        std_output = nil
        std_output = output.to_yaml unless [String, NilClass, TrueClass, FalseClass, Fixnum, Float, Symbol].include?(output.class)
        puts std_output || output
        return output
    end

  rescue CLIError => e
    $stderr.puts e
    $stderr.puts Documentation.get_summary(base)
  end
end

.run_command(base, command, arguments = []) ⇒ Object

Calls the given Method#name on the given Module after parsing the given Array of arguments

Raises:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rubycom.rb', line 120

def self.run_command(base, command, arguments=[])
  arguments = [] if arguments.nil?
  raise CLIError, 'No command specified.' if command.nil? || command.length == 0
  begin
    raise CLIError, "Invalid Command: #{command}" unless Commands.get_top_level_commands(base).include? command.to_sym
    if base.included_modules.map { |mod| mod.name.to_sym }.include?(command.to_sym)
      self.run_command(eval(command), arguments[0], arguments[1..-1])
    else
      method = base.public_method(command.to_sym)
      raise CLIError, "No public method found for symbol: #{command.to_sym}" if method.nil?
      param_defs = Arguments.get_param_definitions(method)
      args = Arguments.parse_arguments(param_defs, arguments)
      flatten = false
      params = method.parameters.map { |arr| flatten = true if arr[0]==:rest; args[arr[1]] }
      if flatten
        rest_arr = params.delete_at(-1)
        rest_arr.each { |arg| params << arg }
      end
      (arguments.nil? || arguments.empty?) ? method.call : method.call(*params)
    end
  rescue CLIError => e
    $stderr.puts e
    $stderr.puts Documentation.get_command_usage(base, command, arguments)
  end
end

.tab_complete(base, arguments) ⇒ Array

Discovers a list of possible matches to the given arguments Intended for use with bash tab completion



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rubycom.rb', line 179

def self.tab_complete(base, arguments)
  arguments = [] if arguments.nil?
  args = (arguments.include?("tab_complete")) ? arguments[2..-1] : arguments
  matches = ['']
  if args.nil? || args.empty?
    matches = Rubycom::Commands.get_top_level_commands(base).map { |sym| sym.to_s }
  elsif args.length == 1
    matches = Rubycom::Commands.get_top_level_commands(base).map { |sym| sym.to_s }.select { |word| !word.match(/^#{args[0]}/).nil? }
    if matches.size == 1 && matches[0] == args[0]
      matches = self.tab_complete(Kernel.const_get(args[0].to_sym), args[1..-1])
    end
  elsif args.length > 1
    begin
      matches = self.tab_complete(Kernel.const_get(args[0].to_sym), args[1..-1])
    rescue Exception
      matches = ['']
    end
  end unless base.nil?
  matches = [''] if matches.nil? || matches.include?(args[0])
  matches
end