thor 0.9.5 → 0.9.9

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.
data/CHANGELOG.rdoc CHANGED
@@ -5,6 +5,23 @@
5
5
  * Improve help output to list shorthand switches, too
6
6
  * Investigate and fix deep namespacing ("foo:bar:baz") issues
7
7
 
8
+ == 0.9.8, released 2008-10-20
9
+
10
+ * Fixed some tiny issues that were introduced lately.
11
+
12
+ == 0.9.7, released 2008-10-13
13
+
14
+ * Setting global method options on the initialize method works as expected:
15
+ All other tasks will accept these global options in addition to their own.
16
+ * Added 'group' notion to Thor task sets (class Thor); by default all tasks
17
+ are in the 'standard' group. Running 'thor -T' will only show the standard
18
+ tasks - adding --all will show all tasks. You can also filter on a specific
19
+ group using the --group option: thor -T --group advanced
20
+
21
+ == 0.9.6, released 2008-09-13
22
+
23
+ * Generic improvements
24
+
8
25
  == 0.9.5, released 2008-08-27
9
26
 
10
27
  * Improve Windows compatibility
data/lib/thor/options.rb CHANGED
@@ -65,6 +65,8 @@ class Thor
65
65
  def initialize(switches)
66
66
  @defaults = {}
67
67
  @shorts = {}
68
+
69
+ @leading_non_opts, @trailing_non_opts = [], []
68
70
 
69
71
  @switches = switches.inject({}) do |mem, (name, type)|
70
72
  if name.is_a?(Array)
@@ -167,13 +169,15 @@ class Thor
167
169
  else
168
170
  sample = @defaults[undasherize(opt)]
169
171
  sample ||= case type
170
- when :optional then opt.gsub(/\-/, "").upcase
172
+ when :optional then undasherize(opt).gsub(/\-/, "_").upcase
171
173
  when :numeric then "N"
172
174
  end
173
175
  "[" + opt + "=" + sample.to_s + "]"
174
176
  end
175
177
  end.join(" ")
176
178
  end
179
+
180
+ alias :to_s :formatted_usage
177
181
 
178
182
  private
179
183
 
data/lib/thor/runner.rb CHANGED
@@ -18,8 +18,17 @@ class Thor::Runner < Thor
18
18
  method_options :as => :optional, :relative => :boolean
19
19
  def install(name)
20
20
  initialize_thorfiles
21
+
22
+ base = name
23
+ package = :file
24
+
21
25
  begin
22
- contents = open(name).read
26
+ if File.directory?(File.expand_path(name))
27
+ base, package = File.join(name, "main.thor"), :directory
28
+ contents = open(base).read
29
+ else
30
+ contents = open(name).read
31
+ end
23
32
  rescue OpenURI::HTTPError
24
33
  raise Error, "Error opening URI `#{name}'"
25
34
  rescue Errno::ENOENT
@@ -35,9 +44,7 @@ class Thor::Runner < Thor
35
44
 
36
45
  return false unless response =~ /^\s*y/i
37
46
 
38
- constants = Thor::Util.constants_in_contents(contents)
39
-
40
- # name = name =~ /\.thor$/ || is_uri ? name : "#{name}.thor"
47
+ constants = Thor::Util.constants_in_contents(contents, base)
41
48
 
42
49
  as = options["as"] || begin
43
50
  first_line = contents.split("\n")[0]
@@ -63,8 +70,12 @@ class Thor::Runner < Thor
63
70
 
64
71
  puts "Storing thor file in your system repository"
65
72
 
66
- File.open(File.join(thor_root, yaml[as][:filename]), "w") do |file|
67
- file.puts contents
73
+ destination = File.join(thor_root, yaml[as][:filename])
74
+
75
+ if package == :file
76
+ File.open(destination, "w") {|f| f.puts contents }
77
+ else
78
+ FileUtils.cp_r(name, destination)
68
79
  end
69
80
 
70
81
  yaml[as][:filename] # Indicate sucess
@@ -78,7 +89,7 @@ class Thor::Runner < Thor
78
89
  puts "Uninstalling #{name}."
79
90
 
80
91
  file = File.join(thor_root, "#{yaml[name][:filename]}")
81
- File.delete(file)
92
+ FileUtils.rm_rf(file)
82
93
  yaml.delete(name)
83
94
  save_yaml(yaml)
84
95
 
@@ -92,7 +103,7 @@ class Thor::Runner < Thor
92
103
 
93
104
  puts "Updating `#{name}' from #{yaml[name][:location]}"
94
105
  old_filename = yaml[name][:filename]
95
- options["as"] = name
106
+ self.options = self.options.merge("as" => name)
96
107
  filename = install(yaml[name][:location])
97
108
  unless filename == old_filename
98
109
  File.delete(File.join(thor_root, old_filename))
@@ -113,14 +124,20 @@ class Thor::Runner < Thor
113
124
  end
114
125
 
115
126
  desc "list [SEARCH]", "list the available thor tasks (--substring means SEARCH can be anywhere in the module)"
116
- method_options :substring => :boolean
127
+ method_options :substring => :boolean,
128
+ :group => :optional,
129
+ :all => :boolean
117
130
  def list(search = "")
118
131
  initialize_thorfiles
119
132
  search = ".*#{search}" if options["substring"]
120
133
  search = /^#{search}.*/i
121
-
122
- display_klasses(false, Thor.subclasses.select {|k|
123
- Thor::Util.constant_to_thor_path(k.name) =~ search})
134
+ group = options[:group] || 'standard'
135
+
136
+ classes = Thor.subclasses.select do |k|
137
+ (options[:all] || k.group_name == group) &&
138
+ Thor::Util.constant_to_thor_path(k.name) =~ search
139
+ end
140
+ display_klasses(false, classes)
124
141
  end
125
142
 
126
143
  # Override Thor#help so we can give info about not-yet-loaded tasks
@@ -148,7 +165,10 @@ class Thor::Runner < Thor
148
165
  # C:\Documents and Settings\james\.thor
149
166
  #
150
167
  # If we don't #gsub the \ character, Dir.glob will fail.
151
- Dir["#{thor_root.gsub(/\\/, '/')}/**/*"]
168
+ files = Dir["#{thor_root.gsub(/\\/, '/')}/*"]
169
+ files.map! do |file|
170
+ File.directory?(file) ? File.join(file, "main.thor") : file
171
+ end
152
172
  end
153
173
 
154
174
  private
@@ -176,7 +196,7 @@ class Thor::Runner < Thor
176
196
  raise Error, "No Thor tasks available" if klasses.empty?
177
197
 
178
198
  if with_modules && !(yaml = thor_yaml).empty?
179
- max_name = yaml.max {|(xk,xv),(yk,yv)| xk.size <=> yk.size }.first.size
199
+ max_name = yaml.max {|(xk,xv),(yk,yv)| xk.to_s.size <=> yk.to_s.size }.first.size
180
200
  modules_label = "Modules"
181
201
  namespaces_label = "Namespaces"
182
202
  column_width = [max_name + 4, modules_label.size + 1].max
@@ -194,9 +214,6 @@ class Thor::Runner < Thor
194
214
  puts
195
215
  end
196
216
 
197
- puts "Tasks"
198
- puts "-----"
199
-
200
217
  # Calculate the largest base class name
201
218
  max_base = klasses.max do |x,y|
202
219
  Thor::Util.constant_to_thor_path(x.name).size <=> Thor::Util.constant_to_thor_path(y.name).size
@@ -208,16 +225,39 @@ class Thor::Runner < Thor
208
225
  end
209
226
 
210
227
  max_left = max_left_item.maxima.usage + max_left_item.maxima.opt
211
-
212
- klasses.each {|k| display_tasks(k, max_base, max_left)}
228
+
229
+ unless klasses.empty?
230
+ puts # add some spacing
231
+ klasses.each { |k| display_tasks(k, max_base, max_left); }
232
+ else
233
+ puts "\033[1;34mNo Thor tasks available\033[0m"
234
+ end
213
235
  end
214
236
 
215
237
  def display_tasks(klass, max_base, max_left)
216
- base = Thor::Util.constant_to_thor_path(klass.name)
217
- klass.tasks.each true do |name, task|
218
- format_string = "%-#{max_left + max_base + 5}s"
219
- print format_string % task.formatted_usage(true)
220
- puts task.description
238
+ if klass.tasks.values.length > 1
239
+
240
+ base = Thor::Util.constant_to_thor_path(klass.name)
241
+
242
+ if base.to_a.empty?
243
+ base = 'default'
244
+ puts "\033[1;35m#{base}\033[0m"
245
+ else
246
+ puts "\033[1;34m#{base}\033[0m"
247
+ end
248
+ puts "-" * base.length
249
+
250
+ klass.tasks.each true do |name, task|
251
+ format_string = "%-#{max_left + max_base + 5}s"
252
+ print format_string % task.formatted_usage(true)
253
+ puts task.description
254
+ end
255
+
256
+ unless klass.opts.empty?
257
+ puts "\nglobal options: #{Options.new(klass.opts)}"
258
+ end
259
+
260
+ puts # add some spacing
221
261
  end
222
262
  end
223
263
 
@@ -226,8 +266,9 @@ class Thor::Runner < Thor
226
266
  end
227
267
 
228
268
  def load_thorfile(path)
269
+ txt = File.read(path)
229
270
  begin
230
- load path
271
+ Thor::Tasks.class_eval txt, path
231
272
  rescue Object => e
232
273
  $stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}"
233
274
  end
@@ -246,8 +287,12 @@ class Thor::Runner < Thor
246
287
 
247
288
  # We want to load system-wide Thorfiles first
248
289
  # so the local Thorfiles will override them.
249
- (relevant_to ? thorfiles_relevant_to(relevant_to) :
290
+ files = (relevant_to ? thorfiles_relevant_to(relevant_to) :
250
291
  thor_root_glob) + thorfiles - ["#{thor_root}/thor.yml"]
292
+
293
+ files.map! do |file|
294
+ File.directory?(file) ? File.join(file, "main.thor") : file
295
+ end
251
296
  end
252
297
 
253
298
  def thorfiles_relevant_to(meth)
data/lib/thor/task.rb CHANGED
@@ -3,9 +3,16 @@ require 'thor/util'
3
3
 
4
4
  class Thor
5
5
  class Task < Struct.new(:meth, :description, :usage, :opts, :klass)
6
+
6
7
  def self.dynamic(meth, klass)
7
8
  new(meth, "A dynamically-generated task", meth.to_s, nil, klass)
8
9
  end
10
+
11
+ def initialize(*args)
12
+ # keep the original opts - we need them later on
13
+ @options = args[3] || {}
14
+ super
15
+ end
9
16
 
10
17
  def parse(obj, args)
11
18
  list, hash = parse_args(args)
@@ -48,9 +55,13 @@ class Thor
48
55
 
49
56
  def opts
50
57
  return super unless super.kind_of? Hash
51
- self.opts = Options.new(super)
58
+ @_opts ||= Options.new(super)
52
59
  end
53
-
60
+
61
+ def full_opts
62
+ @_full_opts ||= Options.new((klass.opts || {}).merge(@options))
63
+ end
64
+
54
65
  def formatted_usage(namespace = false)
55
66
  (namespace ? self.namespace + ':' : '') + usage +
56
67
  (opts ? " " + opts.formatted_usage : "")
@@ -59,9 +70,9 @@ class Thor
59
70
  protected
60
71
 
61
72
  def parse_args(args)
62
- return [args, {}] unless opts
63
- hash = opts.parse(args)
64
- list = opts.non_opts
73
+ return [[], {}] if args.nil?
74
+ hash = full_opts.parse(args)
75
+ list = full_opts.non_opts
65
76
  [list, hash]
66
77
  end
67
78
  end
data/lib/thor/tasks.rb CHANGED
@@ -22,7 +22,7 @@ class Thor
22
22
  old_stderr, $stderr = $stderr.dup, File.open(null, "w")
23
23
  package
24
24
  $stderr = old_stderr
25
- system %{#{sudo} #{gem} install pkg/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
25
+ system %{#{sudo} #{Gem.ruby} -S #{gem} install pkg/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
26
26
  end
27
27
  end
28
28
 
data/lib/thor/util.rb CHANGED
@@ -1,10 +1,38 @@
1
1
  require 'thor/error'
2
2
 
3
+ module ObjectSpace
4
+
5
+ class << self
6
+
7
+ # @return <Array[Class]> All the classes in the object space.
8
+ def classes
9
+ klasses = []
10
+ ObjectSpace.each_object(Class) {|o| klasses << o}
11
+ klasses
12
+ end
13
+ end
14
+
15
+ end
16
+
3
17
  class Thor
18
+ module Tasks; end
19
+
4
20
  module Util
5
21
 
22
+ def self.full_const_get(obj, name)
23
+ list = name.split("::")
24
+ list.shift if list.first.empty?
25
+ list.each do |x|
26
+ # This is required because const_get tries to look for constants in the
27
+ # ancestor chain, but we only want constants that are HERE
28
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
29
+ end
30
+ obj
31
+ end
32
+
6
33
  def self.constant_to_thor_path(str, remove_default = true)
7
- str = snake_case(str.to_s).squeeze(":")
34
+ str = str.to_s.gsub(/^Thor::Tasks::/, "")
35
+ str = snake_case(str).squeeze(":")
8
36
  str.gsub!(/^default/, '') if remove_default
9
37
  str
10
38
  end
@@ -21,16 +49,20 @@ class Thor
21
49
  str.gsub(/:(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
22
50
  end
23
51
 
24
- def self.constants_in_contents(str)
25
- klasses = self.constants.dup
26
- eval(str)
27
- ret = self.constants - klasses
28
- ret.each {|k| self.send(:remove_const, k)}
29
- ret
52
+ def self.constants_in_contents(str, file = __FILE__)
53
+ klasses = ObjectSpace.classes.dup
54
+ Module.new.class_eval(str, file)
55
+ klasses = ObjectSpace.classes - klasses
56
+ klasses = klasses.select {|k| k < Thor }
57
+ klasses.map! {|k| k.to_s.gsub(/#<Module:\w+>::/, '')}
30
58
  end
31
59
 
32
- def self.make_constant(str)
33
- list = str.split("::").inject(Object) {|obj, x| obj.const_get(x)}
60
+ def self.make_constant(str, base = [Thor::Tasks, Object])
61
+ which = base.find do |obj|
62
+ full_const_get(obj, str) rescue nil
63
+ end
64
+ return full_const_get(which, str) if which
65
+ raise NameError, "uninitialized constant #{str}"
34
66
  end
35
67
 
36
68
  def self.snake_case(str)
data/lib/thor.rb CHANGED
@@ -22,6 +22,14 @@ class Thor
22
22
  @usage, @desc = usage, description
23
23
  end
24
24
 
25
+ def self.group(name)
26
+ @group_name = name.to_s
27
+ end
28
+
29
+ def self.group_name
30
+ @group_name || 'standard'
31
+ end
32
+
25
33
  def self.method_options(opts)
26
34
  @method_options = opts
27
35
  end
@@ -80,7 +88,7 @@ class Thor
80
88
 
81
89
  def method_added(meth)
82
90
  meth = meth.to_s
83
-
91
+
84
92
  if meth == "initialize"
85
93
  @opts = @method_options
86
94
  @method_options = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yehuda Katz
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-27 00:00:00 +02:00
12
+ date: 2008-12-15 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,8 +22,8 @@ extensions: []
22
22
 
23
23
  extra_rdoc_files:
24
24
  - README.markdown
25
- - CHANGELOG.rdoc
26
25
  - LICENSE
26
+ - CHANGELOG.rdoc
27
27
  files:
28
28
  - README.markdown
29
29
  - LICENSE
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  requirements: []
66
66
 
67
67
  rubyforge_project: thor
68
- rubygems_version: 1.2.0
68
+ rubygems_version: 1.3.1
69
69
  signing_key:
70
70
  specification_version: 2
71
71
  summary: A gem that maps options to a class