bundler-maglev- 1.0.21

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.
Files changed (144) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +32 -0
  3. data/CHANGELOG.md +805 -0
  4. data/ISSUES.md +62 -0
  5. data/LICENSE +23 -0
  6. data/README.md +29 -0
  7. data/Rakefile +212 -0
  8. data/UPGRADING.md +103 -0
  9. data/bin/bundle +22 -0
  10. data/bundler.gemspec +30 -0
  11. data/lib/bundler.rb +286 -0
  12. data/lib/bundler/capistrano.rb +11 -0
  13. data/lib/bundler/cli.rb +520 -0
  14. data/lib/bundler/definition.rb +438 -0
  15. data/lib/bundler/dependency.rb +134 -0
  16. data/lib/bundler/deployment.rb +58 -0
  17. data/lib/bundler/dsl.rb +257 -0
  18. data/lib/bundler/environment.rb +47 -0
  19. data/lib/bundler/gem_helper.rb +151 -0
  20. data/lib/bundler/gem_installer.rb +9 -0
  21. data/lib/bundler/gem_tasks.rb +2 -0
  22. data/lib/bundler/graph.rb +130 -0
  23. data/lib/bundler/index.rb +138 -0
  24. data/lib/bundler/installer.rb +97 -0
  25. data/lib/bundler/lazy_specification.rb +74 -0
  26. data/lib/bundler/lockfile_parser.rb +108 -0
  27. data/lib/bundler/remote_specification.rb +59 -0
  28. data/lib/bundler/resolver.rb +464 -0
  29. data/lib/bundler/rubygems_ext.rb +237 -0
  30. data/lib/bundler/rubygems_integration.rb +349 -0
  31. data/lib/bundler/runtime.rb +152 -0
  32. data/lib/bundler/settings.rb +115 -0
  33. data/lib/bundler/setup.rb +23 -0
  34. data/lib/bundler/shared_helpers.rb +71 -0
  35. data/lib/bundler/source.rb +708 -0
  36. data/lib/bundler/spec_set.rb +135 -0
  37. data/lib/bundler/templates/Executable +16 -0
  38. data/lib/bundler/templates/Gemfile +4 -0
  39. data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
  40. data/lib/bundler/templates/newgem/Rakefile.tt +1 -0
  41. data/lib/bundler/templates/newgem/bin/newgem.tt +3 -0
  42. data/lib/bundler/templates/newgem/gitignore.tt +4 -0
  43. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +9 -0
  44. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
  45. data/lib/bundler/templates/newgem/newgem.gemspec.tt +24 -0
  46. data/lib/bundler/ui.rb +73 -0
  47. data/lib/bundler/vendor/thor.rb +358 -0
  48. data/lib/bundler/vendor/thor/actions.rb +314 -0
  49. data/lib/bundler/vendor/thor/actions/create_file.rb +105 -0
  50. data/lib/bundler/vendor/thor/actions/create_link.rb +57 -0
  51. data/lib/bundler/vendor/thor/actions/directory.rb +93 -0
  52. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  53. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +270 -0
  54. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +109 -0
  55. data/lib/bundler/vendor/thor/base.rb +576 -0
  56. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  57. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  58. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  59. data/lib/bundler/vendor/thor/error.rb +30 -0
  60. data/lib/bundler/vendor/thor/group.rb +273 -0
  61. data/lib/bundler/vendor/thor/invocation.rb +168 -0
  62. data/lib/bundler/vendor/thor/parser.rb +4 -0
  63. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  64. data/lib/bundler/vendor/thor/parser/arguments.rb +161 -0
  65. data/lib/bundler/vendor/thor/parser/option.rb +120 -0
  66. data/lib/bundler/vendor/thor/parser/options.rb +175 -0
  67. data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
  68. data/lib/bundler/vendor/thor/runner.rb +309 -0
  69. data/lib/bundler/vendor/thor/shell.rb +88 -0
  70. data/lib/bundler/vendor/thor/shell/basic.rb +302 -0
  71. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  72. data/lib/bundler/vendor/thor/shell/html.rb +121 -0
  73. data/lib/bundler/vendor/thor/task.rb +113 -0
  74. data/lib/bundler/vendor/thor/util.rb +229 -0
  75. data/lib/bundler/vendor/thor/version.rb +3 -0
  76. data/lib/bundler/vendored_thor.rb +7 -0
  77. data/lib/bundler/version.rb +6 -0
  78. data/lib/bundler/vlad.rb +11 -0
  79. data/man/bundle-config.ronn +90 -0
  80. data/man/bundle-exec.ronn +111 -0
  81. data/man/bundle-install.ronn +317 -0
  82. data/man/bundle-package.ronn +59 -0
  83. data/man/bundle-update.ronn +176 -0
  84. data/man/bundle.ronn +80 -0
  85. data/man/gemfile.5.ronn +284 -0
  86. data/man/index.txt +6 -0
  87. data/spec/bundler/gem_helper_spec.rb +143 -0
  88. data/spec/cache/gems_spec.rb +230 -0
  89. data/spec/cache/git_spec.rb +12 -0
  90. data/spec/cache/path_spec.rb +27 -0
  91. data/spec/cache/platform_spec.rb +57 -0
  92. data/spec/install/deploy_spec.rb +197 -0
  93. data/spec/install/deprecated_spec.rb +37 -0
  94. data/spec/install/gems/c_ext_spec.rb +48 -0
  95. data/spec/install/gems/env_spec.rb +107 -0
  96. data/spec/install/gems/flex_spec.rb +313 -0
  97. data/spec/install/gems/groups_spec.rb +259 -0
  98. data/spec/install/gems/packed_spec.rb +84 -0
  99. data/spec/install/gems/platform_spec.rb +192 -0
  100. data/spec/install/gems/resolving_spec.rb +72 -0
  101. data/spec/install/gems/simple_case_spec.rb +770 -0
  102. data/spec/install/gems/sudo_spec.rb +74 -0
  103. data/spec/install/gems/win32_spec.rb +26 -0
  104. data/spec/install/gemspec_spec.rb +125 -0
  105. data/spec/install/git_spec.rb +570 -0
  106. data/spec/install/invalid_spec.rb +35 -0
  107. data/spec/install/path_spec.rb +405 -0
  108. data/spec/install/upgrade_spec.rb +26 -0
  109. data/spec/lock/git_spec.rb +35 -0
  110. data/spec/lock/lockfile_spec.rb +739 -0
  111. data/spec/other/check_spec.rb +221 -0
  112. data/spec/other/config_spec.rb +40 -0
  113. data/spec/other/console_spec.rb +54 -0
  114. data/spec/other/exec_spec.rb +248 -0
  115. data/spec/other/ext_spec.rb +37 -0
  116. data/spec/other/help_spec.rb +39 -0
  117. data/spec/other/init_spec.rb +40 -0
  118. data/spec/other/newgem_spec.rb +46 -0
  119. data/spec/other/open_spec.rb +35 -0
  120. data/spec/other/show_spec.rb +82 -0
  121. data/spec/quality_spec.rb +62 -0
  122. data/spec/resolver/basic_spec.rb +20 -0
  123. data/spec/resolver/platform_spec.rb +82 -0
  124. data/spec/runtime/executable_spec.rb +110 -0
  125. data/spec/runtime/load_spec.rb +107 -0
  126. data/spec/runtime/platform_spec.rb +90 -0
  127. data/spec/runtime/require_spec.rb +231 -0
  128. data/spec/runtime/setup_spec.rb +730 -0
  129. data/spec/runtime/with_clean_env_spec.rb +15 -0
  130. data/spec/spec_helper.rb +92 -0
  131. data/spec/support/builders.rb +597 -0
  132. data/spec/support/helpers.rb +239 -0
  133. data/spec/support/indexes.rb +112 -0
  134. data/spec/support/matchers.rb +77 -0
  135. data/spec/support/path.rb +71 -0
  136. data/spec/support/platforms.rb +53 -0
  137. data/spec/support/ruby_ext.rb +20 -0
  138. data/spec/support/rubygems_ext.rb +37 -0
  139. data/spec/support/rubygems_hax/platform.rb +11 -0
  140. data/spec/support/sudo.rb +21 -0
  141. data/spec/update/gems_spec.rb +122 -0
  142. data/spec/update/git_spec.rb +196 -0
  143. data/spec/update/source_spec.rb +51 -0
  144. metadata +296 -0
@@ -0,0 +1,135 @@
1
+ require 'tsort'
2
+ require 'forwardable'
3
+
4
+ module Bundler
5
+ class SpecSet
6
+ extend Forwardable
7
+ include TSort, Enumerable
8
+
9
+ def_delegators :@specs, :<<, :length, :add, :remove
10
+ def_delegators :sorted, :each
11
+
12
+ def initialize(specs)
13
+ @specs = specs.sort_by { |s| s.name }
14
+ end
15
+
16
+ def for(dependencies, skip = [], check = false, match_current_platform = false)
17
+ handled, deps, specs = {}, dependencies.dup, []
18
+ skip << 'bundler'
19
+
20
+ until deps.empty?
21
+ dep = deps.shift
22
+ next if handled[dep] || skip.include?(dep.name)
23
+
24
+ spec = lookup[dep.name].find do |s|
25
+ match_current_platform ?
26
+ Gem::Platform.match(s.platform) :
27
+ s.match_platform(dep.__platform)
28
+ end
29
+
30
+ handled[dep] = true
31
+
32
+ if spec
33
+ specs << spec
34
+
35
+ spec.dependencies.each do |d|
36
+ next if d.type == :development
37
+ d = DepProxy.new(d, dep.__platform) unless match_current_platform
38
+ deps << d
39
+ end
40
+ elsif check
41
+ return false
42
+ end
43
+ end
44
+
45
+ if spec = lookup['bundler'].first
46
+ specs << spec
47
+ end
48
+
49
+ check ? true : SpecSet.new(specs)
50
+ end
51
+
52
+ def valid_for?(deps)
53
+ self.for(deps, [], true)
54
+ end
55
+
56
+ def [](key)
57
+ key = key.name if key.respond_to?(:name)
58
+ lookup[key].reverse
59
+ end
60
+
61
+ def []=(key, value)
62
+ @specs << value
63
+ @lookup = nil
64
+ @sorted = nil
65
+ value
66
+ end
67
+
68
+ def sort!
69
+ self
70
+ end
71
+
72
+ def to_a
73
+ sorted.dup
74
+ end
75
+
76
+ def to_hash
77
+ lookup.dup
78
+ end
79
+
80
+ def materialize(deps, missing_specs = nil)
81
+ materialized = self.for(deps, [], false, true).to_a
82
+ materialized.map! do |s|
83
+ next s unless s.is_a?(LazySpecification)
84
+ spec = s.__materialize__
85
+ if missing_specs
86
+ missing_specs << s unless spec
87
+ else
88
+ raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
89
+ end
90
+ spec if spec
91
+ end
92
+ SpecSet.new(materialized.compact)
93
+ end
94
+
95
+ def merge(set)
96
+ arr = sorted.dup
97
+ set.each do |s|
98
+ next if arr.any? { |s2| s2.name == s.name && s2.version == s.version && s2.platform == s.platform }
99
+ arr << s
100
+ end
101
+ SpecSet.new(arr)
102
+ end
103
+
104
+ private
105
+
106
+ def sorted
107
+ rake = @specs.find { |s| s.name == 'rake' }
108
+ @sorted ||= ([rake] + tsort).compact.uniq
109
+ end
110
+
111
+ def lookup
112
+ @lookup ||= begin
113
+ lookup = Hash.new { |h,k| h[k] = [] }
114
+ specs = @specs.sort_by do |s|
115
+ s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s
116
+ end
117
+ specs.reverse_each do |s|
118
+ lookup[s.name] << s
119
+ end
120
+ lookup
121
+ end
122
+ end
123
+
124
+ def tsort_each_node
125
+ @specs.each { |s| yield s }
126
+ end
127
+
128
+ def tsort_each_child(s)
129
+ s.dependencies.sort_by { |d| d.name }.each do |d|
130
+ next if d.type == :development
131
+ lookup[d.name].each { |s2| yield s2 }
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env <%= RbConfig::CONFIG['ruby_install_name'] %>
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application '<%= executable %>' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../<%= relative_gemfile_path %>",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('<%= spec.name %>', '<%= executable %>')
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ # gem "rails"
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in <%=config[:name]%>.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "<%= config[:name] %>"
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
@@ -0,0 +1,9 @@
1
+ require "<%=config[:name]%>/version"
2
+
3
+ <%- config[:constant_array].each_with_index do |c,i| -%>
4
+ <%= ' '*i %>module <%= c %>
5
+ <%- end -%>
6
+ <%= ' '*config[:constant_array].size %># Your code goes here...
7
+ <%- (config[:constant_array].size-1).downto(0) do |i| -%>
8
+ <%= ' '*i %>end
9
+ <%- end -%>
@@ -0,0 +1,7 @@
1
+ <%- config[:constant_array].each_with_index do |c,i| -%>
2
+ <%= ' '*i %>module <%= c %>
3
+ <%- end -%>
4
+ <%= ' '*config[:constant_array].size %>VERSION = "0.0.1"
5
+ <%- (config[:constant_array].size-1).downto(0) do |i| -%>
6
+ <%= ' '*i %>end
7
+ <%- end -%>
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "<%=config[:name]%>/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = <%=config[:name].inspect%>
7
+ s.version = <%=config[:constant_name]%>::VERSION
8
+ s.authors = ["<%= config[:author_name] %>"]
9
+ s.email = ["<%= config[:author_email] %>"]
10
+ s.homepage = ""
11
+ s.summary = %q{TODO: Write a gem summary}
12
+ s.description = %q{TODO: Write a gem description}
13
+
14
+ s.rubyforge_project = <%=config[:name].inspect%>
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
data/lib/bundler/ui.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'rubygems/user_interaction'
2
+
3
+ module Bundler
4
+ class UI
5
+ def warn(message)
6
+ end
7
+
8
+ def debug(message)
9
+ end
10
+
11
+ def error(message)
12
+ end
13
+
14
+ def info(message)
15
+ end
16
+
17
+ def confirm(message)
18
+ end
19
+
20
+ class Shell < UI
21
+ attr_writer :shell
22
+
23
+ def initialize(shell)
24
+ @shell = shell
25
+ @quiet = false
26
+ @debug = ENV['DEBUG']
27
+ end
28
+
29
+ def debug(msg)
30
+ @shell.say(msg) if @debug && !@quiet
31
+ end
32
+
33
+ def info(msg)
34
+ @shell.say(msg) if !@quiet
35
+ end
36
+
37
+ def confirm(msg)
38
+ @shell.say(msg, :green) if !@quiet
39
+ end
40
+
41
+ def warn(msg)
42
+ @shell.say(msg, :yellow)
43
+ end
44
+
45
+ def error(msg)
46
+ @shell.say(msg, :red)
47
+ end
48
+
49
+ def be_quiet!
50
+ @quiet = true
51
+ end
52
+
53
+ def debug!
54
+ @debug = true
55
+ end
56
+ end
57
+
58
+ class RGProxy < ::Gem::SilentUI
59
+ def initialize(ui)
60
+ @ui = ui
61
+ super()
62
+ end
63
+
64
+ def say(message)
65
+ if message =~ /native extensions/
66
+ @ui.info "with native extensions "
67
+ else
68
+ @ui.debug(message)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,358 @@
1
+ require 'thor/base'
2
+
3
+ class Thor
4
+ class << self
5
+ # Sets the default task when thor is executed without an explicit task to be called.
6
+ #
7
+ # ==== Parameters
8
+ # meth<Symbol>:: name of the defaut task
9
+ #
10
+ def default_task(meth=nil)
11
+ case meth
12
+ when :none
13
+ @default_task = 'help'
14
+ when nil
15
+ @default_task ||= from_superclass(:default_task, 'help')
16
+ else
17
+ @default_task = meth.to_s
18
+ end
19
+ end
20
+
21
+ # Registers another Thor subclass as a command.
22
+ #
23
+ # ==== Parameters
24
+ # klass<Class>:: Thor subclass to register
25
+ # command<String>:: Subcommand name to use
26
+ # usage<String>:: Short usage for the subcommand
27
+ # description<String>:: Description for the subcommand
28
+ def register(klass, subcommand_name, usage, description, options={})
29
+ if klass <= Thor::Group
30
+ desc usage, description, options
31
+ define_method(subcommand_name) { invoke klass }
32
+ else
33
+ desc usage, description, options
34
+ subcommand subcommand_name, klass
35
+ end
36
+ end
37
+
38
+ # Defines the usage and the description of the next task.
39
+ #
40
+ # ==== Parameters
41
+ # usage<String>
42
+ # description<String>
43
+ # options<String>
44
+ #
45
+ def desc(usage, description, options={})
46
+ if options[:for]
47
+ task = find_and_refresh_task(options[:for])
48
+ task.usage = usage if usage
49
+ task.description = description if description
50
+ else
51
+ @usage, @desc, @hide = usage, description, options[:hide] || false
52
+ end
53
+ end
54
+
55
+ # Defines the long description of the next task.
56
+ #
57
+ # ==== Parameters
58
+ # long description<String>
59
+ #
60
+ def long_desc(long_description, options={})
61
+ if options[:for]
62
+ task = find_and_refresh_task(options[:for])
63
+ task.long_description = long_description if long_description
64
+ else
65
+ @long_desc = long_description
66
+ end
67
+ end
68
+
69
+ # Maps an input to a task. If you define:
70
+ #
71
+ # map "-T" => "list"
72
+ #
73
+ # Running:
74
+ #
75
+ # thor -T
76
+ #
77
+ # Will invoke the list task.
78
+ #
79
+ # ==== Parameters
80
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task.
81
+ #
82
+ def map(mappings=nil)
83
+ @map ||= from_superclass(:map, {})
84
+
85
+ if mappings
86
+ mappings.each do |key, value|
87
+ if key.respond_to?(:each)
88
+ key.each {|subkey| @map[subkey] = value}
89
+ else
90
+ @map[key] = value
91
+ end
92
+ end
93
+ end
94
+
95
+ @map
96
+ end
97
+
98
+ # Declares the options for the next task to be declared.
99
+ #
100
+ # ==== Parameters
101
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
102
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
103
+ # or :required (string). If you give a value, the type of the value is used.
104
+ #
105
+ def method_options(options=nil)
106
+ @method_options ||= {}
107
+ build_options(options, @method_options) if options
108
+ @method_options
109
+ end
110
+
111
+ # Adds an option to the set of method options. If :for is given as option,
112
+ # it allows you to change the options from a previous defined task.
113
+ #
114
+ # def previous_task
115
+ # # magic
116
+ # end
117
+ #
118
+ # method_option :foo => :bar, :for => :previous_task
119
+ #
120
+ # def next_task
121
+ # # magic
122
+ # end
123
+ #
124
+ # ==== Parameters
125
+ # name<Symbol>:: The name of the argument.
126
+ # options<Hash>:: Described below.
127
+ #
128
+ # ==== Options
129
+ # :desc - Description for the argument.
130
+ # :required - If the argument is required or not.
131
+ # :default - Default value for this argument. It cannot be required and have default values.
132
+ # :aliases - Aliases for this option.
133
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
134
+ # :banner - String to show on usage notes.
135
+ #
136
+ def method_option(name, options={})
137
+ scope = if options[:for]
138
+ find_and_refresh_task(options[:for]).options
139
+ else
140
+ method_options
141
+ end
142
+
143
+ build_option(name, options, scope)
144
+ end
145
+
146
+ # Prints help information for the given task.
147
+ #
148
+ # ==== Parameters
149
+ # shell<Thor::Shell>
150
+ # task_name<String>
151
+ #
152
+ def task_help(shell, task_name)
153
+ meth = normalize_task_name(task_name)
154
+ task = all_tasks[meth]
155
+ handle_no_task_error(meth) unless task
156
+
157
+ shell.say "Usage:"
158
+ shell.say " #{banner(task)}"
159
+ shell.say
160
+ class_options_help(shell, nil => task.options.map { |_, o| o })
161
+ if task.long_description
162
+ shell.say "Description:"
163
+ shell.print_wrapped(task.long_description, :ident => 2)
164
+ else
165
+ shell.say task.description
166
+ end
167
+ end
168
+
169
+ # Prints help information for this class.
170
+ #
171
+ # ==== Parameters
172
+ # shell<Thor::Shell>
173
+ #
174
+ def help(shell, subcommand = false)
175
+ list = printable_tasks(true, subcommand)
176
+ Thor::Util.thor_classes_in(self).each do |klass|
177
+ list += klass.printable_tasks(false)
178
+ end
179
+ list.sort!{ |a,b| a[0] <=> b[0] }
180
+
181
+ shell.say "Tasks:"
182
+ shell.print_table(list, :ident => 2, :truncate => true)
183
+ shell.say
184
+ class_options_help(shell)
185
+ end
186
+
187
+ # Returns tasks ready to be printed.
188
+ def printable_tasks(all = true, subcommand = false)
189
+ (all ? all_tasks : tasks).map do |_, task|
190
+ next if task.hidden?
191
+ item = []
192
+ item << banner(task, false, subcommand)
193
+ item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
194
+ item
195
+ end.compact
196
+ end
197
+
198
+ def subcommands
199
+ @subcommands ||= from_superclass(:subcommands, [])
200
+ end
201
+
202
+ def subcommand(subcommand, subcommand_class)
203
+ self.subcommands << subcommand.to_s
204
+ subcommand_class.subcommand_help subcommand
205
+ define_method(subcommand) { |*args| invoke subcommand_class, args }
206
+ end
207
+
208
+ # Extend check unknown options to accept a hash of conditions.
209
+ #
210
+ # === Parameters
211
+ # options<Hash>: A hash containing :only and/or :except keys
212
+ def check_unknown_options!(options={})
213
+ @check_unknown_options ||= Hash.new
214
+ options.each do |key, value|
215
+ if value
216
+ @check_unknown_options[key] = Array(value)
217
+ else
218
+ @check_unknown_options.delete(key)
219
+ end
220
+ end
221
+ @check_unknown_options
222
+ end
223
+
224
+ # Overwrite check_unknown_options? to take subcommands and options into account.
225
+ def check_unknown_options?(config) #:nodoc:
226
+ options = check_unknown_options
227
+ return false unless options
228
+
229
+ task = config[:current_task]
230
+ return true unless task
231
+
232
+ name = task.name
233
+
234
+ if subcommands.include?(name)
235
+ false
236
+ elsif options[:except]
237
+ !options[:except].include?(name.to_sym)
238
+ elsif options[:only]
239
+ options[:only].include?(name.to_sym)
240
+ else
241
+ true
242
+ end
243
+ end
244
+
245
+ protected
246
+
247
+ # The method responsible for dispatching given the args.
248
+ def dispatch(meth, given_args, given_opts, config) #:nodoc:
249
+ meth ||= retrieve_task_name(given_args)
250
+ task = all_tasks[normalize_task_name(meth)]
251
+
252
+ if task
253
+ args, opts = Thor::Options.split(given_args)
254
+ else
255
+ args, opts = given_args, nil
256
+ task = Thor::DynamicTask.new(meth)
257
+ end
258
+
259
+ opts = given_opts || opts || []
260
+ config.merge!(:current_task => task, :task_options => task.options)
261
+
262
+ trailing = args[Range.new(arguments.size, -1)]
263
+ new(args, opts, config).invoke_task(task, trailing || [])
264
+ end
265
+
266
+ # The banner for this class. You can customize it if you are invoking the
267
+ # thor class by another ways which is not the Thor::Runner. It receives
268
+ # the task that is going to be invoked and a boolean which indicates if
269
+ # the namespace should be displayed as arguments.
270
+ #
271
+ def banner(task, namespace = nil, subcommand = false)
272
+ "#{basename} #{task.formatted_usage(self, $thor_runner, subcommand)}"
273
+ end
274
+
275
+ def baseclass #:nodoc:
276
+ Thor
277
+ end
278
+
279
+ def create_task(meth) #:nodoc:
280
+ if @usage && @desc
281
+ base_class = @hide ? Thor::HiddenTask : Thor::Task
282
+ tasks[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
283
+ @usage, @desc, @long_desc, @method_options, @hide = nil
284
+ true
285
+ elsif self.all_tasks[meth] || meth == "method_missing"
286
+ true
287
+ else
288
+ puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
289
+ "Call desc if you want this method to be available as task or declare it inside a " <<
290
+ "no_tasks{} block. Invoked from #{caller[1].inspect}."
291
+ false
292
+ end
293
+ end
294
+
295
+ def initialize_added #:nodoc:
296
+ class_options.merge!(method_options)
297
+ @method_options = nil
298
+ end
299
+
300
+ # Retrieve the task name from given args.
301
+ def retrieve_task_name(args) #:nodoc:
302
+ meth = args.first.to_s unless args.empty?
303
+
304
+ if meth && (map[meth] || meth !~ /^\-/)
305
+ args.shift
306
+ else
307
+ nil
308
+ end
309
+ end
310
+
311
+ # Receives a task name (can be nil), and try to get a map from it.
312
+ # If a map can't be found use the sent name or the default task.
313
+ def normalize_task_name(meth) #:nodoc:
314
+ meth = map[meth.to_s] || find_subcommand_and_update_argv(meth) || meth || default_task
315
+ meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
316
+ end
317
+
318
+ # terrible hack that overwrites ARGV
319
+ def find_subcommand_and_update_argv(subcmd_name) #:nodoc:
320
+ return unless subcmd_name
321
+ cmd = find_subcommand(subcmd_name)
322
+ ARGV[0] = cmd if cmd
323
+ cmd
324
+ end
325
+
326
+ def find_subcommand(subcmd_name)
327
+ possibilities = find_subcommand_possibilities subcmd_name
328
+ if possibilities.size > 1
329
+ raise "Ambiguous subcommand #{subcmd_name} matches [#{possibilities.join(', ')}]"
330
+ elsif possibilities.size < 1
331
+ return nil
332
+ end
333
+
334
+ possibilities.first
335
+ end
336
+
337
+ def find_subcommand_possibilities(subcmd_name)
338
+ len = subcmd_name.length
339
+ all_tasks.map {|t| t.first}.select { |n| subcmd_name == n[0, len] }
340
+ end
341
+
342
+ def subcommand_help(cmd)
343
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
344
+ class_eval <<-RUBY
345
+ def help(task = nil, subcommand = true); super; end
346
+ RUBY
347
+ end
348
+ end
349
+
350
+ include Thor::Base
351
+
352
+ map HELP_MAPPINGS => :help
353
+
354
+ desc "help [TASK]", "Describe available tasks or one specific task"
355
+ def help(task = nil, subcommand = false)
356
+ task ? self.class.task_help(shell, task) : self.class.help(shell, subcommand)
357
+ end
358
+ end