foreman 0.85.0 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +1 -1
 - data/data/export/systemd/master.target.erb +1 -1
 - data/data/export/systemd/process.service.erb +6 -3
 - data/lib/foreman/cli.rb +3 -3
 - data/lib/foreman/export/systemd.rb +7 -13
 - data/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb +103 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb +59 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions/directory.rb +118 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb +327 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb +103 -0
 - data/lib/foreman/vendor/thor/lib/thor/actions.rb +318 -0
 - data/lib/foreman/vendor/thor/lib/thor/base.rb +656 -0
 - data/lib/foreman/vendor/thor/lib/thor/command.rb +133 -0
 - data/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +85 -0
 - data/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
 - data/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
 - data/lib/foreman/vendor/thor/lib/thor/error.rb +32 -0
 - data/lib/foreman/vendor/thor/lib/thor/group.rb +281 -0
 - data/lib/foreman/vendor/thor/lib/thor/invocation.rb +177 -0
 - data/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
 - data/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
 - data/lib/foreman/vendor/thor/lib/thor/line_editor.rb +17 -0
 - data/lib/foreman/vendor/thor/lib/thor/parser/argument.rb +70 -0
 - data/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb +175 -0
 - data/lib/foreman/vendor/thor/lib/thor/parser/option.rb +146 -0
 - data/lib/foreman/vendor/thor/lib/thor/parser/options.rb +220 -0
 - data/lib/foreman/vendor/thor/lib/thor/parser.rb +4 -0
 - data/lib/foreman/vendor/thor/lib/thor/rake_compat.rb +71 -0
 - data/lib/foreman/vendor/thor/lib/thor/runner.rb +322 -0
 - data/lib/foreman/vendor/thor/lib/thor/shell/basic.rb +436 -0
 - data/lib/foreman/vendor/thor/lib/thor/shell/color.rb +149 -0
 - data/lib/foreman/vendor/thor/lib/thor/shell/html.rb +126 -0
 - data/lib/foreman/vendor/thor/lib/thor/shell.rb +81 -0
 - data/lib/foreman/vendor/thor/lib/thor/util.rb +268 -0
 - data/lib/foreman/vendor/thor/lib/thor/version.rb +3 -0
 - data/lib/foreman/vendor/thor/lib/thor.rb +492 -0
 - data/lib/foreman/version.rb +1 -1
 - data/man/foreman.1 +1 -1
 - data/spec/foreman/export/systemd_spec.rb +88 -46
 - data/spec/resources/export/systemd/{app-alpha@.service → app-alpha.1.service} +6 -3
 - data/spec/resources/export/systemd/{app-bravo@.service → app-alpha.2.service} +6 -3
 - data/spec/resources/export/systemd/app-bravo.1.service +18 -0
 - data/spec/resources/export/systemd/app.target +1 -1
 - data/spec/spec_helper.rb +1 -1
 - metadata +39 -22
 - data/data/export/systemd/process_master.target.erb +0 -2
 
| 
         @@ -0,0 +1,177 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Invocation
         
     | 
| 
      
 3 
     | 
    
         
            +
                def self.included(base) #:nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
                  base.extend ClassMethods
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # This method is responsible for receiving a name and find the proper
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # class and command for it. The key is an optional parameter which is
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # available only in class methods invocations (i.e. in Foreman::Thor::Group).
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def prepare_for_invocation(key, name) #:nodoc:
         
     | 
| 
      
 12 
     | 
    
         
            +
                    case name
         
     | 
| 
      
 13 
     | 
    
         
            +
                    when Symbol, String
         
     | 
| 
      
 14 
     | 
    
         
            +
                      Foreman::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else
         
     | 
| 
      
 16 
     | 
    
         
            +
                      name
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                # Make initializer aware of invocations and the initialization args.
         
     | 
| 
      
 22 
     | 
    
         
            +
                def initialize(args = [], options = {}, config = {}, &block) #:nodoc:
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @_initializer = [args, options, config]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  super
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                # Make the current command chain accessible with in a Foreman::Thor-(sub)command
         
     | 
| 
      
 29 
     | 
    
         
            +
                def current_command_chain
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @_invocations.values.flatten.map(&:to_sym)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # Receives a name and invokes it. The name can be a string (either "command" or
         
     | 
| 
      
 34 
     | 
    
         
            +
                # "namespace:command"), a Foreman::Thor::Command, a Class or a Foreman::Thor instance. If the
         
     | 
| 
      
 35 
     | 
    
         
            +
                # command cannot be guessed by name, it can also be supplied as second argument.
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                # You can also supply the arguments, options and configuration values for
         
     | 
| 
      
 38 
     | 
    
         
            +
                # the command to be invoked, if none is given, the same values used to
         
     | 
| 
      
 39 
     | 
    
         
            +
                # initialize the invoker are used to initialize the invoked.
         
     | 
| 
      
 40 
     | 
    
         
            +
                #
         
     | 
| 
      
 41 
     | 
    
         
            +
                # When no name is given, it will invoke the default command of the current class.
         
     | 
| 
      
 42 
     | 
    
         
            +
                #
         
     | 
| 
      
 43 
     | 
    
         
            +
                # ==== Examples
         
     | 
| 
      
 44 
     | 
    
         
            +
                #
         
     | 
| 
      
 45 
     | 
    
         
            +
                #   class A < Foreman::Thor
         
     | 
| 
      
 46 
     | 
    
         
            +
                #     def foo
         
     | 
| 
      
 47 
     | 
    
         
            +
                #       invoke :bar
         
     | 
| 
      
 48 
     | 
    
         
            +
                #       invoke "b:hello", ["Erik"]
         
     | 
| 
      
 49 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 50 
     | 
    
         
            +
                #
         
     | 
| 
      
 51 
     | 
    
         
            +
                #     def bar
         
     | 
| 
      
 52 
     | 
    
         
            +
                #       invoke "b:hello", ["Erik"]
         
     | 
| 
      
 53 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 54 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 55 
     | 
    
         
            +
                #
         
     | 
| 
      
 56 
     | 
    
         
            +
                #   class B < Foreman::Thor
         
     | 
| 
      
 57 
     | 
    
         
            +
                #     def hello(name)
         
     | 
| 
      
 58 
     | 
    
         
            +
                #       puts "hello #{name}"
         
     | 
| 
      
 59 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 60 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 61 
     | 
    
         
            +
                #
         
     | 
| 
      
 62 
     | 
    
         
            +
                # You can notice that the method "foo" above invokes two commands: "bar",
         
     | 
| 
      
 63 
     | 
    
         
            +
                # which belongs to the same class and "hello" which belongs to the class B.
         
     | 
| 
      
 64 
     | 
    
         
            +
                #
         
     | 
| 
      
 65 
     | 
    
         
            +
                # By using an invocation system you ensure that a command is invoked only once.
         
     | 
| 
      
 66 
     | 
    
         
            +
                # In the example above, invoking "foo" will invoke "b:hello" just once, even
         
     | 
| 
      
 67 
     | 
    
         
            +
                # if it's invoked later by "bar" method.
         
     | 
| 
      
 68 
     | 
    
         
            +
                #
         
     | 
| 
      
 69 
     | 
    
         
            +
                # When class A invokes class B, all arguments used on A initialization are
         
     | 
| 
      
 70 
     | 
    
         
            +
                # supplied to B. This allows lazy parse of options. Let's suppose you have
         
     | 
| 
      
 71 
     | 
    
         
            +
                # some rspec commands:
         
     | 
| 
      
 72 
     | 
    
         
            +
                #
         
     | 
| 
      
 73 
     | 
    
         
            +
                #   class Rspec < Foreman::Thor::Group
         
     | 
| 
      
 74 
     | 
    
         
            +
                #     class_option :mock_framework, :type => :string, :default => :rr
         
     | 
| 
      
 75 
     | 
    
         
            +
                #
         
     | 
| 
      
 76 
     | 
    
         
            +
                #     def invoke_mock_framework
         
     | 
| 
      
 77 
     | 
    
         
            +
                #       invoke "rspec:#{options[:mock_framework]}"
         
     | 
| 
      
 78 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 79 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                # As you noticed, it invokes the given mock framework, which might have its
         
     | 
| 
      
 82 
     | 
    
         
            +
                # own options:
         
     | 
| 
      
 83 
     | 
    
         
            +
                #
         
     | 
| 
      
 84 
     | 
    
         
            +
                #   class Rspec::RR < Foreman::Thor::Group
         
     | 
| 
      
 85 
     | 
    
         
            +
                #     class_option :style, :type => :string, :default => :mock
         
     | 
| 
      
 86 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 87 
     | 
    
         
            +
                #
         
     | 
| 
      
 88 
     | 
    
         
            +
                # Since it's not rspec concern to parse mock framework options, when RR
         
     | 
| 
      
 89 
     | 
    
         
            +
                # is invoked all options are parsed again, so RR can extract only the options
         
     | 
| 
      
 90 
     | 
    
         
            +
                # that it's going to use.
         
     | 
| 
      
 91 
     | 
    
         
            +
                #
         
     | 
| 
      
 92 
     | 
    
         
            +
                # If you want Rspec::RR to be initialized with its own set of options, you
         
     | 
| 
      
 93 
     | 
    
         
            +
                # have to do that explicitly:
         
     | 
| 
      
 94 
     | 
    
         
            +
                #
         
     | 
| 
      
 95 
     | 
    
         
            +
                #   invoke "rspec:rr", [], :style => :foo
         
     | 
| 
      
 96 
     | 
    
         
            +
                #
         
     | 
| 
      
 97 
     | 
    
         
            +
                # Besides giving an instance, you can also give a class to invoke:
         
     | 
| 
      
 98 
     | 
    
         
            +
                #
         
     | 
| 
      
 99 
     | 
    
         
            +
                #   invoke Rspec::RR, [], :style => :foo
         
     | 
| 
      
 100 
     | 
    
         
            +
                #
         
     | 
| 
      
 101 
     | 
    
         
            +
                def invoke(name = nil, *args)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  if name.nil?
         
     | 
| 
      
 103 
     | 
    
         
            +
                    warn "[Foreman::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
         
     | 
| 
      
 104 
     | 
    
         
            +
                    return invoke_all
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  args.unshift(nil) if args.first.is_a?(Array) || args.first.nil?
         
     | 
| 
      
 108 
     | 
    
         
            +
                  command, args, opts, config = args
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  klass, command = _retrieve_class_and_command(name, command)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  raise "Missing Foreman::Thor class for invoke #{name}" unless klass
         
     | 
| 
      
 112 
     | 
    
         
            +
                  raise "Expected Foreman::Thor class, got #{klass}" unless klass <= Foreman::Thor::Base
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  args, opts, config = _parse_initialization_options(args, opts, config)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  klass.send(:dispatch, command, args, opts, config) do |instance|
         
     | 
| 
      
 116 
     | 
    
         
            +
                    instance.parent_options = options
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                # Invoke the given command if the given args.
         
     | 
| 
      
 121 
     | 
    
         
            +
                def invoke_command(command, *args) #:nodoc:
         
     | 
| 
      
 122 
     | 
    
         
            +
                  current = @_invocations[self.class]
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                  unless current.include?(command.name)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    current << command.name
         
     | 
| 
      
 126 
     | 
    
         
            +
                    command.run(self, *args)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
                alias_method :invoke_task, :invoke_command
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                # Invoke all commands for the current instance.
         
     | 
| 
      
 132 
     | 
    
         
            +
                def invoke_all #:nodoc:
         
     | 
| 
      
 133 
     | 
    
         
            +
                  self.class.all_commands.map { |_, command| invoke_command(command) }
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                # Invokes using shell padding.
         
     | 
| 
      
 137 
     | 
    
         
            +
                def invoke_with_padding(*args)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  with_padding { invoke(*args) }
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
              protected
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                # Configuration values that are shared between invocations.
         
     | 
| 
      
 144 
     | 
    
         
            +
                def _shared_configuration #:nodoc:
         
     | 
| 
      
 145 
     | 
    
         
            +
                  {:invocations => @_invocations}
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                # This method simply retrieves the class and command to be invoked.
         
     | 
| 
      
 149 
     | 
    
         
            +
                # If the name is nil or the given name is a command in the current class,
         
     | 
| 
      
 150 
     | 
    
         
            +
                # use the given name and return self as class. Otherwise, call
         
     | 
| 
      
 151 
     | 
    
         
            +
                # prepare_for_invocation in the current class.
         
     | 
| 
      
 152 
     | 
    
         
            +
                def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
         
     | 
| 
      
 153 
     | 
    
         
            +
                  if name.nil?
         
     | 
| 
      
 154 
     | 
    
         
            +
                    [self.class, nil]
         
     | 
| 
      
 155 
     | 
    
         
            +
                  elsif self.class.all_commands[name.to_s]
         
     | 
| 
      
 156 
     | 
    
         
            +
                    [self.class, name.to_s]
         
     | 
| 
      
 157 
     | 
    
         
            +
                  else
         
     | 
| 
      
 158 
     | 
    
         
            +
                    klass, command = self.class.prepare_for_invocation(nil, name)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    [klass, command || sent_command]
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
                alias_method :_retrieve_class_and_task, :_retrieve_class_and_command
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                # Initialize klass using values stored in the @_initializer.
         
     | 
| 
      
 165 
     | 
    
         
            +
                def _parse_initialization_options(args, opts, config) #:nodoc:
         
     | 
| 
      
 166 
     | 
    
         
            +
                  stored_args, stored_opts, stored_config = @_initializer
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  args ||= stored_args.dup
         
     | 
| 
      
 169 
     | 
    
         
            +
                  opts ||= stored_opts.dup
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  config ||= {}
         
     | 
| 
      
 172 
     | 
    
         
            +
                  config = stored_config.merge(_shared_configuration).merge!(config)
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  [args, opts, config]
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
              end
         
     | 
| 
      
 177 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 2 
     | 
    
         
            +
              module LineEditor
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Basic
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :prompt, :options
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.available?
         
     | 
| 
      
 7 
     | 
    
         
            +
                    true
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(prompt, options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @prompt = prompt
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @options = options
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def readline
         
     | 
| 
      
 16 
     | 
    
         
            +
                    $stdout.print(prompt)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    get_input
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                private
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def get_input
         
     | 
| 
      
 23 
     | 
    
         
            +
                    if echo?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      $stdin.gets
         
     | 
| 
      
 25 
     | 
    
         
            +
                    else
         
     | 
| 
      
 26 
     | 
    
         
            +
                      $stdin.noecho(&:gets)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def echo?
         
     | 
| 
      
 31 
     | 
    
         
            +
                    options.fetch(:echo, true)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            begin
         
     | 
| 
      
 2 
     | 
    
         
            +
              require "readline"
         
     | 
| 
      
 3 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 4 
     | 
    
         
            +
            end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 7 
     | 
    
         
            +
              module LineEditor
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Readline < Basic
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.available?
         
     | 
| 
      
 10 
     | 
    
         
            +
                    Object.const_defined?(:Readline)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def readline
         
     | 
| 
      
 14 
     | 
    
         
            +
                    if echo?
         
     | 
| 
      
 15 
     | 
    
         
            +
                      ::Readline.completion_append_character = nil
         
     | 
| 
      
 16 
     | 
    
         
            +
                      # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil.
         
     | 
| 
      
 17 
     | 
    
         
            +
                      if complete = completion_proc
         
     | 
| 
      
 18 
     | 
    
         
            +
                        ::Readline.completion_proc = complete
         
     | 
| 
      
 19 
     | 
    
         
            +
                      end
         
     | 
| 
      
 20 
     | 
    
         
            +
                      ::Readline.readline(prompt, add_to_history?)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    else
         
     | 
| 
      
 22 
     | 
    
         
            +
                      super
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def add_to_history?
         
     | 
| 
      
 29 
     | 
    
         
            +
                    options.fetch(:add_to_history, true)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def completion_proc
         
     | 
| 
      
 33 
     | 
    
         
            +
                    if use_path_completion?
         
     | 
| 
      
 34 
     | 
    
         
            +
                      proc { |text| PathCompletion.new(text).matches }
         
     | 
| 
      
 35 
     | 
    
         
            +
                    elsif completion_options.any?
         
     | 
| 
      
 36 
     | 
    
         
            +
                      proc do |text|
         
     | 
| 
      
 37 
     | 
    
         
            +
                        completion_options.select { |option| option.start_with?(text) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def completion_options
         
     | 
| 
      
 43 
     | 
    
         
            +
                    options.fetch(:limited_to, [])
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def use_path_completion?
         
     | 
| 
      
 47 
     | 
    
         
            +
                    options.fetch(:path, false)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  class PathCompletion
         
     | 
| 
      
 51 
     | 
    
         
            +
                    attr_reader :text
         
     | 
| 
      
 52 
     | 
    
         
            +
                    private :text
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    def initialize(text)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      @text = text
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    def matches
         
     | 
| 
      
 59 
     | 
    
         
            +
                      relative_matches
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  private
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    def relative_matches
         
     | 
| 
      
 65 
     | 
    
         
            +
                      absolute_matches.map { |path| path.sub(base_path, "") }
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    def absolute_matches
         
     | 
| 
      
 69 
     | 
    
         
            +
                      Dir[glob_pattern].map do |path|
         
     | 
| 
      
 70 
     | 
    
         
            +
                        if File.directory?(path)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          "#{path}/"
         
     | 
| 
      
 72 
     | 
    
         
            +
                        else
         
     | 
| 
      
 73 
     | 
    
         
            +
                          path
         
     | 
| 
      
 74 
     | 
    
         
            +
                        end
         
     | 
| 
      
 75 
     | 
    
         
            +
                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    def glob_pattern
         
     | 
| 
      
 79 
     | 
    
         
            +
                      "#{base_path}#{text}*"
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    def base_path
         
     | 
| 
      
 83 
     | 
    
         
            +
                      "#{Dir.pwd}/"
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "foreman/vendor/thor/lib/thor/line_editor/basic"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "foreman/vendor/thor/lib/thor/line_editor/readline"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 5 
     | 
    
         
            +
              module LineEditor
         
     | 
| 
      
 6 
     | 
    
         
            +
                def self.readline(prompt, options = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                  best_available.new(prompt, options).readline
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def self.best_available
         
     | 
| 
      
 11 
     | 
    
         
            +
                  [
         
     | 
| 
      
 12 
     | 
    
         
            +
                    Foreman::Thor::LineEditor::Readline,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    Foreman::Thor::LineEditor::Basic
         
     | 
| 
      
 14 
     | 
    
         
            +
                  ].detect(&:available?)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Argument #:nodoc:
         
     | 
| 
      
 3 
     | 
    
         
            +
                VALID_TYPES = [:numeric, :hash, :array, :string]
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :name, :description, :enum, :required, :type, :default, :banner
         
     | 
| 
      
 6 
     | 
    
         
            +
                alias_method :human_name, :name
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(name, options = {})
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class_name = self.class.name.split("::").last
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  type = options[:type]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  raise ArgumentError, "#{class_name} name can't be nil."                         if name.nil?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s."  if type && !valid_type?(type)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  @name        = name.to_s
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @description = options[:desc]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @required    = options.key?(:required) ? options[:required] : true
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @type        = (type || :string).to_sym
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @default     = options[:default]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @banner      = options[:banner] || default_banner
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @enum        = options[:enum]
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  validate! # Trigger specific validations
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def usage
         
     | 
| 
      
 28 
     | 
    
         
            +
                  required? ? banner : "[#{banner}]"
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def required?
         
     | 
| 
      
 32 
     | 
    
         
            +
                  required
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def show_default?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  case default
         
     | 
| 
      
 37 
     | 
    
         
            +
                  when Array, String, Hash
         
     | 
| 
      
 38 
     | 
    
         
            +
                    !default.empty?
         
     | 
| 
      
 39 
     | 
    
         
            +
                  else
         
     | 
| 
      
 40 
     | 
    
         
            +
                    default
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              protected
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def validate!
         
     | 
| 
      
 47 
     | 
    
         
            +
                  raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
         
     | 
| 
      
 48 
     | 
    
         
            +
                  raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def valid_type?(type)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  self.class::VALID_TYPES.include?(type.to_sym)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                def default_banner
         
     | 
| 
      
 56 
     | 
    
         
            +
                  case type
         
     | 
| 
      
 57 
     | 
    
         
            +
                  when :boolean
         
     | 
| 
      
 58 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 59 
     | 
    
         
            +
                  when :string, :default
         
     | 
| 
      
 60 
     | 
    
         
            +
                    human_name.upcase
         
     | 
| 
      
 61 
     | 
    
         
            +
                  when :numeric
         
     | 
| 
      
 62 
     | 
    
         
            +
                    "N"
         
     | 
| 
      
 63 
     | 
    
         
            +
                  when :hash
         
     | 
| 
      
 64 
     | 
    
         
            +
                    "key:value"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  when :array
         
     | 
| 
      
 66 
     | 
    
         
            +
                    "one two three"
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,175 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Arguments #:nodoc: # rubocop:disable ClassLength
         
     | 
| 
      
 3 
     | 
    
         
            +
                NUMERIC = /[-+]?(\d*\.\d+|\d+)/
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                # Receives an array of args and returns two arrays, one with arguments
         
     | 
| 
      
 6 
     | 
    
         
            +
                # and one with switches.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                def self.split(args)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  arguments = []
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  args.each do |item|
         
     | 
| 
      
 12 
     | 
    
         
            +
                    break if item =~ /^-/
         
     | 
| 
      
 13 
     | 
    
         
            +
                    arguments << item
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  [arguments, args[Range.new(arguments.size, -1)]]
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def self.parse(*args)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  to_parse = args.pop
         
     | 
| 
      
 21 
     | 
    
         
            +
                  new(*args).parse(to_parse)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Takes an array of Foreman::Thor::Argument objects.
         
     | 
| 
      
 25 
     | 
    
         
            +
                #
         
     | 
| 
      
 26 
     | 
    
         
            +
                def initialize(arguments = [])
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @assigns = {}
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @non_assigned_required = []
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @switches = arguments
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  arguments.each do |argument|
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if !argument.default.nil?
         
     | 
| 
      
 33 
     | 
    
         
            +
                      @assigns[argument.human_name] = argument.default
         
     | 
| 
      
 34 
     | 
    
         
            +
                    elsif argument.required?
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @non_assigned_required << argument
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def parse(args)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @pile = args.dup
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  @switches.each do |argument|
         
     | 
| 
      
 44 
     | 
    
         
            +
                    break unless peek
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @non_assigned_required.delete(argument)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  check_requirement!
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @assigns
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def remaining
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @pile
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              private
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def no_or_skip?(arg)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  arg =~ /^--(no|skip)-([-\w]+)$/
         
     | 
| 
      
 61 
     | 
    
         
            +
                  $2
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def last?
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @pile.empty?
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def peek
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @pile.first
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def shift
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @pile.shift
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def unshift(arg)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  if arg.is_a?(Array)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @pile = arg + @pile
         
     | 
| 
      
 79 
     | 
    
         
            +
                  else
         
     | 
| 
      
 80 
     | 
    
         
            +
                    @pile.unshift(arg)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def current_is_value?
         
     | 
| 
      
 85 
     | 
    
         
            +
                  peek && peek.to_s !~ /^-/
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                # Runs through the argument array getting strings that contains ":" and
         
     | 
| 
      
 89 
     | 
    
         
            +
                # mark it as a hash:
         
     | 
| 
      
 90 
     | 
    
         
            +
                #
         
     | 
| 
      
 91 
     | 
    
         
            +
                #   [ "name:string", "age:integer" ]
         
     | 
| 
      
 92 
     | 
    
         
            +
                #
         
     | 
| 
      
 93 
     | 
    
         
            +
                # Becomes:
         
     | 
| 
      
 94 
     | 
    
         
            +
                #
         
     | 
| 
      
 95 
     | 
    
         
            +
                #   { "name" => "string", "age" => "integer" }
         
     | 
| 
      
 96 
     | 
    
         
            +
                #
         
     | 
| 
      
 97 
     | 
    
         
            +
                def parse_hash(name)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  return shift if peek.is_a?(Hash)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  hash = {}
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  while current_is_value? && peek.include?(":")
         
     | 
| 
      
 102 
     | 
    
         
            +
                    key, value = shift.split(":", 2)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key
         
     | 
| 
      
 104 
     | 
    
         
            +
                    hash[key] = value
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
                  hash
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                # Runs through the argument array getting all strings until no string is
         
     | 
| 
      
 110 
     | 
    
         
            +
                # found or a switch is found.
         
     | 
| 
      
 111 
     | 
    
         
            +
                #
         
     | 
| 
      
 112 
     | 
    
         
            +
                #   ["a", "b", "c"]
         
     | 
| 
      
 113 
     | 
    
         
            +
                #
         
     | 
| 
      
 114 
     | 
    
         
            +
                # And returns it as an array:
         
     | 
| 
      
 115 
     | 
    
         
            +
                #
         
     | 
| 
      
 116 
     | 
    
         
            +
                #   ["a", "b", "c"]
         
     | 
| 
      
 117 
     | 
    
         
            +
                #
         
     | 
| 
      
 118 
     | 
    
         
            +
                def parse_array(name)
         
     | 
| 
      
 119 
     | 
    
         
            +
                  return shift if peek.is_a?(Array)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  array = []
         
     | 
| 
      
 121 
     | 
    
         
            +
                  array << shift while current_is_value?
         
     | 
| 
      
 122 
     | 
    
         
            +
                  array
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                # Check if the peek is numeric format and return a Float or Integer.
         
     | 
| 
      
 126 
     | 
    
         
            +
                # Check if the peek is included in enum if enum is provided.
         
     | 
| 
      
 127 
     | 
    
         
            +
                # Otherwise raises an error.
         
     | 
| 
      
 128 
     | 
    
         
            +
                #
         
     | 
| 
      
 129 
     | 
    
         
            +
                def parse_numeric(name)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  return shift if peek.is_a?(Numeric)
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  unless peek =~ NUMERIC && $& == peek
         
     | 
| 
      
 133 
     | 
    
         
            +
                    raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                  value = $&.index(".") ? shift.to_f : shift.to_i
         
     | 
| 
      
 137 
     | 
    
         
            +
                  if @switches.is_a?(Hash) && switch = @switches[name]
         
     | 
| 
      
 138 
     | 
    
         
            +
                    if switch.enum && !switch.enum.include?(value)
         
     | 
| 
      
 139 
     | 
    
         
            +
                      raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
         
     | 
| 
      
 140 
     | 
    
         
            +
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
                  value
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                # Parse string:
         
     | 
| 
      
 146 
     | 
    
         
            +
                # for --string-arg, just return the current value in the pile
         
     | 
| 
      
 147 
     | 
    
         
            +
                # for --no-string-arg, nil
         
     | 
| 
      
 148 
     | 
    
         
            +
                # Check if the peek is included in enum if enum is provided. Otherwise raises an error.
         
     | 
| 
      
 149 
     | 
    
         
            +
                #
         
     | 
| 
      
 150 
     | 
    
         
            +
                def parse_string(name)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  if no_or_skip?(name)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 153 
     | 
    
         
            +
                  else
         
     | 
| 
      
 154 
     | 
    
         
            +
                    value = shift
         
     | 
| 
      
 155 
     | 
    
         
            +
                    if @switches.is_a?(Hash) && switch = @switches[name]
         
     | 
| 
      
 156 
     | 
    
         
            +
                      if switch.enum && !switch.enum.include?(value)
         
     | 
| 
      
 157 
     | 
    
         
            +
                        raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
         
     | 
| 
      
 158 
     | 
    
         
            +
                      end
         
     | 
| 
      
 159 
     | 
    
         
            +
                    end
         
     | 
| 
      
 160 
     | 
    
         
            +
                    value
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
                end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                # Raises an error if @non_assigned_required array is not empty.
         
     | 
| 
      
 165 
     | 
    
         
            +
                #
         
     | 
| 
      
 166 
     | 
    
         
            +
                def check_requirement!
         
     | 
| 
      
 167 
     | 
    
         
            +
                  return if @non_assigned_required.empty?
         
     | 
| 
      
 168 
     | 
    
         
            +
                  names = @non_assigned_required.map do |o|
         
     | 
| 
      
 169 
     | 
    
         
            +
                    o.respond_to?(:switch_name) ? o.switch_name : o.human_name
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end.join("', '")
         
     | 
| 
      
 171 
     | 
    
         
            +
                  class_name = self.class.name.split("::").last.downcase
         
     | 
| 
      
 172 
     | 
    
         
            +
                  raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
         
     | 
| 
      
 173 
     | 
    
         
            +
                end
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,146 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Foreman::Thor
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Option < Argument #:nodoc:
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :aliases, :group, :lazy_default, :hide
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(name, options = {})
         
     | 
| 
      
 8 
     | 
    
         
            +
                  options[:required] = false unless options.key?(:required)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  super
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @lazy_default = options[:lazy_default]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @group        = options[:group].to_s.capitalize if options[:group]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @aliases      = Array(options[:aliases])
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @hide         = options[:hide]
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # This parse quick options given as method_options. It makes several
         
     | 
| 
      
 17 
     | 
    
         
            +
                # assumptions, but you can be more specific using the option method.
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   parse :foo => "bar"
         
     | 
| 
      
 20 
     | 
    
         
            +
                #   #=> Option foo with default value bar
         
     | 
| 
      
 21 
     | 
    
         
            +
                #
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   parse [:foo, :baz] => "bar"
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   #=> Option foo with default value bar and alias :baz
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                #   parse :foo => :required
         
     | 
| 
      
 26 
     | 
    
         
            +
                #   #=> Required option foo without default value
         
     | 
| 
      
 27 
     | 
    
         
            +
                #
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   parse :foo => 2
         
     | 
| 
      
 29 
     | 
    
         
            +
                #   #=> Option foo with default value 2 and type numeric
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                #   parse :foo => :numeric
         
     | 
| 
      
 32 
     | 
    
         
            +
                #   #=> Option foo without default value and type numeric
         
     | 
| 
      
 33 
     | 
    
         
            +
                #
         
     | 
| 
      
 34 
     | 
    
         
            +
                #   parse :foo => true
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   #=> Option foo with default value true and type boolean
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                # The valid types are :boolean, :numeric, :hash, :array and :string. If none
         
     | 
| 
      
 38 
     | 
    
         
            +
                # is given a default type is assumed. This default type accepts arguments as
         
     | 
| 
      
 39 
     | 
    
         
            +
                # string (--foo=value) or booleans (just --foo).
         
     | 
| 
      
 40 
     | 
    
         
            +
                #
         
     | 
| 
      
 41 
     | 
    
         
            +
                # By default all options are optional, unless :required is given.
         
     | 
| 
      
 42 
     | 
    
         
            +
                #
         
     | 
| 
      
 43 
     | 
    
         
            +
                def self.parse(key, value)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  if key.is_a?(Array)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    name, *aliases = key
         
     | 
| 
      
 46 
     | 
    
         
            +
                  else
         
     | 
| 
      
 47 
     | 
    
         
            +
                    name = key
         
     | 
| 
      
 48 
     | 
    
         
            +
                    aliases = []
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  name    = name.to_s
         
     | 
| 
      
 52 
     | 
    
         
            +
                  default = value
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  type = case value
         
     | 
| 
      
 55 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 56 
     | 
    
         
            +
                    default = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if VALID_TYPES.include?(value)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      value
         
     | 
| 
      
 59 
     | 
    
         
            +
                    elsif required = (value == :required) # rubocop:disable AssignmentInCondition
         
     | 
| 
      
 60 
     | 
    
         
            +
                      :string
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  when TrueClass, FalseClass
         
     | 
| 
      
 63 
     | 
    
         
            +
                    :boolean
         
     | 
| 
      
 64 
     | 
    
         
            +
                  when Numeric
         
     | 
| 
      
 65 
     | 
    
         
            +
                    :numeric
         
     | 
| 
      
 66 
     | 
    
         
            +
                  when Hash, Array, String
         
     | 
| 
      
 67 
     | 
    
         
            +
                    value.class.name.downcase.to_sym
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def switch_name
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @switch_name ||= dasherized? ? name : dasherize(name)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def human_name
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @human_name ||= dasherized? ? undasherize(name) : name
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def usage(padding = 0)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  sample = if banner && !banner.to_s.empty?
         
     | 
| 
      
 83 
     | 
    
         
            +
                    "#{switch_name}=#{banner}"
         
     | 
| 
      
 84 
     | 
    
         
            +
                  else
         
     | 
| 
      
 85 
     | 
    
         
            +
                    switch_name
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  sample = "[#{sample}]" unless required?
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  if boolean?
         
     | 
| 
      
 91 
     | 
    
         
            +
                    sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  if aliases.empty?
         
     | 
| 
      
 95 
     | 
    
         
            +
                    (" " * padding) << sample
         
     | 
| 
      
 96 
     | 
    
         
            +
                  else
         
     | 
| 
      
 97 
     | 
    
         
            +
                    "#{aliases.join(', ')}, #{sample}"
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                VALID_TYPES.each do |type|
         
     | 
| 
      
 102 
     | 
    
         
            +
                  class_eval <<-RUBY, __FILE__, __LINE__ + 1
         
     | 
| 
      
 103 
     | 
    
         
            +
                    def #{type}?
         
     | 
| 
      
 104 
     | 
    
         
            +
                      self.type == #{type.inspect}
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
              protected
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                def validate!
         
     | 
| 
      
 112 
     | 
    
         
            +
                  raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
         
     | 
| 
      
 113 
     | 
    
         
            +
                  validate_default_type!
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                def validate_default_type!
         
     | 
| 
      
 117 
     | 
    
         
            +
                  default_type = case @default
         
     | 
| 
      
 118 
     | 
    
         
            +
                  when nil
         
     | 
| 
      
 119 
     | 
    
         
            +
                    return
         
     | 
| 
      
 120 
     | 
    
         
            +
                  when TrueClass, FalseClass
         
     | 
| 
      
 121 
     | 
    
         
            +
                    required? ? :string : :boolean
         
     | 
| 
      
 122 
     | 
    
         
            +
                  when Numeric
         
     | 
| 
      
 123 
     | 
    
         
            +
                    :numeric
         
     | 
| 
      
 124 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 125 
     | 
    
         
            +
                    :string
         
     | 
| 
      
 126 
     | 
    
         
            +
                  when Hash, Array, String
         
     | 
| 
      
 127 
     | 
    
         
            +
                    @default.class.name.downcase.to_sym
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  # TODO: This should raise an ArgumentError in a future version of Foreman::Thor
         
     | 
| 
      
 131 
     | 
    
         
            +
                  warn "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                def dasherized?
         
     | 
| 
      
 135 
     | 
    
         
            +
                  name.index("-") == 0
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def undasherize(str)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  str.sub(/^-{1,2}/, "")
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                def dasherize(str)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  (str.length > 1 ? "--" : "-") + str.tr("_", "-")
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
              end
         
     | 
| 
      
 146 
     | 
    
         
            +
            end
         
     |