bundler 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

@@ -54,6 +54,12 @@ Bundler::Dsl.
54
54
  #
55
55
  gem "rack", "1.0.0"
56
56
 
57
+ # Add a git repository as a source, and add a dependency on a gem
58
+ # from it
59
+ git "git://github.com/indirect/rails3-generators.git"
60
+ gem "rails3-generators"
61
+
62
+
57
63
  ### Groups
58
64
 
59
65
  Applications may have dependencies that are specific to certain environments,
@@ -4,7 +4,7 @@ require 'yaml'
4
4
  require 'bundler/rubygems-ext'
5
5
 
6
6
  module Bundler
7
- VERSION = "0.9.6"
7
+ VERSION = "0.9.7"
8
8
 
9
9
  autoload :Definition, 'bundler/definition'
10
10
  autoload :Dependency, 'bundler/dependency'
@@ -126,6 +126,12 @@ module Bundler
126
126
  Kernel.exec *ARGV
127
127
  end
128
128
 
129
+ desc "version", "Prints the bundler's version information"
130
+ def version
131
+ Bundler.ui.info "Bundler version #{Bundler::VERSION}"
132
+ end
133
+ map %w(-v --version) => :version
134
+
129
135
  private
130
136
 
131
137
  def locked?
@@ -16,15 +16,15 @@ module Bundler
16
16
  FileUtils.mkdir_p(Bundler.bundle_path)
17
17
 
18
18
  specs.sort_by { |s| s.name }.each do |spec|
19
- # unless spec.source.is_a?(Source::SystemGems)
20
- Bundler.ui.info "Installing #{spec.name} (#{spec.version}) from #{spec.source} "
21
- # end
22
-
23
19
  if (spec.groups & options[:without]).any?
24
20
  Bundler.ui.debug " * Not in requested group; skipping."
25
21
  next
26
22
  end
27
23
 
24
+ # unless spec.source.is_a?(Source::SystemGems)
25
+ Bundler.ui.info "Installing #{spec.name} (#{spec.version}) from #{spec.source} "
26
+ # end
27
+
28
28
  spec.source.install(spec)
29
29
 
30
30
  Bundler.ui.info ""
@@ -1,6 +1,6 @@
1
1
  require 'set'
2
2
  # This is the latest iteration of the gem dependency resolving algorithm. As of now,
3
- # it can resolve (as a success of failure) any set of gem dependencies we throw at it
3
+ # it can resolve (as a success or failure) any set of gem dependencies we throw at it
4
4
  # in a reasonable amount of time. The most iterations I've seen it take is about 150.
5
5
  # The actual implementation of the algorithm is not as good as it could be yet, but that
6
6
  # can come later.
@@ -172,13 +172,10 @@ module Bundler
172
172
  end
173
173
 
174
174
  def specs_for_lock_file
175
- specs.map do |spec|
176
- dep = @definition.dependencies.find { |d| d.name == spec.name }
175
+ specs.map do |s|
177
176
  hash = {}
178
- hash[:name] = spec.name
179
- hash[:version] = spec.version.to_s
180
- hash[:groups] = spec.groups
181
- hash[:load_paths] = spec.load_paths
177
+ hash[:loaded_from] = s.loaded_from.to_s
178
+ hash[:load_paths] = s.load_paths
182
179
  hash
183
180
  end
184
181
  end
@@ -213,70 +210,5 @@ module Bundler
213
210
  groups.inject({}) { |h,g| h[g] = autorequires[g]; h }
214
211
  end
215
212
  end
216
-
217
- def cripple_rubygems(specs)
218
- reverse_rubygems_kernel_mixin
219
-
220
- executables = specs.map { |s| s.executables }.flatten
221
-
222
- # TODO: This is duplicated a bit too much in environment.erb.
223
- # Let's figure out how to improve that.
224
- ::Kernel.send(:define_method, :gem) do |dep, *reqs|
225
- if executables.include? File.basename(caller.first.split(':').first)
226
- return
227
- end
228
- opts = reqs.last.is_a?(Hash) ? reqs.pop : {}
229
-
230
- unless dep.respond_to?(:name) && dep.respond_to?(:version_requirements)
231
- dep = Gem::Dependency.new(dep, reqs)
232
- end
233
-
234
- spec = specs.find { |s| s.name == dep.name }
235
-
236
- if spec.nil?
237
- e = Gem::LoadError.new "#{dep} is not part of the bundle. Add it to Gemfile."
238
- e.name = dep.name
239
- e.version_requirement = dep.version_requirements
240
- raise e
241
- elsif dep !~ spec
242
- e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
243
- "Make sure all dependencies are added to Gemfile."
244
- e.name = dep.name
245
- e.version_requirement = dep.version_requirements
246
- raise e
247
- end
248
-
249
- true
250
- end
251
-
252
- # === Following hacks are to improve on the generated bin wrappers ===
253
-
254
- # Yeah, talk about a hack
255
- source_index_class = (class << Gem::SourceIndex ; self ; end)
256
- source_index_class.send(:define_method, :from_gems_in) do |*args|
257
- source_index = Gem::SourceIndex.new
258
- source_index.spec_dirs = *args
259
- source_index.add_specs(*specs)
260
- source_index
261
- end
262
-
263
- # OMG more hacks
264
- gem_class = (class << Gem ; self ; end)
265
- gem_class.send(:define_method, :bin_path) do |name, *args|
266
- exec_name, *reqs = args
267
-
268
- spec = nil
269
-
270
- if exec_name
271
- spec = specs.find { |s| s.executables.include?(exec_name) }
272
- spec or raise Gem::Exception, "can't find executable #{exec_name}"
273
- else
274
- spec = specs.find { |s| s.name == name }
275
- exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
276
- end
277
-
278
- File.join(spec.full_gem_path, spec.bindir, exec_name)
279
- end
280
- end
281
213
  end
282
214
  end
@@ -1,18 +1,6 @@
1
1
  module Bundler
2
2
  module SharedHelpers
3
3
 
4
- def reverse_rubygems_kernel_mixin
5
- # Disable rubygems' gem activation system
6
- ::Kernel.class_eval do
7
- if private_method_defined?(:gem_original_require)
8
- alias rubygems_require require
9
- alias require gem_original_require
10
- end
11
-
12
- undef gem
13
- end
14
- end
15
-
16
4
  def default_gemfile
17
5
  gemfile = find_gemfile
18
6
  gemfile or raise GemfileNotFound, "The default Gemfile was not found"
@@ -51,6 +39,87 @@ module Bundler
51
39
  end
52
40
  end
53
41
 
42
+ def reverse_rubygems_kernel_mixin
43
+ # Disable rubygems' gem activation system
44
+ ::Kernel.class_eval do
45
+ if private_method_defined?(:gem_original_require)
46
+ alias rubygems_require require
47
+ alias require gem_original_require
48
+ end
49
+
50
+ undef gem
51
+ end
52
+ end
53
+
54
+ def cripple_rubygems(specs)
55
+ reverse_rubygems_kernel_mixin
56
+
57
+ executables = specs.map { |s| s.executables }.flatten
58
+
59
+ :: Kernel.class_eval do
60
+ private
61
+ def gem(*) ; end
62
+ end
63
+ Gem.source_index # ensure RubyGems is fully loaded
64
+
65
+ ::Kernel.send(:define_method, :gem) do |dep, *reqs|
66
+ if executables.include? File.basename(caller.first.split(':').first)
67
+ return
68
+ end
69
+ opts = reqs.last.is_a?(Hash) ? reqs.pop : {}
70
+
71
+ unless dep.respond_to?(:name) && dep.respond_to?(:version_requirements)
72
+ dep = Gem::Dependency.new(dep, reqs)
73
+ end
74
+
75
+ spec = specs.find { |s| s.name == dep.name }
76
+
77
+ if spec.nil?
78
+ e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
79
+ e.name = dep.name
80
+ e.version_requirement = dep.version_requirements
81
+ raise e
82
+ elsif dep !~ spec
83
+ e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
84
+ "Make sure all dependencies are added to Gemfile."
85
+ e.name = dep.name
86
+ e.version_requirement = dep.version_requirements
87
+ raise e
88
+ end
89
+
90
+ true
91
+ end
92
+
93
+ # === Following hacks are to improve on the generated bin wrappers ===
94
+
95
+ # Yeah, talk about a hack
96
+ source_index_class = (class << Gem::SourceIndex ; self ; end)
97
+ source_index_class.send(:define_method, :from_gems_in) do |*args|
98
+ source_index = Gem::SourceIndex.new
99
+ source_index.spec_dirs = *args
100
+ source_index.add_specs(*specs)
101
+ source_index
102
+ end
103
+
104
+ # OMG more hacks
105
+ gem_class = (class << Gem ; self ; end)
106
+ gem_class.send(:define_method, :bin_path) do |name, *args|
107
+ exec_name, *reqs = args
108
+
109
+ spec = nil
110
+
111
+ if exec_name
112
+ spec = specs.find { |s| s.executables.include?(exec_name) }
113
+ spec or raise Gem::Exception, "can't find executable #{exec_name}"
114
+ else
115
+ spec = specs.find { |s| s.name == name }
116
+ exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
117
+ end
118
+
119
+ File.join(spec.full_gem_path, spec.bindir, exec_name)
120
+ end
121
+ end
122
+
54
123
  extend self
55
124
  end
56
125
  end
@@ -8,20 +8,20 @@ require "rubygems"
8
8
  module Bundler
9
9
  LOCKED_BY = '<%= Bundler::VERSION %>'
10
10
  FINGERPRINT = <%= gemfile_fingerprint.inspect %>
11
+ AUTOREQUIRES = <%= autorequires_for_groups.inspect %>
11
12
  SPECS = [
12
13
  <% specs_for_lock_file.each do |spec| -%>
13
14
  <%= spec.inspect %>,
14
15
  <% end -%>
15
- ]
16
- AUTOREQUIRES = <%= autorequires_for_groups.inspect %>
16
+ ].map do |hash|
17
+ spec = eval(File.read(hash[:loaded_from]), binding, hash[:loaded_from])
18
+ spec.loaded_from = hash[:loaded_from]
19
+ spec.require_paths = hash[:load_paths]
20
+ spec
21
+ end
17
22
 
18
23
  extend SharedHelpers
19
24
 
20
- def self.cripple_ruby_gems
21
- reverse_rubygems_kernel_mixin
22
- patch_rubygems
23
- end
24
-
25
25
  def self.match_fingerprint
26
26
  print = Digest::SHA1.hexdigest(File.read(File.expand_path('../../Gemfile', __FILE__)))
27
27
  unless print == FINGERPRINT
@@ -32,8 +32,10 @@ module Bundler
32
32
  def self.setup(*groups)
33
33
  match_fingerprint
34
34
  clean_load_path
35
+ cripple_rubygems(SPECS)
35
36
  SPECS.each do |spec|
36
- spec[:load_paths].each { |path| $LOAD_PATH.unshift path }
37
+ Gem.loaded_specs[spec.name] = spec
38
+ $LOAD_PATH.unshift(*spec.require_paths)
37
39
  end
38
40
  end
39
41
 
@@ -53,30 +55,6 @@ module Bundler
53
55
  end
54
56
  end
55
57
 
56
- def self.patch_rubygems
57
- Kernel.class_eval do
58
- private
59
- def gem(*) ; end
60
- end
61
- Gem.source_index # ensure RubyGems is fully loaded
62
- specs = SPECS
63
-
64
- ::Kernel.send(:define_method, :gem) do |dep, *reqs|
65
- opts = reqs.last.is_a?(Hash) ? reqs.pop : {}
66
-
67
- dep = dep.name if dep.respond_to?(:name)
68
- unless specs.any? { |s| s[:name] == dep }
69
- e = Gem::LoadError.new "#{dep} is not part of the bundle. Add it to Gemfile."
70
- e.name = dep
71
- e.version_requirement = reqs
72
- raise e
73
- end
74
-
75
- true
76
- end
77
- end
78
-
79
58
  # Setup bundle when it's required.
80
- cripple_ruby_gems
81
59
  setup
82
60
  end
@@ -120,8 +120,8 @@ class Thor
120
120
  # script = MyScript.new(args, options, config)
121
121
  # script.invoke(:task, first_arg, second_arg, third_arg)
122
122
  #
123
- def start(given_args=ARGV, config={})
124
- super do
123
+ def start(original_args=ARGV, config={})
124
+ super do |given_args|
125
125
  meth = normalize_task_name(given_args.shift)
126
126
  task = all_tasks[meth]
127
127
 
@@ -145,8 +145,9 @@ class Thor
145
145
  # task_name<String>
146
146
  #
147
147
  def task_help(shell, task_name)
148
- task = all_tasks[task_name]
149
- raise UndefinedTaskError, "task '#{task_name}' could not be found in namespace '#{self.namespace}'" unless task
148
+ meth = normalize_task_name(task_name)
149
+ task = all_tasks[meth]
150
+ handle_no_task_error(meth) unless task
150
151
 
151
152
  shell.say "Usage:"
152
153
  shell.say " #{banner(task)}"
@@ -183,6 +184,10 @@ class Thor
183
184
  end
184
185
  end
185
186
 
187
+ def handle_argument_error(task, error) #:nodoc:
188
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{task.formatted_usage(self, banner_base == "thor").inspect}."
189
+ end
190
+
186
191
  protected
187
192
 
188
193
  # The banner for this class. You can customize it if you are invoking the
@@ -191,8 +196,7 @@ class Thor
191
196
  # the namespace should be displayed as arguments.
192
197
  #
193
198
  def banner(task)
194
- base = $thor_runner ? "thor" : File.basename($0.split(" ").first)
195
- "#{base} #{task.formatted_usage(self, base == "thor")}"
199
+ "#{banner_base} #{task.formatted_usage(self, banner_base == "thor")}"
196
200
  end
197
201
 
198
202
  def baseclass #:nodoc:
@@ -38,9 +38,8 @@ class Thor
38
38
  # config<Hash>:: Configuration for this Thor class.
39
39
  #
40
40
  def initialize(args=[], options={}, config={})
41
- Thor::Arguments.parse(self.class.arguments, args).each do |key, value|
42
- send("#{key}=", value)
43
- end
41
+ args = Thor::Arguments.parse(self.class.arguments, args)
42
+ args.each { |key, value| send("#{key}=", value) }
44
43
 
45
44
  parse_options = self.class.class_options
46
45
 
@@ -52,9 +51,9 @@ class Thor
52
51
  array_options, hash_options = [], options
53
52
  end
54
53
 
55
- options = Thor::Options.parse(parse_options, array_options)
56
- self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options)
57
- self.options.freeze
54
+ opts = Thor::Options.new(parse_options, hash_options)
55
+ self.options = opts.parse(array_options)
56
+ opts.check_unknown! if self.class.check_unknown_options?
58
57
  end
59
58
 
60
59
  class << self
@@ -109,6 +108,16 @@ class Thor
109
108
  no_tasks { super }
110
109
  end
111
110
 
111
+ # If you want to raise an error for unknown options, call check_unknown_options!
112
+ # This is disabled by default to allow dynamic invocations.
113
+ def check_unknown_options!
114
+ @check_unknown_options = true
115
+ end
116
+
117
+ def check_unknown_options? #:nodoc:
118
+ @check_unknown_options || false
119
+ end
120
+
112
121
  # Adds an argument to the class and creates an attr_accessor for it.
113
122
  #
114
123
  # Arguments are different from options in several aspects. The first one
@@ -355,7 +364,7 @@ class Thor
355
364
  def namespace(name=nil)
356
365
  case name
357
366
  when nil
358
- @namespace ||= Thor::Util.namespace_from_thor_class(self, false)
367
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
359
368
  else
360
369
  @namespace = name.to_s
361
370
  end
@@ -366,14 +375,18 @@ class Thor
366
375
  def start(given_args=ARGV, config={})
367
376
  self.debugging = given_args.include?("--debug")
368
377
  config[:shell] ||= Thor::Base.shell.new
369
- yield
378
+ yield(given_args.dup)
370
379
  rescue Thor::Error => e
371
- if debugging
372
- raise e
380
+ debugging ? (raise e) : config[:shell].error(e.message)
381
+ exit(1) if exit_on_failure?
382
+ end
383
+
384
+ def handle_no_task_error(task) #:nodoc:
385
+ if self.banner_base == "thor"
386
+ raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
373
387
  else
374
- config[:shell].error e.message
388
+ raise UndefinedTaskError, "Could not find task #{task.inspect}."
375
389
  end
376
- exit(1) if exit_on_failure?
377
390
  end
378
391
 
379
392
  protected
@@ -419,7 +432,6 @@ class Thor
419
432
  end
420
433
 
421
434
  # Raises an error if the word given is a Thor reserved word.
422
- #
423
435
  def is_thor_reserved_word?(word, type) #:nodoc:
424
436
  return false unless THOR_RESERVED_WORDS.include?(word.to_s)
425
437
  raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
@@ -430,7 +442,6 @@ class Thor
430
442
  # ==== Parameters
431
443
  # name<Symbol>:: The name of the argument.
432
444
  # options<Hash>:: Described in both class_option and method_option.
433
- #
434
445
  def build_option(name, options, scope) #:nodoc:
435
446
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
436
447
  options[:type], options[:default], options[:banner],
@@ -444,7 +455,6 @@ class Thor
444
455
  #
445
456
  # ==== Parameters
446
457
  # Hash[Symbol => Object]
447
- #
448
458
  def build_options(options, scope) #:nodoc:
449
459
  options.each do |key, value|
450
460
  scope[key] = Thor::Option.parse(key, value)
@@ -454,7 +464,6 @@ class Thor
454
464
  # Finds a task with the given name. If the task belongs to the current
455
465
  # class, just return it, otherwise dup it and add the fresh copy to the
456
466
  # current task hash.
457
- #
458
467
  def find_and_refresh_task(name) #:nodoc:
459
468
  task = if task = tasks[name.to_s]
460
469
  task
@@ -467,14 +476,12 @@ class Thor
467
476
 
468
477
  # Everytime someone inherits from a Thor class, register the klass
469
478
  # and file into baseclass.
470
- #
471
479
  def inherited(klass)
472
480
  Thor::Base.register_klass_file(klass)
473
481
  end
474
482
 
475
483
  # Fire this callback whenever a method is added. Added methods are
476
484
  # tracked as tasks by invoking the create_task method.
477
- #
478
485
  def method_added(meth)
479
486
  meth = meth.to_s
480
487
 
@@ -495,7 +502,6 @@ class Thor
495
502
 
496
503
  # Retrieves a value from superclass. If it reaches the baseclass,
497
504
  # returns default.
498
- #
499
505
  def from_superclass(method, default=nil)
500
506
  if self == baseclass || !superclass.respond_to?(method, true)
501
507
  default
@@ -506,11 +512,15 @@ class Thor
506
512
  end
507
513
 
508
514
  # A flag that makes the process exit with status 1 if any error happens.
509
- #
510
515
  def exit_on_failure?
511
516
  false
512
517
  end
513
518
 
519
+ # Returns the base for banner.
520
+ def banner_base
521
+ @banner_base ||= $thor_runner ? "thor" : File.basename($0.split(" ").first)
522
+ end
523
+
514
524
  # SIGNATURE: Sets the baseclass. This is where the superclass lookup
515
525
  # finishes.
516
526
  def baseclass #:nodoc:
@@ -19,6 +19,9 @@ class Thor
19
19
  class InvocationError < Error
20
20
  end
21
21
 
22
+ class UnknownArgumentError < Error
23
+ end
24
+
22
25
  class RequiredArgumentMissingError < InvocationError
23
26
  end
24
27
 
@@ -5,21 +5,20 @@ class Thor
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- # Prepare for class methods invocations. This method must return a klass to
9
- # have the invoked class options showed in help messages in generators.
10
- #
8
+ # This method is responsible for receiving a name and find the proper
9
+ # class and task for it. The key is an optional parameter which is
10
+ # available only in class methods invocations (i.e. in Thor::Group).
11
11
  def prepare_for_invocation(key, name) #:nodoc:
12
12
  case name
13
13
  when Symbol, String
14
- Thor::Util.namespace_to_thor_class_and_task(name.to_s, false)
14
+ Thor::Util.find_class_and_task_by_namespace(name.to_s)
15
15
  else
16
16
  name
17
17
  end
18
18
  end
19
19
  end
20
20
 
21
- # Make initializer aware of invocations and the initializer proc.
22
- #
21
+ # Make initializer aware of invocations and the initialization args.
23
22
  def initialize(args=[], options={}, config={}, &block) #:nodoc:
24
23
  @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
25
24
  @_initializer = [ args, options, config ]
@@ -34,6 +33,8 @@ class Thor
34
33
  # the task to be invoked, if none is given, the same values used to
35
34
  # initialize the invoker are used to initialize the invoked.
36
35
  #
36
+ # When no name is given, it will invoke the default task of the current class.
37
+ #
37
38
  # ==== Examples
38
39
  #
39
40
  # class A < Thor
@@ -92,9 +93,9 @@ class Thor
92
93
  #
93
94
  # invoke Rspec::RR, [], :style => :foo
94
95
  #
95
- def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil)
96
- task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array)
97
- args, opts, config = nil, args, opts if args.is_a?(Hash)
96
+ def invoke(name=nil, *args)
97
+ args.unshift(nil) if Array === args.first || NilClass === args.first
98
+ task, args, opts, config = args
98
99
 
99
100
  object, task = _prepare_for_invocation(name, task)
100
101
  klass, instance = _initialize_klass_with_initializer(object, args, opts, config)
@@ -121,15 +122,13 @@ class Thor
121
122
  protected
122
123
 
123
124
  # Configuration values that are shared between invocations.
124
- #
125
125
  def _shared_configuration #:nodoc:
126
126
  { :invocations => @_invocations }
127
127
  end
128
128
 
129
- # Prepare for invocation in the instance level. In this case, we have to
130
- # take into account that a just a task name from the current class was
131
- # given or even a Thor::Task object.
132
- #
129
+ # This method can receive several different types of arguments and it's then
130
+ # responsible to normalize them by returning the object where the task should
131
+ # be invoked and a Thor::Task object.
133
132
  def _prepare_for_invocation(name, sent_task=nil) #:nodoc:
134
133
  if name.is_a?(Thor::Task)
135
134
  task = name
@@ -147,18 +146,16 @@ class Thor
147
146
 
148
147
  # Check if the object given is a Thor class object and get a task object
149
148
  # for it.
150
- #
151
149
  def _validate_task(object, task) #:nodoc:
152
150
  klass = object.is_a?(Class) ? object : object.class
153
151
  raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
154
152
 
155
- task ||= klass.default_task if klass <= Thor
153
+ task ||= klass.default_task if klass.respond_to?(:default_task)
156
154
  task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task)
157
155
  task
158
156
  end
159
157
 
160
158
  # Initialize klass using values stored in the @_initializer.
161
- #
162
159
  def _initialize_klass_with_initializer(object, args, opts, config) #:nodoc:
163
160
  if object.is_a?(Class)
164
161
  klass = object
@@ -16,8 +16,9 @@ class Thor
16
16
  return arguments, args[Range.new(arguments.size, -1)]
17
17
  end
18
18
 
19
- def self.parse(base, args)
20
- new(base).parse(args)
19
+ def self.parse(*args)
20
+ to_parse = args.pop
21
+ new(*args).parse(to_parse)
21
22
  end
22
23
 
23
24
  # Takes an array of Thor::Argument objects.
@@ -116,7 +117,7 @@ class Thor
116
117
  return shift if peek.is_a?(Numeric)
117
118
 
118
119
  unless peek =~ NUMERIC && $& == peek
119
- raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}"
120
+ raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
120
121
  end
121
122
 
122
123
  $&.index('.') ? shift.to_f : shift.to_i
@@ -137,7 +138,7 @@ class Thor
137
138
  end.join("', '")
138
139
 
139
140
  class_name = self.class.name.split('::').last.downcase
140
- raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'"
141
+ raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
141
142
  end
142
143
  end
143
144
 
@@ -55,10 +55,6 @@ class Thor
55
55
  value
56
56
  elsif required = (value == :required)
57
57
  :string
58
- elsif value == :optional
59
- # TODO Remove this warning in the future.
60
- warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean."
61
- :boolean
62
58
  end
63
59
  when TrueClass, FalseClass
64
60
  :boolean
@@ -10,7 +10,6 @@ class Thor
10
10
  SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
11
11
 
12
12
  # Receives a hash and makes it switches.
13
- #
14
13
  def self.to_switches(options)
15
14
  options.map do |key, value|
16
15
  case value
@@ -28,12 +27,18 @@ class Thor
28
27
  end.join(" ")
29
28
  end
30
29
 
31
- # Takes a hash of Thor::Option objects.
32
- #
33
- def initialize(options={})
34
- options = options.values
30
+ # Takes a hash of Thor::Option and a hash with defaults.
31
+ def initialize(hash_options={}, defaults={})
32
+ options = hash_options.values
35
33
  super(options)
36
- @shorts, @switches = {}, {}
34
+
35
+ # Add defaults
36
+ defaults.each do |key, value|
37
+ @assigns[key.to_s] = value
38
+ @non_assigned_required.delete(hash_options[key])
39
+ end
40
+
41
+ @shorts, @switches, @unknown = {}, {}, []
37
42
 
38
43
  options.each do |option|
39
44
  @switches[option.switch_name] = option
@@ -61,16 +66,24 @@ class Thor
61
66
  end
62
67
 
63
68
  switch = normalize_switch(switch)
64
- next unless option = switch_option(switch)
65
-
69
+ option = switch_option(switch)
66
70
  @assigns[option.human_name] = parse_peek(switch, option)
71
+ elsif peek =~ /^\-/
72
+ @unknown << shift
67
73
  else
68
74
  shift
69
75
  end
70
76
  end
71
77
 
72
78
  check_requirement!
73
- @assigns
79
+
80
+ assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
81
+ assigns.freeze
82
+ assigns
83
+ end
84
+
85
+ def check_unknown!
86
+ raise UnknownArgumentError, "Unknown switches '#{@unknown.join(', ')}'" unless @unknown.empty?
74
87
  end
75
88
 
76
89
  protected
@@ -130,7 +143,7 @@ class Thor
130
143
  elsif option.string? && !option.required?
131
144
  return option.human_name # Return the option name
132
145
  else
133
- raise MalformattedArgumentError, "no value provided for option '#{switch}'"
146
+ raise MalformattedArgumentError, "No value provided for option '#{switch}'"
134
147
  end
135
148
  end
136
149
 
@@ -9,10 +9,11 @@ class Thor
9
9
  end
10
10
 
11
11
  def run(instance, args=[])
12
- unless (instance.methods & [name.to_s, name.to_sym]).empty?
13
- raise Error, "could not find Thor class or task '#{name}'"
12
+ if (instance.methods & [name.to_s, name.to_sym]).empty?
13
+ super
14
+ else
15
+ instance.class.handle_no_task_error(name)
14
16
  end
15
- super
16
17
  end
17
18
  end
18
19
 
@@ -28,14 +29,14 @@ class Thor
28
29
  # By default, a task invokes a method in the thor class. You can change this
29
30
  # implementation to create custom tasks.
30
31
  def run(instance, args=[])
31
- raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
32
- instance.send(name, *args)
32
+ public_method?(instance) ?
33
+ instance.send(name, *args) : instance.class.handle_no_task_error(name)
33
34
  rescue ArgumentError => e
34
- raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
35
- parse_argument_error(instance, e, caller)
35
+ handle_argument_error?(instance, e, caller) ?
36
+ instance.class.handle_argument_error(self, e) : (raise e)
36
37
  rescue NoMethodError => e
37
- raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
38
- parse_no_method_error(instance, e)
38
+ handle_no_method_error?(instance, e, caller) ?
39
+ instance.class.handle_no_task_error(name) : (raise e)
39
40
  end
40
41
 
41
42
  # Returns the formatted usage by injecting given required arguments
@@ -68,6 +69,10 @@ class Thor
68
69
 
69
70
  protected
70
71
 
72
+ def not_debugging?(instance)
73
+ !(instance.class.respond_to?(:debugging) && instance.class.debugging)
74
+ end
75
+
71
76
  def required_options
72
77
  @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
73
78
  end
@@ -83,28 +88,14 @@ class Thor
83
88
  saned -= caller
84
89
  end
85
90
 
86
- def parse_argument_error(instance, e, caller) #:nodoc:
87
- backtrace = sans_backtrace(e.backtrace, caller)
88
-
89
- if backtrace.empty? && e.message =~ /wrong number of arguments/
90
- if instance.is_a?(Thor::Group)
91
- raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
92
- else
93
- raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
94
- "'#{formatted_usage(instance.class)}'"
95
- end
96
- else
97
- raise e
98
- end
91
+ def handle_argument_error?(instance, error, caller)
92
+ not_debugging?(instance) && error.message =~ /wrong number of arguments/ &&
93
+ sans_backtrace(error.backtrace, caller).empty?
99
94
  end
100
95
 
101
- def parse_no_method_error(instance, e) #:nodoc:
102
- if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
103
- raise UndefinedTaskError, "The #{instance.class.namespace} namespace " <<
104
- "doesn't have a '#{name}' task"
105
- else
106
- raise e
107
- end
96
+ def handle_no_method_error?(instance, error, caller)
97
+ not_debugging?(instance) &&
98
+ error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
108
99
  end
109
100
 
110
101
  end
@@ -23,10 +23,7 @@ class Thor
23
23
  #
24
24
  def self.find_by_namespace(namespace)
25
25
  namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/
26
-
27
- Thor::Base.subclasses.find do |klass|
28
- klass.namespace == namespace
29
- end
26
+ Thor::Base.subclasses.find { |klass| klass.namespace == namespace }
30
27
  end
31
28
 
32
29
  # Receives a constant and converts it to a Thor namespace. Since Thor tasks
@@ -43,10 +40,9 @@ class Thor
43
40
  # ==== Returns
44
41
  # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
45
42
  #
46
- def self.namespace_from_thor_class(constant, remove_default=true)
43
+ def self.namespace_from_thor_class(constant)
47
44
  constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
48
45
  constant = snake_case(constant).squeeze(":")
49
- constant.gsub!(/^default/, '') if remove_default
50
46
  constant
51
47
  end
52
48
 
@@ -132,13 +128,7 @@ class Thor
132
128
  # ==== Parameters
133
129
  # namespace<String>
134
130
  #
135
- # ==== Errors
136
- # Thor::Error:: raised if the namespace cannot be found.
137
- #
138
- # Thor::Error:: raised if the namespace evals to a class which does not
139
- # inherit from Thor or Thor::Group.
140
- #
141
- def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true)
131
+ def self.find_class_and_task_by_namespace(namespace)
142
132
  if namespace.include?(?:)
143
133
  pieces = namespace.split(":")
144
134
  task = pieces.pop
@@ -149,7 +139,14 @@ class Thor
149
139
  klass, task = Thor::Util.find_by_namespace(namespace), nil
150
140
  end
151
141
 
152
- raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil?
142
+ return klass, task
143
+ end
144
+
145
+ # The same as namespace_to_thor_class_and_task!, but raises an error if a klass
146
+ # could not be found.
147
+ def self.find_class_and_task_by_namespace!(namespace)
148
+ klass, task = find_class_and_task_by_namespace(namespace)
149
+ raise Error, "Could not find namespace or task #{namespace.inspect}." unless klass
153
150
  return klass, task
154
151
  end
155
152
 
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.12.4".freeze
2
+ VERSION = "0.13.3".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Lerche
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2010-02-16 00:00:00 -08:00
13
+ date: 2010-02-17 00:00:00 -08:00
14
14
  default_executable:
15
15
  dependencies: []
16
16