bundler 0.8.1 → 0.9.0.pre1

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.

Files changed (58) hide show
  1. data/README +7 -0
  2. data/bin/bundle +3 -0
  3. data/lib/bundler.rb +72 -37
  4. data/lib/bundler/cli.rb +64 -68
  5. data/lib/bundler/definition.rb +78 -0
  6. data/lib/bundler/dependency.rb +7 -57
  7. data/lib/bundler/dsl.rb +42 -142
  8. data/lib/bundler/environment.rb +94 -54
  9. data/lib/bundler/index.rb +98 -0
  10. data/lib/bundler/installer.rb +137 -0
  11. data/lib/bundler/remote_specification.rb +1 -1
  12. data/lib/bundler/resolver.rb +20 -50
  13. data/lib/bundler/rubygems.rb +22 -0
  14. data/lib/bundler/source.rb +185 -295
  15. data/lib/bundler/specification.rb +22 -0
  16. data/lib/bundler/templates/Gemfile +4 -0
  17. data/lib/bundler/templates/environment.erb +3 -153
  18. data/lib/bundler/ui.rb +51 -0
  19. data/lib/bundler/vendor/thor.rb +241 -0
  20. data/lib/bundler/vendor/thor/actions.rb +274 -0
  21. data/lib/bundler/vendor/thor/actions/create_file.rb +103 -0
  22. data/lib/bundler/vendor/thor/actions/directory.rb +91 -0
  23. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  24. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +223 -0
  25. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +101 -0
  26. data/lib/bundler/vendor/thor/base.rb +515 -0
  27. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  28. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  29. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  30. data/lib/bundler/vendor/thor/error.rb +27 -0
  31. data/lib/bundler/vendor/thor/group.rb +267 -0
  32. data/lib/bundler/vendor/thor/invocation.rb +178 -0
  33. data/lib/bundler/vendor/thor/parser.rb +4 -0
  34. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  35. data/lib/bundler/vendor/thor/parser/arguments.rb +145 -0
  36. data/lib/bundler/vendor/thor/parser/option.rb +132 -0
  37. data/lib/bundler/vendor/thor/parser/options.rb +142 -0
  38. data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
  39. data/lib/bundler/vendor/thor/runner.rb +303 -0
  40. data/lib/bundler/vendor/thor/shell.rb +78 -0
  41. data/lib/bundler/vendor/thor/shell/basic.rb +239 -0
  42. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  43. data/lib/bundler/vendor/thor/task.rb +111 -0
  44. data/lib/bundler/vendor/thor/util.rb +233 -0
  45. data/lib/bundler/vendor/thor/version.rb +3 -0
  46. metadata +48 -26
  47. data/README.markdown +0 -284
  48. data/Rakefile +0 -81
  49. data/lib/bundler/bundle.rb +0 -314
  50. data/lib/bundler/commands/bundle_command.rb +0 -72
  51. data/lib/bundler/commands/exec_command.rb +0 -36
  52. data/lib/bundler/finder.rb +0 -51
  53. data/lib/bundler/gem_bundle.rb +0 -11
  54. data/lib/bundler/gem_ext.rb +0 -34
  55. data/lib/bundler/runtime.rb +0 -2
  56. data/lib/bundler/templates/app_script.erb +0 -3
  57. data/lib/bundler/templates/environment_picker.erb +0 -4
  58. data/lib/rubygems_plugin.rb +0 -6
@@ -0,0 +1,142 @@
1
+ class Thor
2
+ # This is a modified version of Daniel Berger's Getopt::Long class, licensed
3
+ # under Ruby's license.
4
+ #
5
+ class Options < Arguments #:nodoc:
6
+ LONG_RE = /^(--\w+[-\w+]*)$/
7
+ SHORT_RE = /^(-[a-z])$/i
8
+ EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i
9
+ SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
10
+ SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
11
+
12
+ # Receives a hash and makes it switches.
13
+ #
14
+ def self.to_switches(options)
15
+ options.map do |key, value|
16
+ case value
17
+ when true
18
+ "--#{key}"
19
+ when Array
20
+ "--#{key} #{value.map{ |v| v.inspect }.join(' ')}"
21
+ when Hash
22
+ "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}"
23
+ when nil, false
24
+ ""
25
+ else
26
+ "--#{key} #{value.inspect}"
27
+ end
28
+ end.join(" ")
29
+ end
30
+
31
+ # Takes a hash of Thor::Option objects.
32
+ #
33
+ def initialize(options={})
34
+ options = options.values
35
+ super(options)
36
+ @shorts, @switches = {}, {}
37
+
38
+ options.each do |option|
39
+ @switches[option.switch_name] = option
40
+
41
+ option.aliases.each do |short|
42
+ @shorts[short.to_s] ||= option.switch_name
43
+ end
44
+ end
45
+ end
46
+
47
+ def parse(args)
48
+ @pile = args.dup
49
+
50
+ while peek
51
+ if current_is_switch?
52
+ case shift
53
+ when SHORT_SQ_RE
54
+ unshift($1.split('').map { |f| "-#{f}" })
55
+ next
56
+ when EQ_RE, SHORT_NUM
57
+ unshift($2)
58
+ switch = $1
59
+ when LONG_RE, SHORT_RE
60
+ switch = $1
61
+ end
62
+
63
+ switch = normalize_switch(switch)
64
+ next unless option = switch_option(switch)
65
+
66
+ @assigns[option.human_name] = parse_peek(switch, option)
67
+ else
68
+ shift
69
+ end
70
+ end
71
+
72
+ check_requirement!
73
+ @assigns
74
+ end
75
+
76
+ protected
77
+
78
+ # Returns true if the current value in peek is a registered switch.
79
+ #
80
+ def current_is_switch?
81
+ case peek
82
+ when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
83
+ switch?($1)
84
+ when SHORT_SQ_RE
85
+ $1.split('').any? { |f| switch?("-#{f}") }
86
+ end
87
+ end
88
+
89
+ def switch?(arg)
90
+ switch_option(arg) || @shorts.key?(arg)
91
+ end
92
+
93
+ def switch_option(arg)
94
+ if match = no_or_skip?(arg)
95
+ @switches[arg] || @switches["--#{match}"]
96
+ else
97
+ @switches[arg]
98
+ end
99
+ end
100
+
101
+ def no_or_skip?(arg)
102
+ arg =~ /^--(no|skip)-([-\w]+)$/
103
+ $2
104
+ end
105
+
106
+ # Check if the given argument is actually a shortcut.
107
+ #
108
+ def normalize_switch(arg)
109
+ @shorts.key?(arg) ? @shorts[arg] : arg
110
+ end
111
+
112
+ # Parse boolean values which can be given as --foo=true, --foo or --no-foo.
113
+ #
114
+ def parse_boolean(switch)
115
+ if current_is_value?
116
+ ["true", "TRUE", "t", "T", true].include?(shift)
117
+ else
118
+ @switches.key?(switch) || !no_or_skip?(switch)
119
+ end
120
+ end
121
+
122
+ # Parse the value at the peek analyzing if it requires an input or not.
123
+ #
124
+ def parse_peek(switch, option)
125
+ unless current_is_value?
126
+ if option.boolean?
127
+ # No problem for boolean types
128
+ elsif no_or_skip?(switch)
129
+ return nil # User set value to nil
130
+ elsif option.string? && !option.required?
131
+ return option.human_name # Return the option name
132
+ else
133
+ raise MalformattedArgumentError, "no value provided for option '#{switch}'"
134
+ end
135
+ end
136
+
137
+ @non_assigned_required.delete(option)
138
+ send(:"parse_#{option.type}", switch)
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,66 @@
1
+ require 'rake'
2
+
3
+ class Thor
4
+ # Adds a compatibility layer to your Thor classes which allows you to use
5
+ # rake package tasks. For example, to use rspec rake tasks, one can do:
6
+ #
7
+ # require 'thor/rake_compat'
8
+ #
9
+ # class Default < Thor
10
+ # include Thor::RakeCompat
11
+ #
12
+ # Spec::Rake::SpecTask.new(:spec) do |t|
13
+ # t.spec_opts = ['--options', "spec/spec.opts"]
14
+ # t.spec_files = FileList['spec/**/*_spec.rb']
15
+ # end
16
+ # end
17
+ #
18
+ module RakeCompat
19
+ def self.rake_classes
20
+ @rake_classes ||= []
21
+ end
22
+
23
+ def self.included(base)
24
+ # Hack. Make rakefile point to invoker, so rdoc task is generated properly.
25
+ rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
26
+ Rake.application.instance_variable_set(:@rakefile, rakefile)
27
+ self.rake_classes << base
28
+ end
29
+ end
30
+ end
31
+
32
+ class Object #:nodoc:
33
+ alias :rake_task :task
34
+ alias :rake_namespace :namespace
35
+
36
+ def task(*args, &block)
37
+ task = rake_task(*args, &block)
38
+
39
+ if klass = Thor::RakeCompat.rake_classes.last
40
+ non_namespaced_name = task.name.split(':').last
41
+
42
+ description = non_namespaced_name
43
+ description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ')
44
+ description.strip!
45
+
46
+ klass.desc description, task.comment || non_namespaced_name
47
+ klass.send :define_method, non_namespaced_name do |*args|
48
+ Rake::Task[task.name.to_sym].invoke(*args)
49
+ end
50
+ end
51
+
52
+ task
53
+ end
54
+
55
+ def namespace(name, &block)
56
+ if klass = Thor::RakeCompat.rake_classes.last
57
+ const_name = Thor::Util.camel_case(name.to_s).to_sym
58
+ klass.const_set(const_name, Class.new(Thor))
59
+ new_klass = klass.const_get(const_name)
60
+ Thor::RakeCompat.rake_classes << new_klass
61
+ end
62
+
63
+ rake_namespace(name, &block)
64
+ Thor::RakeCompat.rake_classes.pop
65
+ end
66
+ end
@@ -0,0 +1,303 @@
1
+ require 'fileutils'
2
+ require 'open-uri'
3
+ require 'yaml'
4
+ require 'digest/md5'
5
+ require 'pathname'
6
+
7
+ class Thor::Runner < Thor #:nodoc:
8
+ map "-T" => :list, "-i" => :install, "-u" => :update
9
+
10
+ # Override Thor#help so it can give information about any class and any method.
11
+ #
12
+ def help(meth=nil)
13
+ if meth && !self.respond_to?(meth)
14
+ initialize_thorfiles(meth)
15
+ klass, task = Thor::Util.namespace_to_thor_class_and_task(meth)
16
+ # Send mapping -h because it works with Thor::Group too
17
+ klass.start(["-h", task].compact, :shell => self.shell)
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ # If a task is not found on Thor::Runner, method missing is invoked and
24
+ # Thor::Runner is then responsable for finding the task in all classes.
25
+ #
26
+ def method_missing(meth, *args)
27
+ meth = meth.to_s
28
+ initialize_thorfiles(meth)
29
+ klass, task = Thor::Util.namespace_to_thor_class_and_task(meth)
30
+ args.unshift(task) if task
31
+ klass.start(args, :shell => shell)
32
+ end
33
+
34
+ desc "install NAME", "Install an optionally named Thor file into your system tasks"
35
+ method_options :as => :string, :relative => :boolean
36
+ def install(name)
37
+ initialize_thorfiles
38
+
39
+ # If a directory name is provided as the argument, look for a 'main.thor'
40
+ # task in said directory.
41
+ begin
42
+ if File.directory?(File.expand_path(name))
43
+ base, package = File.join(name, "main.thor"), :directory
44
+ contents = open(base).read
45
+ else
46
+ base, package = name, :file
47
+ contents = open(name).read
48
+ end
49
+ rescue OpenURI::HTTPError
50
+ raise Error, "Error opening URI '#{name}'"
51
+ rescue Errno::ENOENT
52
+ raise Error, "Error opening file '#{name}'"
53
+ end
54
+
55
+ say "Your Thorfile contains:"
56
+ say contents
57
+
58
+ return false if no?("Do you wish to continue [y/N]?")
59
+
60
+ as = options["as"] || begin
61
+ first_line = contents.split("\n")[0]
62
+ (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil
63
+ end
64
+
65
+ unless as
66
+ basename = File.basename(name)
67
+ as = ask("Please specify a name for #{name} in the system repository [#{basename}]:")
68
+ as = basename if as.empty?
69
+ end
70
+
71
+ location = if options[:relative] || name =~ /^http:\/\//
72
+ name
73
+ else
74
+ File.expand_path(name)
75
+ end
76
+
77
+ thor_yaml[as] = {
78
+ :filename => Digest::MD5.hexdigest(name + as),
79
+ :location => location,
80
+ :namespaces => Thor::Util.namespaces_in_content(contents, base)
81
+ }
82
+
83
+ save_yaml(thor_yaml)
84
+ say "Storing thor file in your system repository"
85
+ destination = File.join(thor_root, thor_yaml[as][:filename])
86
+
87
+ if package == :file
88
+ File.open(destination, "w") { |f| f.puts contents }
89
+ else
90
+ FileUtils.cp_r(name, destination)
91
+ end
92
+
93
+ thor_yaml[as][:filename] # Indicate success
94
+ end
95
+
96
+ desc "uninstall NAME", "Uninstall a named Thor module"
97
+ def uninstall(name)
98
+ raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
99
+ say "Uninstalling #{name}."
100
+ FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}"))
101
+
102
+ thor_yaml.delete(name)
103
+ save_yaml(thor_yaml)
104
+
105
+ puts "Done."
106
+ end
107
+
108
+ desc "update NAME", "Update a Thor file from its original location"
109
+ def update(name)
110
+ raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
111
+
112
+ say "Updating '#{name}' from #{thor_yaml[name][:location]}"
113
+
114
+ old_filename = thor_yaml[name][:filename]
115
+ self.options = self.options.merge("as" => name)
116
+ filename = install(thor_yaml[name][:location])
117
+
118
+ unless filename == old_filename
119
+ File.delete(File.join(thor_root, old_filename))
120
+ end
121
+ end
122
+
123
+ desc "installed", "List the installed Thor modules and tasks"
124
+ method_options :internal => :boolean
125
+ def installed
126
+ initialize_thorfiles(nil, true)
127
+ display_klasses(true, options["internal"])
128
+ end
129
+
130
+ desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)"
131
+ method_options :substring => :boolean, :group => :string, :all => :boolean
132
+ def list(search="")
133
+ initialize_thorfiles
134
+
135
+ search = ".*#{search}" if options["substring"]
136
+ search = /^#{search}.*/i
137
+ group = options[:group] || "standard"
138
+
139
+ klasses = Thor::Base.subclasses.select do |k|
140
+ (options[:all] || k.group == group) && k.namespace =~ search
141
+ end
142
+
143
+ display_klasses(false, false, klasses)
144
+ end
145
+
146
+ private
147
+
148
+ def self.banner(task)
149
+ "thor " + task.formatted_usage(self, false)
150
+ end
151
+
152
+ def thor_root
153
+ Thor::Util.thor_root
154
+ end
155
+
156
+ def thor_yaml
157
+ @thor_yaml ||= begin
158
+ yaml_file = File.join(thor_root, "thor.yml")
159
+ yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
160
+ yaml || {}
161
+ end
162
+ end
163
+
164
+ # Save the yaml file. If none exists in thor root, creates one.
165
+ #
166
+ def save_yaml(yaml)
167
+ yaml_file = File.join(thor_root, "thor.yml")
168
+
169
+ unless File.exists?(yaml_file)
170
+ FileUtils.mkdir_p(thor_root)
171
+ yaml_file = File.join(thor_root, "thor.yml")
172
+ FileUtils.touch(yaml_file)
173
+ end
174
+
175
+ File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
176
+ end
177
+
178
+ def self.exit_on_failure?
179
+ true
180
+ end
181
+
182
+ # Load the thorfiles. If relevant_to is supplied, looks for specific files
183
+ # in the thor_root instead of loading them all.
184
+ #
185
+ # By default, it also traverses the current path until find Thor files, as
186
+ # described in thorfiles. This look up can be skipped by suppliying
187
+ # skip_lookup true.
188
+ #
189
+ def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
190
+ thorfiles(relevant_to, skip_lookup).each do |f|
191
+ Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f))
192
+ end
193
+ end
194
+
195
+ # Finds Thorfiles by traversing from your current directory down to the root
196
+ # directory of your system. If at any time we find a Thor file, we stop.
197
+ #
198
+ # We also ensure that system-wide Thorfiles are loaded first, so local
199
+ # Thorfiles can override them.
200
+ #
201
+ # ==== Example
202
+ #
203
+ # If we start at /Users/wycats/dev/thor ...
204
+ #
205
+ # 1. /Users/wycats/dev/thor
206
+ # 2. /Users/wycats/dev
207
+ # 3. /Users/wycats <-- we find a Thorfile here, so we stop
208
+ #
209
+ # Suppose we start at c:\Documents and Settings\james\dev\thor ...
210
+ #
211
+ # 1. c:\Documents and Settings\james\dev\thor
212
+ # 2. c:\Documents and Settings\james\dev
213
+ # 3. c:\Documents and Settings\james
214
+ # 4. c:\Documents and Settings
215
+ # 5. c:\ <-- no Thorfiles found!
216
+ #
217
+ def thorfiles(relevant_to=nil, skip_lookup=false)
218
+ thorfiles = []
219
+
220
+ unless skip_lookup
221
+ Pathname.pwd.ascend do |path|
222
+ thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
223
+ break unless thorfiles.empty?
224
+ end
225
+ end
226
+
227
+ files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob)
228
+ files += thorfiles
229
+ files -= ["#{thor_root}/thor.yml"]
230
+
231
+ files.map! do |file|
232
+ File.directory?(file) ? File.join(file, "main.thor") : file
233
+ end
234
+ end
235
+
236
+ # Load thorfiles relevant to the given method. If you provide "foo:bar" it
237
+ # will load all thor files in the thor.yaml that has "foo" e "foo:bar"
238
+ # namespaces registered.
239
+ #
240
+ def thorfiles_relevant_to(meth)
241
+ lookup = [ meth, meth.split(":")[0...-1].join(":") ]
242
+
243
+ files = thor_yaml.select do |k, v|
244
+ v[:namespaces] && !(v[:namespaces] & lookup).empty?
245
+ end
246
+
247
+ files.map { |k, v| File.join(thor_root, "#{v[:filename]}") }
248
+ end
249
+
250
+ # Display information about the given klasses. If with_module is given,
251
+ # it shows a table with information extracted from the yaml file.
252
+ #
253
+ def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
254
+ klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
255
+
256
+ raise Error, "No Thor tasks available" if klasses.empty?
257
+ show_modules if with_modules && !thor_yaml.empty?
258
+
259
+ # Remove subclasses
260
+ klasses.dup.each do |klass|
261
+ klasses -= Thor::Util.thor_classes_in(klass)
262
+ end
263
+
264
+ list = Hash.new { |h,k| h[k] = [] }
265
+ groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
266
+
267
+ # Get classes which inherit from Thor
268
+ (klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
269
+
270
+ # Get classes which inherit from Thor::Base
271
+ groups.map! { |k| k.printable_tasks(false).first }
272
+ list["root"] = groups
273
+
274
+ # Order namespaces with default coming first
275
+ list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
276
+ list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
277
+ end
278
+
279
+ def display_tasks(namespace, list) #:nodoc:
280
+ list.sort!{ |a,b| a[0] <=> b[0] }
281
+
282
+ say shell.set_color(namespace, :blue, true)
283
+ say "-" * namespace.size
284
+
285
+ print_table(list, :truncate => true)
286
+ say
287
+ end
288
+
289
+ def show_modules #:nodoc:
290
+ info = []
291
+ labels = ["Modules", "Namespaces"]
292
+
293
+ info << labels
294
+ info << [ "-" * labels[0].size, "-" * labels[1].size ]
295
+
296
+ thor_yaml.each do |name, hash|
297
+ info << [ name, hash[:namespaces].join(", ") ]
298
+ end
299
+
300
+ print_table info
301
+ say ""
302
+ end
303
+ end