bundler 1.0.0.beta.5 → 1.0.0.beta.8

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.

@@ -2,24 +2,31 @@ module Bundler
2
2
  class Settings
3
3
  def initialize(root)
4
4
  @root = root
5
- @config = File.exist?(config_file) ? YAML.load_file(config_file) : {}
5
+ @local_config = File.exist?(local_config_file) ? YAML.load_file(local_config_file) : {}
6
+ @global_config = File.exist?(global_config_file) ? YAML.load_file(global_config_file) : {}
6
7
  end
7
8
 
8
9
  def [](key)
9
10
  key = key_for(key)
10
- @config[key] || ENV[key]
11
+ @local_config[key] || ENV[key] || @global_config[key]
11
12
  end
12
13
 
13
14
  def []=(key, value)
15
+ set_key(key, value, @local_config, local_config_file)
16
+ end
17
+
18
+ def set_global(key, value)
19
+ set_key(key, value, @global_config, global_config_file)
20
+ end
21
+
22
+ def locations(key)
14
23
  key = key_for(key)
15
- unless @config[key] == value
16
- @config[key] = value
17
- FileUtils.mkdir_p(config_file.dirname)
18
- File.open(config_file, 'w') do |f|
19
- f.puts @config.to_yaml
20
- end
21
- end
22
- value
24
+
25
+ locations = {}
26
+ locations[:local] = @local_config[key]
27
+ locations[:env] = ENV[key]
28
+ locations[:global] = @global_config[key]
29
+ locations
23
30
  end
24
31
 
25
32
  def without=(array)
@@ -32,11 +39,10 @@ module Bundler
32
39
  self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
33
40
  end
34
41
 
35
- # @config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
42
+ # @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
36
43
  def path
37
- path = ENV[key_for(:path)]
38
-
39
- return path if path && !@config.key?(key_for(:path))
44
+ path = ENV[key_for(:path)] || @global_config[key_for(:path)]
45
+ return path if path && !@local_config.key?(key_for(:path))
40
46
 
41
47
  if path = self[:path]
42
48
  "#{path}/#{Bundler.ruby_scope}"
@@ -47,12 +53,28 @@ module Bundler
47
53
 
48
54
  private
49
55
 
56
+ def set_key(key, value, hash, file)
57
+ key = key_for(key)
58
+
59
+ unless hash[key] == value
60
+ hash[key] = value
61
+ FileUtils.mkdir_p(file.dirname)
62
+ File.open(file, "w") { |f| f.puts hash.to_yaml }
63
+ end
64
+ value
65
+ end
66
+
50
67
  def key_for(key)
51
68
  "BUNDLE_#{key.to_s.upcase}"
52
69
  end
53
70
 
54
- def config_file
71
+ def global_config_file
72
+ file = ENV["BUNDLE_CONFIG"] || File.join(Gem.user_home, ".bundle/config")
73
+ Pathname.new(file)
74
+ end
75
+
76
+ def local_config_file
55
77
  Pathname.new("#{@root}/.bundle/config")
56
78
  end
57
79
  end
58
- end
80
+ end
@@ -258,7 +258,7 @@ module Bundler
258
258
  class Path
259
259
  attr_reader :path, :options
260
260
  # Kind of a hack, but needed for the lock file parser
261
- attr_accessor :version
261
+ attr_accessor :name, :version
262
262
 
263
263
  DEFAULT_GLOB = "{,*/}*.gemspec"
264
264
 
@@ -523,7 +523,7 @@ module Bundler
523
523
 
524
524
  # TODO: actually cache git specs
525
525
  def specs
526
- if (@allow_remote || @allow_cached) && !@update
526
+ if allow_git_ops? && !@update
527
527
  # Start by making sure the git cache is up to date
528
528
  cache
529
529
  checkout
@@ -537,7 +537,7 @@ module Bundler
537
537
 
538
538
  unless @installed
539
539
  Bundler.ui.debug " * Checking out revision: #{ref}"
540
- checkout
540
+ checkout if allow_git_ops?
541
541
  @installed = true
542
542
  end
543
543
  generate_bin(spec)
@@ -553,12 +553,18 @@ module Bundler
553
553
  private
554
554
 
555
555
  def git(command)
556
- out = %x{git #{command}}
556
+ if allow_git_ops?
557
+ out = %x{git #{command}}
557
558
 
558
- if $? != 0
559
- raise GitError, "An error has occurred in git. Cannot complete bundling."
559
+ if $? != 0
560
+ raise GitError, "An error has occurred in git. Cannot complete bundling."
561
+ end
562
+ out
563
+ else
564
+ raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
565
+ "this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
566
+ "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
560
567
  end
561
- out
562
568
  end
563
569
 
564
570
  def base_name
@@ -595,6 +601,7 @@ module Bundler
595
601
 
596
602
  def cache
597
603
  if cached?
604
+ return if has_revision_cached?
598
605
  Bundler.ui.info "Updating #{uri}"
599
606
  in_cache { git %|fetch --force --quiet "#{uri}" refs/heads/*:refs/heads/*| }
600
607
  else
@@ -620,8 +627,24 @@ module Bundler
620
627
  end
621
628
  end
622
629
 
630
+ def has_revision_cached?
631
+ return unless @revision
632
+ in_cache { git %|cat-file -t #{@revision}| }
633
+ $? == 0
634
+ end
635
+
636
+ def allow_git_ops?
637
+ @allow_remote || @allow_cached
638
+ end
639
+
623
640
  def revision
624
- @revision ||= in_cache { git("rev-parse #{ref}").strip }
641
+ @revision ||= begin
642
+ if allow_git_ops?
643
+ in_cache { git("rev-parse #{ref}").strip }
644
+ else
645
+ raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
646
+ end
647
+ end
625
648
  end
626
649
 
627
650
  def cached?
@@ -127,7 +127,7 @@ module Bundler
127
127
  def tsort_each_child(s)
128
128
  s.dependencies.sort_by { |d| d.name }.each do |d|
129
129
  next if d.type == :development
130
- lookup[d.name].each { |s| yield s }
130
+ lookup[d.name].each { |s2| yield s2 }
131
131
  end
132
132
  end
133
133
  end
@@ -1,6 +1,5 @@
1
1
  require 'thor/base'
2
2
 
3
- # TODO: Update thor to allow for git-style CLI (git bisect run)
4
3
  class Thor
5
4
  class << self
6
5
  # Sets the default task when thor is executed without an explicit task to be called.
@@ -24,6 +23,7 @@ class Thor
24
23
  # ==== Parameters
25
24
  # usage<String>
26
25
  # description<String>
26
+ # options<String>
27
27
  #
28
28
  def desc(usage, description, options={})
29
29
  if options[:for]
@@ -31,7 +31,7 @@ class Thor
31
31
  task.usage = usage if usage
32
32
  task.description = description if description
33
33
  else
34
- @usage, @desc = usage, description
34
+ @usage, @desc, @hide = usage, description, options[:hide] || false
35
35
  end
36
36
  end
37
37
 
@@ -135,6 +135,8 @@ class Thor
135
135
  # script.invoke(:task, first_arg, second_arg, third_arg)
136
136
  #
137
137
  def start(original_args=ARGV, config={})
138
+ @@original_args = original_args
139
+
138
140
  super do |given_args|
139
141
  meth = given_args.first.to_s
140
142
 
@@ -154,7 +156,7 @@ class Thor
154
156
  args, opts = given_args, {}
155
157
  end
156
158
 
157
- task ||= Thor::Task::Dynamic.new(meth)
159
+ task ||= Thor::DynamicTask.new(meth)
158
160
  trailing = args[Range.new(arguments.size, -1)]
159
161
  new(args, opts, config).invoke(task, trailing || [])
160
162
  end
@@ -188,8 +190,8 @@ class Thor
188
190
  # ==== Parameters
189
191
  # shell<Thor::Shell>
190
192
  #
191
- def help(shell)
192
- list = printable_tasks
193
+ def help(shell, subcommand = false)
194
+ list = printable_tasks(true, subcommand)
193
195
  Thor::Util.thor_classes_in(self).each do |klass|
194
196
  list += klass.printable_tasks(false)
195
197
  end
@@ -202,17 +204,25 @@ class Thor
202
204
  end
203
205
 
204
206
  # Returns tasks ready to be printed.
205
- def printable_tasks(all=true)
207
+ def printable_tasks(all = true, subcommand = false)
206
208
  (all ? all_tasks : tasks).map do |_, task|
209
+ next if task.hidden?
207
210
  item = []
208
- item << banner(task)
211
+ item << banner(task, false, subcommand)
209
212
  item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
210
213
  item
211
- end
214
+ end.compact
212
215
  end
213
216
 
214
- def handle_argument_error(task, error) #:nodoc:
215
- raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{task.formatted_usage(self, banner_base == "thor").inspect}."
217
+ def subcommands
218
+ @@subcommands ||= {}
219
+ end
220
+
221
+ def subcommand(subcommand, subcommand_class)
222
+ subcommand = subcommand.to_s
223
+ subcommands[subcommand] = subcommand_class
224
+ subcommand_class.subcommand_help subcommand
225
+ define_method(subcommand) { |*_| subcommand_class.start(subcommand_args) }
216
226
  end
217
227
 
218
228
  protected
@@ -222,8 +232,8 @@ class Thor
222
232
  # the task that is going to be invoked and a boolean which indicates if
223
233
  # the namespace should be displayed as arguments.
224
234
  #
225
- def banner(task)
226
- "#{banner_base} #{task.formatted_usage(self, banner_base == "thor")}"
235
+ def banner(task, namespace = nil, subcommand = false)
236
+ "#{$0} #{task.formatted_usage(self, $thor_runner, subcommand)}"
227
237
  end
228
238
 
229
239
  def baseclass #:nodoc:
@@ -232,8 +242,9 @@ class Thor
232
242
 
233
243
  def create_task(meth) #:nodoc:
234
244
  if @usage && @desc
235
- tasks[meth.to_s] = Thor::Task.new(meth, @desc, @long_desc, @usage, method_options)
236
- @usage, @desc, @long_desc, @method_options = nil
245
+ base_class = @hide ? Thor::HiddenTask : Thor::Task
246
+ tasks[meth.to_s] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
247
+ @usage, @desc, @long_desc, @method_options, @hide = nil
237
248
  true
238
249
  elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing
239
250
  true
@@ -254,10 +265,21 @@ class Thor
254
265
  # If a map can't be found use the sent name or the default task.
255
266
  #
256
267
  def normalize_task_name(meth) #:nodoc:
257
- mapping = map[meth.to_s]
258
- meth = mapping || meth || default_task
268
+ meth = map[meth.to_s] || meth || default_task
259
269
  meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
260
270
  end
271
+
272
+ def subcommand_help(cmd)
273
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
274
+ class_eval <<-RUBY
275
+ def help(task = nil, subcommand = true); super; end
276
+ RUBY
277
+ end
278
+
279
+ end
280
+
281
+ def subcommand_args
282
+ @@original_args[1..-1]
261
283
  end
262
284
 
263
285
  include Thor::Base
@@ -265,7 +287,7 @@ class Thor
265
287
  map HELP_MAPPINGS => :help
266
288
 
267
289
  desc "help [TASK]", "Describe available tasks or one specific task"
268
- def help(task=nil)
269
- task ? self.class.task_help(shell, task) : self.class.help(shell)
290
+ def help(task = nil, subcommand = false)
291
+ task ? self.class.task_help(shell, task) : self.class.help(shell, subcommand)
270
292
  end
271
293
  end
@@ -336,6 +336,7 @@ class Thor
336
336
  def no_tasks
337
337
  @no_tasks = true
338
338
  yield
339
+ ensure
339
340
  @no_tasks = false
340
341
  end
341
342
 
@@ -382,13 +383,17 @@ class Thor
382
383
  end
383
384
 
384
385
  def handle_no_task_error(task) #:nodoc:
385
- if self.banner_base == "thor"
386
+ if $thor_runner
386
387
  raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
387
388
  else
388
389
  raise UndefinedTaskError, "Could not find task #{task.inspect}."
389
390
  end
390
391
  end
391
392
 
393
+ def handle_argument_error(task, error) #:nodoc:
394
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{self.banner(task).inspect}."
395
+ end
396
+
392
397
  protected
393
398
 
394
399
  # Prints the class options per group. If an option does not belong to
@@ -445,7 +450,7 @@ class Thor
445
450
  def build_option(name, options, scope) #:nodoc:
446
451
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
447
452
  options[:type], options[:default], options[:banner],
448
- options[:group], options[:aliases])
453
+ options[:lazy_default], options[:group], options[:aliases])
449
454
  end
450
455
 
451
456
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -516,11 +521,6 @@ class Thor
516
521
  false
517
522
  end
518
523
 
519
- # Returns the base for banner.
520
- def banner_base
521
- @banner_base ||= $thor_runner ? "thor" : File.basename($0.split(" ").first)
522
- end
523
-
524
524
  # SIGNATURE: Sets the baseclass. This is where the superclass lookup
525
525
  # finishes.
526
526
  def baseclass #:nodoc:
@@ -156,7 +156,7 @@ class Thor
156
156
  raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
157
157
 
158
158
  task ||= klass.default_task if klass.respond_to?(:default_task)
159
- task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task)
159
+ task = klass.all_tasks[task.to_s] || Thor::DynamicTask.new(task) if task && !task.is_a?(Thor::Task)
160
160
  task
161
161
  end
162
162
 
@@ -31,10 +31,10 @@ class Thor
31
31
 
32
32
  def show_default?
33
33
  case default
34
- when Array, String, Hash
35
- !default.empty?
36
- else
37
- default
34
+ when Array, String, Hash
35
+ !default.empty?
36
+ else
37
+ default
38
38
  end
39
39
  end
40
40
 
@@ -45,21 +45,21 @@ class Thor
45
45
  end
46
46
 
47
47
  def valid_type?(type)
48
- VALID_TYPES.include?(type.to_sym)
48
+ self.class::VALID_TYPES.include?(type.to_sym)
49
49
  end
50
50
 
51
51
  def default_banner
52
52
  case type
53
- when :boolean
54
- nil
55
- when :string, :default
56
- human_name.upcase
57
- when :numeric
58
- "N"
59
- when :hash
60
- "key:value"
61
- when :array
62
- "one two three"
53
+ when :boolean
54
+ nil
55
+ when :string, :default
56
+ human_name.upcase
57
+ when :numeric
58
+ "N"
59
+ when :hash
60
+ "key:value"
61
+ when :array
62
+ "one two three"
63
63
  end
64
64
  end
65
65
 
@@ -1,13 +1,14 @@
1
1
  class Thor
2
2
  class Option < Argument #:nodoc:
3
- attr_reader :aliases, :group
3
+ attr_reader :aliases, :group, :lazy_default
4
4
 
5
5
  VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
6
6
 
7
- def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil)
7
+ def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, lazy_default=nil, group=nil, aliases=nil)
8
8
  super(name, description, required, type, default, banner)
9
- @aliases = [*aliases].compact
10
- @group = group.to_s.capitalize if group
9
+ @lazy_default = lazy_default
10
+ @group = group.to_s.capitalize if group
11
+ @aliases = [*aliases].compact
11
12
  end
12
13
 
13
14
  # This parse quick options given as method_options. It makes several
@@ -48,23 +49,22 @@ class Thor
48
49
  default = value
49
50
 
50
51
  type = case value
51
- when Symbol
52
- default = nil
53
-
54
- if VALID_TYPES.include?(value)
55
- value
56
- elsif required = (value == :required)
57
- :string
58
- end
59
- when TrueClass, FalseClass
60
- :boolean
61
- when Numeric
62
- :numeric
63
- when Hash, Array, String
64
- value.class.name.downcase.to_sym
52
+ when Symbol
53
+ default = nil
54
+ if VALID_TYPES.include?(value)
55
+ value
56
+ elsif required = (value == :required)
57
+ :string
58
+ end
59
+ when TrueClass, FalseClass
60
+ :boolean
61
+ when Numeric
62
+ :numeric
63
+ when Hash, Array, String
64
+ value.class.name.downcase.to_sym
65
65
  end
66
66
 
67
- self.new(name.to_s, nil, required, type, default, nil, nil, aliases)
67
+ self.new(name.to_s, nil, required, type, default, nil, nil, nil, aliases)
68
68
  end
69
69
 
70
70
  def switch_name
@@ -91,38 +91,30 @@ class Thor
91
91
  end
92
92
  end
93
93
 
94
- # Allow some type predicates as: boolean?, string? and etc.
95
- #
96
- def method_missing(method, *args, &block)
97
- given = method.to_s.sub(/\?$/, '').to_sym
98
- if valid_type?(given)
99
- self.type == given
100
- else
101
- super
102
- end
94
+ VALID_TYPES.each do |type|
95
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
96
+ def #{type}?
97
+ self.type == #{type.inspect}
98
+ end
99
+ RUBY
103
100
  end
104
101
 
105
- protected
106
-
107
- def validate!
108
- raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
109
- end
110
-
111
- def valid_type?(type)
112
- VALID_TYPES.include?(type.to_sym)
113
- end
102
+ protected
114
103
 
115
- def dasherized?
116
- name.index('-') == 0
117
- end
104
+ def validate!
105
+ raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
106
+ end
118
107
 
119
- def undasherize(str)
120
- str.sub(/^-{1,2}/, '')
121
- end
108
+ def dasherized?
109
+ name.index('-') == 0
110
+ end
122
111
 
123
- def dasherize(str)
124
- (str.length > 1 ? "--" : "-") + str.gsub('_', '-')
125
- end
112
+ def undasherize(str)
113
+ str.sub(/^-{1,2}/, '')
114
+ end
126
115
 
116
+ def dasherize(str)
117
+ (str.length > 1 ? "--" : "-") + str.gsub('_', '-')
118
+ end
127
119
  end
128
120
  end