atli 0.1.4 → 0.1.5

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.
@@ -0,0 +1,730 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Deps
5
+ # -----------------------------------------------------------------------
6
+ require 'nrser'
7
+
8
+ # Project / Package
9
+ # -----------------------------------------------------------------------
10
+ require 'thor/base/common_class_options'
11
+
12
+
13
+ # Refinements
14
+ # =======================================================================
15
+
16
+ using NRSER
17
+ using NRSER::Types
18
+
19
+
20
+ # Declarations
21
+ # =======================================================================
22
+
23
+ module Thor::Base; end
24
+
25
+
26
+ # Definitions
27
+ # =======================================================================
28
+
29
+ # Methods that are mixed in as module/class/singleton methods to modules
30
+ # that include {Thor::Base}.
31
+ #
32
+ module Thor::Base::ClassMethods
33
+
34
+ # Mixins
35
+ # ==========================================================================
36
+
37
+ # Include common class option macros
38
+ include Thor::Base::CommonClassOptions
39
+
40
+
41
+ # Mixin Methods
42
+ # ==========================================================================
43
+ #
44
+ # (Which become class methods on includers of {Thor::Base})
45
+ #
46
+
47
+ def attr_reader(*) #:nodoc:
48
+ no_commands { super }
49
+ end
50
+
51
+
52
+ def attr_writer(*) #:nodoc:
53
+ no_commands { super }
54
+ end
55
+
56
+
57
+ def attr_accessor(*) #:nodoc:
58
+ no_commands { super }
59
+ end
60
+
61
+
62
+ # If you want to raise an error for unknown options, call
63
+ # check_unknown_options!
64
+ # This is disabled by default to allow dynamic invocations.
65
+ def check_unknown_options!
66
+ @check_unknown_options = true
67
+ end
68
+
69
+
70
+ def check_unknown_options #:nodoc:
71
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
72
+ end
73
+
74
+
75
+ def check_unknown_options?(config) #:nodoc:
76
+ !!check_unknown_options
77
+ end
78
+
79
+
80
+ # If you want to raise an error when the default value of an option does
81
+ # not match the type call check_default_type!
82
+ # This is disabled by default for compatibility.
83
+ def check_default_type!
84
+ @check_default_type = true
85
+ end
86
+
87
+
88
+ def check_default_type #:nodoc:
89
+ @check_default_type ||= from_superclass(:check_default_type, false)
90
+ end
91
+
92
+
93
+ def check_default_type? #:nodoc:
94
+ !!check_default_type
95
+ end
96
+
97
+
98
+ # If true, option parsing is suspended as soon as an unknown option or a
99
+ # regular argument is encountered. All remaining arguments are passed to
100
+ # the command as regular arguments.
101
+ def stop_on_unknown_option?(command_name) #:nodoc:
102
+ false
103
+ end
104
+
105
+
106
+ # If true, option set will not suspend the execution of the command when
107
+ # a required option is not provided.
108
+ def disable_required_check?(command_name) #:nodoc:
109
+ false
110
+ end
111
+
112
+
113
+ # If you want only strict string args (useful when cascading thor classes),
114
+ # call strict_args_position! This is disabled by default to allow dynamic
115
+ # invocations.
116
+ def strict_args_position!
117
+ @strict_args_position = true
118
+ end
119
+
120
+
121
+ def strict_args_position #:nodoc:
122
+ @strict_args_position ||= from_superclass(:strict_args_position, false)
123
+ end
124
+
125
+
126
+ def strict_args_position?(config) #:nodoc:
127
+ !!strict_args_position
128
+ end
129
+
130
+
131
+ # Adds an argument to the class and creates an attr_accessor for it.
132
+ #
133
+ # Arguments are different from options in several aspects. The first one
134
+ # is how they are parsed from the command line, arguments are retrieved
135
+ # from position:
136
+ #
137
+ # thor command NAME
138
+ #
139
+ # Instead of:
140
+ #
141
+ # thor command --name=NAME
142
+ #
143
+ # Besides, arguments are used inside your code as an accessor
144
+ # (self.argument), while options are all kept in a hash (self.options).
145
+ #
146
+ # Finally, arguments cannot have type :default or :boolean but can be
147
+ # optional (supplying :optional => :true or :required => false), although
148
+ # you cannot have a required argument after a non-required argument. If
149
+ # you try it, an error is raised.
150
+ #
151
+ # ==== Parameters
152
+ # name<Symbol>:: The name of the argument.
153
+ # options<Hash>:: Described below.
154
+ #
155
+ # ==== Options
156
+ # :desc - Description for the argument.
157
+ # :required - If the argument is required or not.
158
+ # :optional - If the argument is optional or not.
159
+ # :type - The type of the argument, can be :string, :hash, :array,
160
+ # :numeric.
161
+ # :default - Default value for this argument. It cannot be required and
162
+ # have default values.
163
+ # :banner - String to show on usage notes.
164
+ #
165
+ # ==== Errors
166
+ # ArgumentError:: Raised if you supply a required argument after a non
167
+ # required one.
168
+ #
169
+ def argument(name, options = {})
170
+ is_thor_reserved_word?(name, :argument)
171
+ no_commands { attr_accessor name }
172
+
173
+ required = if options.key?(:optional)
174
+ !options[:optional]
175
+ elsif options.key?(:required)
176
+ options[:required]
177
+ else
178
+ options[:default].nil?
179
+ end
180
+
181
+ remove_argument name
182
+
183
+ if required
184
+ arguments.each do |argument|
185
+ next if argument.required?
186
+ raise ArgumentError,
187
+ "You cannot have #{name.to_s.inspect} as required argument " \
188
+ "after the non-required argument #{argument.human_name.inspect}."
189
+ end
190
+ end
191
+
192
+ options[:required] = required
193
+
194
+ arguments << Thor::Argument.new(name, options)
195
+ end
196
+
197
+
198
+ # Returns this class arguments, looking up in the ancestors chain.
199
+ #
200
+ # ==== Returns
201
+ # Array[Thor::Argument]
202
+ #
203
+ def arguments
204
+ @arguments ||= from_superclass(:arguments, [])
205
+ end
206
+
207
+
208
+ # Adds a bunch of options to the set of class options.
209
+ #
210
+ # class_options :foo => false, :bar => :required, :baz => :string
211
+ #
212
+ # If you prefer more detailed declaration, check class_option.
213
+ #
214
+ # ==== Parameters
215
+ # Hash[Symbol => Object]
216
+ #
217
+ def class_options(options = nil)
218
+ @class_options ||= from_superclass(:class_options, {})
219
+ build_options(options, @class_options) if options
220
+ @class_options
221
+ end
222
+
223
+
224
+ # Adds an option to the set of class options
225
+ #
226
+ # ==== Parameters
227
+ # name<Symbol>:: The name of the argument.
228
+ # options<Hash>:: Described below.
229
+ #
230
+ # ==== Options
231
+ # :desc:: -- Description for the argument.
232
+ # :required:: -- If the argument is required or not.
233
+ # :default:: -- Default value for this argument.
234
+ # :group:: -- The group for this options. Use by class options to
235
+ # output options in different levels.
236
+ # :aliases:: -- Aliases for this option. <b>Note:</b> Thor follows a
237
+ # convention of one-dash-one-letter options. Thus
238
+ # aliases like "-something" wouldn't be parsed; use
239
+ # either "\--something" or "-s" instead.
240
+ # :type:: -- The type of the argument, can be :string, :hash, :array,
241
+ # :numeric or :boolean.
242
+ # :banner:: -- String to show on usage notes.
243
+ # :hide:: -- If you want to hide this option from the help.
244
+ #
245
+ def class_option(name, options = {})
246
+ build_option(name, options, class_options)
247
+ end
248
+
249
+
250
+ # Removes a previous defined argument. If :undefine is given, undefine
251
+ # accessors as well.
252
+ #
253
+ # ==== Parameters
254
+ # names<Array>:: Arguments to be removed
255
+ #
256
+ # ==== Examples
257
+ #
258
+ # remove_argument :foo
259
+ # remove_argument :foo, :bar, :baz, :undefine => true
260
+ #
261
+ def remove_argument(*names)
262
+ options = names.last.is_a?(Hash) ? names.pop : {}
263
+
264
+ names.each do |name|
265
+ arguments.delete_if { |a| a.name == name.to_s }
266
+ undef_method name, "#{name}=" if options[:undefine]
267
+ end
268
+ end
269
+
270
+
271
+ # Removes a previous defined class option.
272
+ #
273
+ # ==== Parameters
274
+ # names<Array>:: Class options to be removed
275
+ #
276
+ # ==== Examples
277
+ #
278
+ # remove_class_option :foo
279
+ # remove_class_option :foo, :bar, :baz
280
+ #
281
+ def remove_class_option(*names)
282
+ names.each do |name|
283
+ class_options.delete(name)
284
+ end
285
+ end
286
+
287
+
288
+ # Defines the group. This is used when thor list is invoked so you can
289
+ # specify that only commands from a pre-defined group will be shown.
290
+ # Defaults to standard.
291
+ #
292
+ # ==== Parameters
293
+ # name<String|Symbol>
294
+ #
295
+ def group(name = nil)
296
+ if name
297
+ @group = name.to_s
298
+ else
299
+ @group ||= from_superclass(:group, "standard")
300
+ end
301
+ end
302
+
303
+
304
+ # Returns the commands for this Thor class.
305
+ #
306
+ # ==== Returns
307
+ # OrderedHash:: An ordered hash with commands names as keys and
308
+ # Thor::Command objects as values.
309
+ #
310
+ def commands
311
+ @commands ||= Thor::CoreExt::OrderedHash.new
312
+ end
313
+ alias_method :tasks, :commands
314
+
315
+
316
+ # Returns the commands for this Thor class and all subclasses.
317
+ #
318
+ # ==== Returns
319
+ # OrderedHash:: An ordered hash with commands names as keys and
320
+ # Thor::Command objects as values.
321
+ #
322
+ def all_commands
323
+ @all_commands ||= from_superclass( :all_commands,
324
+ Thor::CoreExt::OrderedHash.new )
325
+ @all_commands.merge!(commands)
326
+ end
327
+ alias_method :all_tasks, :all_commands
328
+
329
+
330
+ # Removes a given command from this Thor class. This is usually done if you
331
+ # are inheriting from another class and don't want it to be available
332
+ # anymore.
333
+ #
334
+ # By default it only remove the mapping to the command. But you can supply
335
+ # :undefine => true to undefine the method from the class as well.
336
+ #
337
+ # ==== Parameters
338
+ # name<Symbol|String>:: The name of the command to be removed
339
+ # options<Hash>:: You can give :undefine => true if you want commands the
340
+ # method to be undefined from the class as well.
341
+ #
342
+ def remove_command(*names)
343
+ options = names.last.is_a?(Hash) ? names.pop : {}
344
+
345
+ names.each do |name|
346
+ commands.delete(name.to_s)
347
+ all_commands.delete(name.to_s)
348
+ undef_method name if options[:undefine]
349
+ end
350
+ end
351
+ alias_method :remove_task, :remove_command
352
+
353
+
354
+ # All methods defined inside the given block are not added as commands.
355
+ #
356
+ # So you can do:
357
+ #
358
+ # class MyScript < Thor
359
+ # no_commands do
360
+ # def this_is_not_a_command
361
+ # end
362
+ # end
363
+ # end
364
+ #
365
+ # You can also add the method and remove it from the command list:
366
+ #
367
+ # class MyScript < Thor
368
+ # def this_is_not_a_command
369
+ # end
370
+ # remove_command :this_is_not_a_command
371
+ # end
372
+ #
373
+ def no_commands
374
+ @no_commands = true
375
+ yield
376
+ ensure
377
+ @no_commands = false
378
+ end
379
+ alias_method :no_tasks, :no_commands
380
+
381
+
382
+ # Sets the namespace for the Thor or Thor::Group class. By default the
383
+ # namespace is retrieved from the class name. If your Thor class is named
384
+ # Scripts::MyScript, the help method, for example, will be called as:
385
+ #
386
+ # thor scripts:my_script -h
387
+ #
388
+ # If you change the namespace:
389
+ #
390
+ # namespace :my_scripts
391
+ #
392
+ # You change how your commands are invoked:
393
+ #
394
+ # thor my_scripts -h
395
+ #
396
+ # Finally, if you change your namespace to default:
397
+ #
398
+ # namespace :default
399
+ #
400
+ # Your commands can be invoked with a shortcut. Instead of:
401
+ #
402
+ # thor :my_command
403
+ #
404
+ def namespace(name = nil)
405
+ if name
406
+ @namespace = name.to_s
407
+ else
408
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
409
+ end
410
+ end
411
+
412
+
413
+ # @depreciated
414
+ # Use {#exec!} in executable script files.
415
+ #
416
+ # Without additional configuration, using this method will often result in
417
+ # executables that return success (exit code `0`) when they fail due to
418
+ # bad arguments.
419
+ #
420
+ # Parses the command and options from the given args, instantiate the class
421
+ # and invoke the command. This method is used when the arguments must be parsed
422
+ # from an array. If you are inside Ruby and want to use a Thor class, you
423
+ # can simply initialize it:
424
+ #
425
+ # script = MyScript.new(args, options, config)
426
+ # script.invoke(:command, first_arg, second_arg, third_arg)
427
+ #
428
+ def start(given_args = ARGV, config = {})
429
+ config[:shell] ||= Thor::Base.shell.new
430
+ dispatch(nil, given_args.dup, nil, config)
431
+ rescue Thor::Error => e
432
+ if config[:debug] || ENV["THOR_DEBUG"] == "1"
433
+ raise e
434
+ else
435
+ config[:shell].error(e.message)
436
+ end
437
+ exit(1) if exit_on_failure?
438
+ rescue Errno::EPIPE
439
+ # This happens if a thor command is piped to something like `head`,
440
+ # which closes the pipe when it's done reading. This will also
441
+ # mean that if the pipe is closed, further unnecessary
442
+ # computation will not occur.
443
+ exit(0)
444
+ end
445
+
446
+
447
+ # Like {#start}, but explicitly for handling over control in an
448
+ # executable.
449
+ #
450
+ # For details on why this is here see
451
+ # {file:doc/files/notes/too-broken-to-fail.md Too Broken to Fail}.
452
+ #
453
+ def exec!(given_args = ARGV, config = {})
454
+ execution = Thor::Execution.new thor_class: self,
455
+ given_args: given_args,
456
+ thor_config: config
457
+
458
+ execution.exec!
459
+ end # #start
460
+
461
+
462
+ # Allows to use private methods from parent in child classes as commands.
463
+ #
464
+ # ==== Parameters
465
+ # names<Array>:: Method names to be used as commands
466
+ #
467
+ # ==== Examples
468
+ #
469
+ # public_command :foo
470
+ # public_command :foo, :bar, :baz
471
+ #
472
+ def public_command(*names)
473
+ names.each do |name|
474
+ class_eval "def #{name}(*); super end"
475
+ end
476
+ end
477
+ alias_method :public_task, :public_command
478
+
479
+
480
+ def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
481
+ if has_namespace
482
+ raise Thor::UndefinedCommandError,
483
+ "Could not find command #{command.inspect} in " \
484
+ "#{namespace.inspect} namespace."
485
+ end
486
+ raise Thor::UndefinedCommandError,
487
+ "Could not find command #{command.inspect}."
488
+ end
489
+ alias_method :handle_no_task_error, :handle_no_command_error
490
+
491
+
492
+ # Called in {Thor::Command#run} when an {ArgumentError} is raised and
493
+ # {Thor::Command#handle_argument_error?} returned `true`.
494
+ #
495
+ # Assembles a message and raises {Thor::InvocationError}.
496
+ #
497
+ # Defined on the Thor instance so it can be overridden to customize the
498
+ # message and/or error, as {Thor::Group} does in
499
+ # {Thor::Group#handle_argument_error}.
500
+ #
501
+ # @param [Thor::Command] command
502
+ # The command that encountered the {ArgumentError}.
503
+ #
504
+ # @param [ArgumentError] error
505
+ # The argument error itself.
506
+ #
507
+ # @param [Array] args
508
+ # The arguments the command was run with.
509
+ #
510
+ # @param [Fixnum?] arity
511
+ # The arity of the method on the Thor instance that was called, if known.
512
+ #
513
+ # Not used in this implementation.
514
+ #
515
+ # @raise [Thor::InvocationError]
516
+ # Always.
517
+ #
518
+ def handle_argument_error command, error, args, arity
519
+ name = [command.ancestor_name, command.name].compact.join(" ")
520
+ msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
521
+ msg << "no arguments" if args.empty?
522
+ msg << "arguments " << args.inspect unless args.empty?
523
+ msg << "\nUsage: #{banner(command).inspect}"
524
+ raise Thor::InvocationError, msg
525
+ end
526
+
527
+
528
+ protected # Mixin Methods
529
+ # ============================================================================
530
+ #
531
+ # (Which become protected class methods on includers of {Thor::Base})
532
+ #
533
+
534
+ # Prints the class options per group. If an option does not belong to
535
+ # any group, it's printed as Class option.
536
+ #
537
+ def class_options_help(shell, groups = {}) #:nodoc:
538
+ # Group options by group
539
+ class_options.each do |_, value|
540
+ groups[value.group] ||= []
541
+ groups[value.group] << value
542
+ end
543
+
544
+ # Deal with default group
545
+ global_options = groups.delete(nil) || []
546
+ print_options(shell, global_options)
547
+
548
+ # Print all others
549
+ groups.each do |group_name, options|
550
+ print_options(shell, options, group_name)
551
+ end
552
+ end
553
+
554
+
555
+ # Receives a set of options and print them.
556
+ def print_options(shell, options, group_name = nil)
557
+ return if options.empty?
558
+
559
+ list = []
560
+ padding = options.map { |o| o.aliases.size }.max.to_i * 4
561
+
562
+ options.each do |option|
563
+ next if option.hide
564
+ item = [option.usage(padding)]
565
+ item.push(option.description ? "# #{option.description}" : "")
566
+
567
+ list << item
568
+ list << ["", "# Default: #{option.default}"] if option.show_default?
569
+ if option.enum
570
+ list << ["", "# Possible values: #{option.enum.join(', ')}"]
571
+ end
572
+ end
573
+
574
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
575
+ shell.print_table(list, :indent => 2)
576
+ shell.say ""
577
+ end
578
+
579
+
580
+ # Raises an error if the word given is a Thor reserved word.
581
+ def is_thor_reserved_word?(word, type) #:nodoc:
582
+ return false unless Thor::THOR_RESERVED_WORDS.include?(word.to_s)
583
+ raise "#{word.inspect} is a Thor reserved word and cannot be " \
584
+ "defined as #{type}"
585
+ end
586
+
587
+
588
+ # Build an option and adds it to the given scope.
589
+ #
590
+ # ==== Parameters
591
+ # name<Symbol>:: The name of the argument.
592
+ # options<Hash>:: Described in both class_option and method_option.
593
+ # scope<Hash>:: Options hash that is being built up
594
+ def build_option(name, options, scope) #:nodoc:
595
+ scope[name] = Thor::Option.new(
596
+ name,
597
+ options.merge(:check_default_type => check_default_type?)
598
+ )
599
+ end
600
+
601
+
602
+ # Receives a hash of options, parse them and add to the scope. This is a
603
+ # fast way to set a bunch of options:
604
+ #
605
+ # build_options :foo => true, :bar => :required, :baz => :string
606
+ #
607
+ # ==== Parameters
608
+ # Hash[Symbol => Object]
609
+ def build_options(options, scope) #:nodoc:
610
+ options.each do |key, value|
611
+ scope[key] = Thor::Option.parse(key, value)
612
+ end
613
+ end
614
+
615
+
616
+ # Finds a command with the given name. If the command belongs to the current
617
+ # class, just return it, otherwise dup it and add the fresh copy to the
618
+ # current command hash.
619
+ def find_and_refresh_command(name) #:nodoc:
620
+ if commands[name.to_s]
621
+ commands[name.to_s]
622
+ elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
623
+ commands[name.to_s] = command.clone
624
+ else
625
+ raise ArgumentError,
626
+ "You supplied :for => #{name.inspect}, but the command " \
627
+ "#{name.inspect} could not be found."
628
+ end
629
+ end
630
+ alias_method :find_and_refresh_task, :find_and_refresh_command
631
+
632
+
633
+ # Everytime someone inherits from a Thor class, register the klass
634
+ # and file into baseclass.
635
+ def inherited(klass)
636
+ Thor::Base.register_klass_file(klass)
637
+ klass.instance_variable_set(:@no_commands, false)
638
+ end
639
+
640
+
641
+ # Fire this callback whenever a method is added. Added methods are
642
+ # tracked as commands by invoking the create_command method.
643
+ def method_added(meth)
644
+ meth = meth.to_s
645
+
646
+ if meth == "initialize"
647
+ initialize_added
648
+ return
649
+ end
650
+
651
+ # Return if it's not a public instance method
652
+ return unless public_method_defined?(meth.to_sym)
653
+
654
+ @no_commands ||= false
655
+ return if @no_commands || !create_command(meth)
656
+
657
+ is_thor_reserved_word?(meth, :command)
658
+ Thor::Base.register_klass_file(self)
659
+ end
660
+
661
+
662
+ # Retrieves a value from superclass. If it reaches the baseclass,
663
+ # returns default.
664
+ def from_superclass(method, default = nil)
665
+ if self == baseclass || !superclass.respond_to?(method, true)
666
+ default
667
+ else
668
+ value = superclass.send(method)
669
+
670
+ # Ruby implements `dup` on Object, but raises a `TypeError`
671
+ # if the method is called on immediates. As a result, we
672
+ # don't have a good way to check whether dup will succeed
673
+ # without calling it and rescuing the TypeError.
674
+ begin
675
+ value.dup
676
+ rescue TypeError
677
+ value
678
+ end
679
+
680
+ end
681
+ end
682
+
683
+
684
+ # A flag that makes the process exit with status 1 if any error happens.
685
+ def exit_on_failure?
686
+ false
687
+ end
688
+
689
+
690
+ # The basename of the program invoking the thor class.
691
+ #
692
+ def basename
693
+ File.basename($PROGRAM_NAME).split(" ").first
694
+ end
695
+
696
+
697
+ # Protected Mixin Method Declarations
698
+ # ------------------------------------------------------------------------
699
+ #
700
+ # These define method signatures that overriders/implementors should
701
+ # adhere to.
702
+ #
703
+
704
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
705
+ # finishes.
706
+ def baseclass #:nodoc:
707
+ end
708
+
709
+
710
+ # SIGNATURE: Creates a new command if valid_command? is true. This method
711
+ # is called when a new method is added to the class.
712
+ def create_command(meth) #:nodoc:
713
+ end
714
+ alias_method :create_task, :create_command
715
+
716
+
717
+ # SIGNATURE: Defines behavior when the initialize method is added to the
718
+ # class.
719
+ def initialize_added #:nodoc:
720
+ end
721
+
722
+
723
+ # SIGNATURE: The hook invoked by start.
724
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
725
+ raise NotImplementedError
726
+ end
727
+
728
+ public # end Protected Mixin Methods ***************************************
729
+
730
+ end # module Thor::Base::ClassMethods