wycats-thor 0.9.8 → 0.10.26

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
@@ -1,9 +1,32 @@
1
1
  == TODO
2
2
 
3
- * Change Thor.start to parse ARGV in a single pass
4
3
  * Improve spec coverage for Thor::Runner
5
4
  * Improve help output to list shorthand switches, too
6
- * Investigate and fix deep namespacing ("foo:bar:baz") issues
5
+
6
+ == Current
7
+
8
+ * BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
9
+ since it wrong behavior to the invocation system.
10
+
11
+ * thor help now show information about any class/task. All those calls are
12
+ possible:
13
+
14
+ thor help describe
15
+ thor help describe:amazing
16
+
17
+ Or even with default namespaces:
18
+
19
+ thor help :spec
20
+
21
+ * Thor::Runner now invokes the default task if none is supplied:
22
+
23
+ thor describe # invokes the default task, usually help
24
+
25
+ * Thor::Runner now works with mappings:
26
+
27
+ thor describe -h
28
+
29
+ * Added some documentation and code refactoring.
7
30
 
8
31
  == 0.9.8, released 2008-10-20
9
32
 
@@ -49,4 +72,4 @@
49
72
  * Thor::Options now doesn't parse through things that look like options but aren't.
50
73
  * Add URI detection to install task, and make sure we don't append ".thor" to URIs
51
74
  * Add rake2thor to the gem binfiles.
52
- * Make sure local Thorfiles override system-wide ones.
75
+ * Make sure local Thorfiles override system-wide ones.
data/README.markdown CHANGED
@@ -1,76 +1,247 @@
1
1
  thor
2
2
  ====
3
3
 
4
- Map options to a class. Simply create a class with the appropriate annotations, and have options automatically map
5
- to functions and parameters.
4
+ Map options to a class. Simply create a class with the appropriate annotations
5
+ and have options automatically map to functions and parameters.
6
6
 
7
7
  Example:
8
8
 
9
- class MyApp < Thor # [1]
10
- map "-L" => :list # [2]
11
-
12
- desc "install APP_NAME", "install one of the available apps" # [3]
13
- method_options :force => :boolean, :alias => :optional # [4]
9
+ class App < Thor # [1]
10
+ map "-L" => :list # [2]
11
+
12
+ desc "install APP_NAME", "install one of the available apps" # [3]
13
+ method_options :force => :boolean, :alias => :string # [4]
14
14
  def install(name)
15
15
  user_alias = options[:alias]
16
16
  if options.force?
17
17
  # do something
18
18
  end
19
- # ... other code ...
19
+ # other code
20
20
  end
21
21
 
22
22
  desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
23
- def list(search = "")
23
+ def list(search="")
24
24
  # list everything
25
25
  end
26
26
  end
27
-
27
+
28
28
  Thor automatically maps commands as such:
29
29
 
30
- app install myname --force
31
-
30
+ thor app:install myname --force
31
+
32
32
  That gets converted to:
33
33
 
34
- MyApp.new.install("myname")
34
+ App.new.install("myname")
35
35
  # with {'force' => true} as options hash
36
36
 
37
37
  1. Inherit from Thor to turn a class into an option mapper
38
- 2. Map additional non-valid identifiers to specific methods. In this case,
39
- convert -L to :list
40
- 3. Describe the method immediately below. The first parameter is the usage information,
41
- and the second parameter is the description.
42
- 4. Provide any additional options. These will be marshaled from `--` and `-` params.
43
- In this case, a `--force` and a `-f` option is added.
44
-
38
+ 2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list
39
+ 3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description
40
+ 4. Provide any additional options that will be available the instance method options.
41
+
45
42
  Types for `method_options`
46
43
  --------------------------
47
44
 
48
45
  <dl>
49
46
  <dt><code>:boolean</code></dt>
50
- <dd>true if the option is passed</dd>
51
- <dt><code>true</code></dt>
52
- <dd>same as <code>:boolean</code></dd>
53
- <dt><code>:required</code></dt>
54
- <dd>the value for this option MUST be provided</dd>
55
- <dt><code>:optional</code></dt>
56
- <dd>the value for this option MAY be provided</dd>
47
+ <dd>is parsed as --option or --option=true</dd>
48
+ <dt><code>:string</code></dt>
49
+ <dd>is parsed as --option=VALUE</dd>
57
50
  <dt><code>:numeric</code></dt>
58
- <dd>the value MAY be provided, but MUST be in numeric form</dd>
59
- <dt>a String or Numeric</dt>
60
- <dd>same as <code>:optional</code>, but fall back to the given object as default value</dd>
51
+ <dd>is parsed as --option=N</dd>
52
+ <dt><code>:array</code></dt>
53
+ <dd>is parsed as --option=one two three</dd>
54
+ <dt><code>:hash</code></dt>
55
+ <dd>is parsed as --option=key:value key:value key:value</dd>
61
56
  </dl>
62
57
 
63
- In case of unsatisfied requirements, `Thor::Options::Error` is raised.
58
+ Besides, method_option allows a default value to be given, examples:
59
+
60
+ method_options :force => false
61
+ #=> Creates a boolean option with default value false
62
+
63
+ method_options :alias => "bar"
64
+ #=> Creates a string option with default value "bar"
65
+
66
+ method_options :threshold => 3.0
67
+ #=> Creates a numeric option with default value 3.0
68
+
69
+ You can also supply :option => :required to mark an option as required. The
70
+ type is assumed to be string. If you want a required hash with default values
71
+ as option, you can use `method_option` which uses a more declarative style:
72
+
73
+ method_option :attributes, :type => :hash, :default => {}, :required => true
74
+
75
+ All arguments can be set to nil (except required arguments), by suppling a no or
76
+ skip variant. For example:
77
+
78
+ thor app name --no-attributes
79
+
80
+ In previous versions, aliases for options were created automatically, but now
81
+ they should be explicit. You can supply aliases in both short and declarative
82
+ styles:
83
+
84
+ method_options %w( force -f ) => :boolean
85
+
86
+ Or:
87
+
88
+ method_option :force, :type => :boolean, :aliases => "-f"
89
+
90
+ You can supply as many aliases as you want.
91
+
92
+ NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead.
93
+
94
+ Namespaces
95
+ ----------
96
+
97
+ By default, your Thor tasks are invoked using Ruby namespace. In the example
98
+ above, tasks are invoked as:
99
+
100
+ thor app:install name --force
101
+
102
+ However, you could namespace your class as:
103
+
104
+ module Sinatra
105
+ class App < Thor
106
+ # tasks
107
+ end
108
+ end
109
+
110
+ And then you should invoke your tasks as:
111
+
112
+ thor sinatra:app:install name --force
113
+
114
+ If desired, you can change the namespace:
115
+
116
+ module Sinatra
117
+ class App < Thor
118
+ namespace :myapp
119
+ # tasks
120
+ end
121
+ end
122
+
123
+ And then your tasks hould be invoked as:
124
+
125
+ thor myapp:install name --force
126
+
127
+ Invocations
128
+ -----------
129
+
130
+ Thor comes with a invocation-dependency system as well which allows a task to be
131
+ invoked only once. For example:
132
+
133
+ class Counter < Thor
134
+ desc "one", "Prints 1, 2, 3"
135
+ def one
136
+ puts 1
137
+ invoke :two
138
+ invoke :three
139
+ end
140
+
141
+ desc "two", "Prints 2, 3"
142
+ def two
143
+ puts 2
144
+ invoke :three
145
+ end
146
+
147
+ desc "three", "Prints 3"
148
+ def three
149
+ puts 3
150
+ end
151
+ end
152
+
153
+ When invoking the task one:
154
+
155
+ thor counter:one
156
+
157
+ The output is "1 2 3", which means that the three task was invoked only once.
158
+ You can even invoke tasks from another class, so be sure to check the
159
+ documentation.
160
+
161
+ Thor::Group
162
+ -----------
163
+
164
+ Thor has a special class called Thor::Group. The main difference to Thor class
165
+ is that it invokes all tasks at once. The example above could be rewritten in
166
+ Thor::Group as this:
167
+
168
+ class Counter < Thor::Group
169
+ desc "Prints 1, 2, 3"
170
+
171
+ def one
172
+ puts 1
173
+ end
174
+
175
+ def two
176
+ puts 2
177
+ end
178
+
179
+ def three
180
+ puts 3
181
+ end
182
+ end
183
+
184
+ When invoked:
185
+
186
+ thor counter
187
+
188
+ It prints "1 2 3" as well. Notice you should described (desc) only the class
189
+ and not each task anymore. Thor::Group is a great tool to create generators,
190
+ since you can define several steps which are invoked in the order they are
191
+ defined (Thor::Group is the tool use in generators in Rails 3.0).
192
+
193
+ Besides, Thor::Group can parse arguments and options as Thor tasks:
194
+
195
+ class Counter < Thor::Group
196
+ # number will be available as attr_accessor
197
+ argument :number, :type => :numeric, :desc => "The number to start counting"
198
+ desc "Prints the 'number' given upto 'number+2'"
199
+
200
+ def one
201
+ puts number + 0
202
+ end
203
+
204
+ def two
205
+ puts number + 1
206
+ end
207
+
208
+ def three
209
+ puts number + 2
210
+ end
211
+ end
212
+
213
+ The counter above expects one parameter and has the folling outputs:
214
+
215
+ thor counter 5
216
+ # Prints "5 6 7"
217
+
218
+ thor counter 11
219
+ # Prints "11 12 13"
220
+
221
+ You can also give options to Thor::Group, but instead of using `method_option` and
222
+ `method_options`, you should use `class_option` and `class_options`. Both argument
223
+ and class_options methods are available to Thor class as well.
224
+
225
+ Actions
226
+ -------
227
+
228
+ Thor comes with several actions which helps with script and generator tasks. You
229
+ might be familiar with them since some came from Rails Templates. They are: `say`,
230
+ `ask`, `yes?`, `no?`, `add_file`, `remove_file`, `copy_file`, `template`,
231
+ `directory`, `inside`, `run`, `inject_into_file` and a couple more.
232
+
233
+ To use them, you just need to include Thor::Actions in your Thor classes:
234
+
235
+ class App < Thor
236
+ include Thor::Actions
237
+ # tasks
238
+ end
239
+
240
+ Some actions like copy file requires that a class method called source_root is
241
+ defined in your class. This is the directory where your templates should be
242
+ placed. Be sure to check the documentation.
64
243
 
65
- Examples of option parsing:
244
+ License
245
+ -------
66
246
 
67
- # let's say this is how we defined options for a method:
68
- method_options(:force => :boolean, :retries => :numeric)
69
-
70
- # here is how the following command-line invocations would be parsed:
71
-
72
- command -f --retries 5 # => {'force' => true, 'retries' => 5}
73
- command --force -r=5 # => {'force' => true, 'retries' => 5}
74
- command -fr 5 # => {'force' => true, 'retries' => 5}
75
- command --retries=5 # => {'retries' => 5}
76
- command -r5 # => {'retries' => 5}
247
+ See MIT LICENSE.
data/bin/rake2thor CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'ruby2ruby'
5
+ require 'parse_tree'
6
+ if Ruby2Ruby::VERSION >= "1.2.0"
7
+ require 'parse_tree_extensions'
8
+ end
5
9
  require 'rake'
6
10
 
7
11
  input = ARGV[0] || 'Rakefile'
data/bin/thor CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require File.dirname(__FILE__) + "/../lib/thor"
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'thor')
5
5
  require 'thor/runner'
6
6
 
7
7
  Thor::Runner.start
@@ -0,0 +1,32 @@
1
+ require 'thor/actions/templater'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Copies the file from the relative source to the relative destination. If
7
+ # the destination is not given it's assumed to be equal to the source.
8
+ #
9
+ # ==== Parameters
10
+ # source<String>:: the relative path to the source root
11
+ # destination<String>:: the relative path to the destination root
12
+ # log_status<Boolean>:: if false, does not log the status. True by default.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # copy_file "README", "doc/README"
17
+ #
18
+ # copy_file "doc/README"
19
+ #
20
+ def copy_file(source, destination=nil, log_status=true)
21
+ action CopyFile.new(self, source, destination || source, log_status)
22
+ end
23
+
24
+ class CopyFile < Templater #:nodoc:
25
+
26
+ def render
27
+ @render ||= ::File.read(source)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ require 'thor/actions/templater'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Create a new file relative to the destination root with the given data,
7
+ # which is the return value of a block or a data string.
8
+ #
9
+ # ==== Parameters
10
+ # destination<String>:: the relative path to the destination root.
11
+ # data<String|NilClass>:: the data to append to the file.
12
+ # log_status<Boolean>:: if false, does not log the status. True by default.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # create_file "lib/fun_party.rb" do
17
+ # hostname = ask("What is the virtual hostname I should use?")
18
+ # "vhost.name = #{hostname}"
19
+ # end
20
+ #
21
+ # create_file "config/apach.conf", "your apache config"
22
+ #
23
+ def create_file(destination, data=nil, log_status=true, &block)
24
+ action CreateFile.new(self, destination, block || data.to_s, log_status)
25
+ end
26
+ alias :add_file :create_file
27
+
28
+ # AddFile is a subset of Template, which instead of rendering a file with
29
+ # ERB, it gets the content from the user.
30
+ #
31
+ class CreateFile < Templater #:nodoc:
32
+ attr_reader :data
33
+
34
+ def initialize(base, destination, data, log_status)
35
+ super(base, nil, destination, log_status)
36
+ @data = data
37
+ end
38
+
39
+ def render
40
+ @render ||= if data.is_a?(Proc)
41
+ data.call
42
+ else
43
+ data
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ require 'thor/actions/templater'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Copies interactively the files from source directory to root directory.
7
+ # If any of the files finishes with .tt, it's considered to be a template
8
+ # and is placed in the destination without the extension .tt. If any
9
+ # empty directory is found, it's copied and all .empty_directory files are
10
+ # ignored. Remember that file paths can also be encoded, let's suppose a doc
11
+ # directory with the following files:
12
+ #
13
+ # doc/
14
+ # components/.empty_directory
15
+ # README
16
+ # rdoc.rb.tt
17
+ # %app_name%.rb
18
+ #
19
+ # When invoked as:
20
+ #
21
+ # directory "doc"
22
+ #
23
+ # It will create a doc directory in the destination with the following
24
+ # files (assuming that the app_name is "blog"):
25
+ #
26
+ # doc/
27
+ # components/
28
+ # README
29
+ # rdoc.rb
30
+ # blog.rb
31
+ #
32
+ # ==== Parameters
33
+ # source<String>:: the relative path to the source root
34
+ # destination<String>:: the relative path to the destination root
35
+ # recursive<Boolean>:: if the directory must be copied recursively, true by default
36
+ # log_status<Boolean>:: if false, does not log the status. True by default.
37
+ #
38
+ # ==== Examples
39
+ #
40
+ # directory "doc"
41
+ # directory "doc", "docs", false
42
+ #
43
+ def directory(source, destination=nil, recursive=true, log_status=true)
44
+ action Directory.new(self, source, destination || source, recursive, log_status)
45
+ end
46
+
47
+ class Directory < Templater #:nodoc:
48
+ attr_reader :recursive
49
+
50
+ def initialize(base, source, destination=nil, recursive=true, log_status=true)
51
+ @recursive = recursive
52
+ super(base, source, destination, log_status)
53
+ end
54
+
55
+ def invoke!
56
+ base.empty_directory given_destination
57
+ lookup = recursive ? File.join(source, '**', '*') : File.join(source, '*')
58
+
59
+ Dir[lookup].each do |file_source|
60
+ file_destination = File.join(given_destination, file_source.gsub(source, '.'))
61
+
62
+ if File.directory?(file_source)
63
+ base.empty_directory(file_destination, @log_status) if recursive
64
+ elsif file_source !~ /\.empty_directory$/
65
+ if file_source =~ /\.tt$/
66
+ base.template(file_source, file_destination[0..-4], @log_status)
67
+ else
68
+ base.copy_file(file_source, file_destination, @log_status)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end