Class Command::Definition
In: lib/command/definition.rb
Parent: Object

Methods

Constants

RequiredOptionArgument = /[A-Z][A-Z_]*/
OptionArgument = /\[#{RequiredOptionArgument}\]|#{RequiredOptionArgument}/
ShortOption = /\A-[A-Za-z](?: #{OptionArgument})?\z/
NegationSequence = /\[(?:no-|with-|without-)\]/
LongOption = /\A-- (?: #{NegationSequence}?[A-Za-z][A-Za-z0-9_-]* | [A-Za-z][A-Za-z0-9_-]*#{NegationSequence}[A-Za-z0-9][A-Za-z0-9_-] ) (?:\x20#{OptionArgument})?\z /x

Attributes

argument_position  [R] 
arguments  [R] 
arguments_by_name  [R] 
commands_by_name  [R] 
default_command  [R] 
default_options  [R] 
env_by_variable  [R] 
options_by_flag  [R] 
options_by_name  [R] 
parent  [R] 

Public Class methods

[Source]

    # File lib/command/definition.rb, line 23
23:     def self.create_argument(*args)
24:       name        = Symbol === args.first && args.shift
25:       usage       = args.shift
26:       bare        = usage[/\w+/]
27:       type        = Symbol === args.first && args.shift
28:       description = args.shift
29: 
30:       Argument.new(name, bare, usage, type, description)
31:     end

valid arguments:

  name # --> copy from parent
  name, short[, long][, type][, description]
  name, long[, type][, description]

short can be

where ? is any of A-Za-z examples:

  • ’-a‘
  • ’-a [OPTIONAL_ARG]’
  • ’-a REQUIRED_ARG‘

long can be

where ? is [A-Za-z0-9][A-Za-z0-9-_]* It may contain a negation sequence, which is one of ’[no-]’, ’[with-]’, ’[without-]’ examples:

  • ’—colored‘
  • ’—[no-]colors‘
  • ’—port PORT‘
  • ’—foo [OPTIONAL_ARG]’

Only one of short and long may have the argument declared. Usually you‘ll have the argument in long and only have it in short if there‘s no long at all.

type can be

  • :Virtual
  • :Boolean
  • :String (default)
  • :Integer
  • :Float
  • :Octal
  • :Hex
  • :File - requires the provided path to exist and be a file
  • :Directory - requires the provided path to exist and be a directory
  • :Path

Exceptions (incomplete):

  • ArgumentError with single argument if it doesn‘t identify an inheritable option
  • ArgumentError on invalid short definition
  • ArgumentError on invalid long definition
  • ArgumentError on option argument declaration in both, short and long definition
  • ArgumentError on invalid/unsupported type

[Source]

     # File lib/command/definition.rb, line 82
 82:     def self.create_option(name, *args)
 83:       case args.first when nil, /\A-[^- ]/ then
 84:         short = args.shift
 85:       end
 86:       case args.first when nil, /\A--[^- ]/ then
 87:         long = args.shift
 88:       end
 89:       case args.first when nil, Symbol then
 90:         type = args.shift
 91:       end
 92:       declaration = short ? [short,long].compact.join(", ") : "    #{long}"
 93:       description = args.shift
 94: 
 95:       raise ArgumentError, "Too many arguments" unless args.empty?
 96:       raise ArgumentError, "Invalid short declaration: #{short.inspect}" unless (short.nil? || short =~ ShortOption)
 97:       raise ArgumentError, "Invalid long declaration: #{long.inspect}" unless (long.nil? || long =~ LongOption)
 98:       raise ArgumentError, "Argument declaration must only be in one of short and long" if (short && long && short =~ /\s/ && long =~ /\s/)
 99: 
100:       necessity        = :none
101:       extract_argument = nil
102:       extract_argument = short if short =~ /\s/
103:       extract_argument = long if long =~ /\s/
104:       if extract_argument then
105:         flag, *argument = extract_argument.split(/ /)
106:         extract_argument.replace(flag) # long/short should only contain the flag, not the argument declaration as well
107:         raise ArgumentError, "Multiple arguments for an option not yet supported" if argument.size > 1
108:         if argument.empty?
109:           necessity = :none
110:         elsif argument.first =~ /\A\[.*\]\z/ then
111:           necessity = :optional
112:         else
113:           necessity = :required
114:         end
115:       end
116: 
117:       negated = nil
118:       if long =~ NegationSequence then
119:         negated = long.delete('[]')
120:         long    = long.gsub(NegationSequence, '')
121:       end
122: 
123:       Option.new(name, short, long, negated, necessity, type, declaration, description)
124:     end

[Source]

     # File lib/command/definition.rb, line 138
138:     def initialize(parent=nil, default_command=nil, default_options={}, &block)
139:       @default_command   = default_command
140:       @default_options   = DecoratingHash.new(@parent && @parent.default_options).update(default_options)
141:       @elements          = []
142:       @parent            = parent
143:       @arguments_by_name = DecoratingHash.new(@parent && @parent.arguments_by_name)
144:       @options_by_name   = DecoratingHash.new(@parent && @parent.options_by_name)
145:       @options_by_flag   = DecoratingHash.new(@parent && @parent.options_by_flag)
146:       @commands_by_name  = DecoratingHash.new(@parent && @parent.commands_by_name)
147:       @env_by_variable   = DecoratingHash.new(@parent && @parent.env_by_variable)
148:       @argument_position = {}
149:       @text              = []
150:       @placeholders      = {}
151:       @content_for       = [@elements]
152:       instance_eval(&block) if block
153:     end

Public Instance methods

[Source]

     # File lib/command/definition.rb, line 155
155:     def [](command)
156:       command ? @commands_by_name[command] : self
157:     end

[Source]

     # File lib/command/definition.rb, line 216
216:     def argument(*args)
217:       unless @argument_position[args.first]
218:         @argument_position[args.first] = @argument_position.size
219:       end
220: 
221:       if args.size == 1 then
222:         argument   = @arguments_by_name[args.first]
223:         raise ArgumentError, "No argument with name #{args.first.inspect} in any parent found." unless argument
224:       else
225:         argument    = self.class.create_argument(*args)
226:         @arguments_by_name[argument.name] = argument
227:       end
228:       @content_for.last << argument
229: 
230:       argument
231:     end

[Source]

     # File lib/command/definition.rb, line 288
288:     def command(*args, &block)
289:       definition = Definition.new(self, *args, &block)
290:       @commands_by_name[args.first] = definition
291:       @content_for.last << definition
292:     end

[Source]

     # File lib/command/definition.rb, line 159
159:     def content_for(placeholder)
160:       @placeholders[placeholder] ||= []
161:       @content_for << @placeholders[placeholder]
162:       yield(self)
163:       @content_for.pop
164:     end

[Source]

     # File lib/command/definition.rb, line 282
282:     def env_option(name, variable)
283:       env = Env.new(name, variable)
284:       @env_by_variable[variable] = env
285:       @content_for.last << env
286:     end
o(*args)

Alias for option

[Source]

     # File lib/command/definition.rb, line 246
246:     def option(*args)
247:       if args.size == 1 then
248:         inherited_option = @options_by_name[args.first]
249:         raise ArgumentError, "No inherited option #{args.first.inspect}" unless inherited_option
250:         @content_for.last << inherited_option
251: 
252:         inherited_option
253:       else
254:         option = self.class.create_option(*args)
255: 
256:         @options_by_name[option.name]    = option
257:         @options_by_flag[option.short]   = option
258:         @options_by_flag[option.long]    = option
259:         @options_by_flag[option.negated] = option
260:         @content_for.last << option
261: 
262:         option
263:       end
264:     end

[Source]

     # File lib/command/definition.rb, line 278
278:     def placeholder(identifier)
279:       @content_for.last << identifier
280:     end

[Source]

     # File lib/command/definition.rb, line 267
267:     def text(*args)
268:       if args.size == 2 then
269:         indent, text = *args
270:         text = text.gsub(/^/, indent)
271:       else
272:         text = args.first
273:       end
274:       @text             << text
275:       @content_for.last << text
276:     end

[Source]

     # File lib/command/definition.rb, line 212
212:     def usage
213:       @content_for.last << :usage
214:     end

[Source]

     # File lib/command/definition.rb, line 166
166:     def usage_text(elements=@elements)
167:       longest_arg_bare = elements.grep(Argument).max { |a,b|
168:         a.bare.size <=> b.bare.size
169:       }
170:       longest_option = elements.grep(Option).max { |a,b|
171:         a.declaration.size <=> b.declaration.size
172:       }
173:       longest_env_name = elements.grep(Env).max { |a,b|
174:         a.variable.size <=> b.variable.size
175:       }
176:       longest_arg_bare = longest_arg_bare && longest_arg_bare.bare.size
177:       longest_option   = longest_option && longest_option.declaration.size
178:       longest_env_name = longest_env_name && longest_env_name.variable.size
179:       arguments = elements.grep(Argument)
180: 
181:       elements.map { |e|
182:         case e
183:           when :usage
184:             "Usage: #{File.basename($0)} #{arguments.map{|a|a.usage}.join(' ')}\n"
185:           when Symbol # placeholder
186:             usage_text(@placeholders[e])
187:           when Option
188:             sprintf "  %*s%s\n",
189:                     -(longest_option+2),
190:                     e.declaration,
191:                     e.description
192:           when Env
193:             sprintf "* %*s%s\n",
194:                     -longest_env_name-2,
195:                     e.variable,
196:                     @options_by_name[e.name].description
197:           when String
198:             e+"\n"
199:           when Argument
200:             indent = "\n     "+(" "*longest_arg_bare)
201:             sprintf "  %*s%s\n",
202:                     -(longest_arg_bare+3),
203:                     "#{e.bare}:",
204:                     e.description.to_s.gsub(/\n/, indent)
205:           when Definition
206:           else
207:             "unimplemented(#{e.class})"
208:         end
209:       }.join('')
210:     end

[Source]

     # File lib/command/definition.rb, line 233
233:     def virtual_argument(*args)
234:       if args.size == 1 then
235:         argument   = @arguments_by_name[args.first]
236:         raise ArgumentError, "No argument with name #{args.first.inspect} in any parent found." unless argument
237:       else
238:         argument    = self.class.create_argument(*args)
239:         @arguments_by_name[argument.name] = argument
240:       end
241: 
242:       @content_for.last << argument
243:       argument
244:     end

[Validate]