thor 0.12.3 → 0.13.0
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 +2 -0
- data/README.rdoc +65 -2
- data/Thorfile +13 -8
- data/lib/thor.rb +0 -2
- data/lib/thor/actions/inject_into_file.rb +9 -6
- data/lib/thor/base.rb +15 -0
- data/lib/thor/group.rb +2 -1
- data/lib/thor/runner.rb +5 -2
- data/lib/thor/task.rb +7 -7
- data/lib/thor/version.rb +1 -1
- data/spec/actions/inject_into_file_spec.rb +29 -0
- data/spec/base_spec.rb +15 -0
- data/spec/fixtures/script.thor +4 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/thor_spec.rb +1 -1
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
== 0.12, released 2010-01-02
|
2
2
|
|
3
|
+
* Methods generated by attr_* are automatically not marked as tasks
|
4
|
+
* inject_into_file does not add the same content twice, unless :force is set
|
3
5
|
* Removed rr in favor to rspec mock framework
|
4
6
|
* Improved output for thor -T
|
5
7
|
* [#7] Do not force white color on status
|
data/README.rdoc
CHANGED
@@ -145,7 +145,7 @@ When invoking the task one:
|
|
145
145
|
|
146
146
|
The output is "1 2 3", which means that the three task was invoked only once.
|
147
147
|
You can even invoke tasks from another class, so be sure to check the
|
148
|
-
documentation.
|
148
|
+
documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html].
|
149
149
|
|
150
150
|
== Thor::Group
|
151
151
|
|
@@ -227,7 +227,70 @@ To use them, you just need to include Thor::Actions in your Thor classes:
|
|
227
227
|
|
228
228
|
Some actions like copy file requires that a class method called source_root is
|
229
229
|
defined in your class. This is the directory where your templates should be
|
230
|
-
placed. Be sure to check the documentation.
|
230
|
+
placed. Be sure to check the documentation on actions[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html].
|
231
|
+
|
232
|
+
== Generators
|
233
|
+
|
234
|
+
A great use for Thor is creating custom generators. Combining Thor::Group,
|
235
|
+
Thor::Actions and ERB templates makes this very easy. Here is an example:
|
236
|
+
|
237
|
+
class Newgem < Thor::Group
|
238
|
+
include Thor::Actions
|
239
|
+
|
240
|
+
# Define arguments and options
|
241
|
+
argument :name
|
242
|
+
class_option :test_framework, :default => :test_unit
|
243
|
+
|
244
|
+
def self.source_root
|
245
|
+
File.dirname(__FILE__)
|
246
|
+
end
|
247
|
+
|
248
|
+
def create_lib_file
|
249
|
+
template('templates/newgem.tt', "#{name}/lib/#{name}.rb")
|
250
|
+
end
|
251
|
+
|
252
|
+
def create_test_file
|
253
|
+
test = options[:test_framework] == "rspec" ? :spec : :test
|
254
|
+
create_file "#{name}/#{test}/#{name}_#{test}.rb"
|
255
|
+
end
|
256
|
+
|
257
|
+
def copy_licence
|
258
|
+
if yes?("Use MIT license?")
|
259
|
+
# Make a copy of the MITLICENSE file at the source root
|
260
|
+
copy_file "MITLICENSE", "#{name}/MITLICENSE"
|
261
|
+
else
|
262
|
+
say "Shame on you…", :red
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
Doing a <tt>thor -T</tt> will show how to run our generator. It should read:
|
268
|
+
<tt>thor newgem NAME</tt>. This shows that we have to supply a NAME
|
269
|
+
argument for our generator to run.
|
270
|
+
|
271
|
+
The <tt>create_lib_file</tt> uses an ERB template. This is what it looks like:
|
272
|
+
|
273
|
+
class <%= name.camelize %>
|
274
|
+
end
|
275
|
+
|
276
|
+
The arguments that you set in your generator will automatically be passed in
|
277
|
+
when <tt>template</tt> gets called. Be sure to read the documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html] for
|
278
|
+
more options.
|
279
|
+
|
280
|
+
Running the generator with <tt>thor newgem devise</tt> will
|
281
|
+
create two files: "devise/lib/devise.rb",
|
282
|
+
"devise/test/devise_test.rb". The user will then be prompt (with the
|
283
|
+
use of the method <tt>yes?</tt>) if he wants to copy the MITLICENSE. If you
|
284
|
+
want to change the test framework, you can add the option:
|
285
|
+
<tt>thor newgem devise --test-framework=rspec</tt>
|
286
|
+
This will generate: "devise/lib/devise.rb" and
|
287
|
+
"devise/spec/devise_spec.rb".
|
288
|
+
|
289
|
+
== Further Reading
|
290
|
+
|
291
|
+
Thor has many scripting possibilities beyond these examples. Be sure to read
|
292
|
+
through the documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html] and specs[http://github.com/wycats/thor/tree/master/spec/] to get a better understanding of all the
|
293
|
+
options Thor offers.
|
231
294
|
|
232
295
|
== License
|
233
296
|
|
data/Thorfile
CHANGED
@@ -4,7 +4,10 @@ require File.join(File.dirname(__FILE__), "lib", "thor", "version")
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'thor/rake_compat'
|
6
6
|
require 'spec/rake/spectask'
|
7
|
-
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
8
11
|
|
9
12
|
GEM_NAME = 'thor'
|
10
13
|
EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"]
|
@@ -26,13 +29,15 @@ class Default < Thor
|
|
26
29
|
t.rcov_dir = "rcov"
|
27
30
|
end
|
28
31
|
|
29
|
-
RDoc
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
if defined?(RDoc)
|
33
|
+
RDoc::Task.new do |rdoc|
|
34
|
+
rdoc.main = "README.rdoc"
|
35
|
+
rdoc.rdoc_dir = "rdoc"
|
36
|
+
rdoc.title = GEM_NAME
|
37
|
+
rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
43
|
begin
|
data/lib/thor.rb
CHANGED
@@ -10,7 +10,8 @@ class Thor
|
|
10
10
|
# destination<String>:: Relative path to the destination root
|
11
11
|
# data<String>:: Data to add to the file. Can be given as a block.
|
12
12
|
# config<Hash>:: give :verbose => false to not log the status and the flag
|
13
|
-
# for injection (:after or :before)
|
13
|
+
# for injection (:after or :before) or :force => true for
|
14
|
+
# insert two or more times the same content.
|
14
15
|
#
|
15
16
|
# ==== Examples
|
16
17
|
#
|
@@ -55,7 +56,7 @@ class Thor
|
|
55
56
|
replacement + '\0'
|
56
57
|
end
|
57
58
|
|
58
|
-
replace!(/#{flag}/, content)
|
59
|
+
replace!(/#{flag}/, content, config[:force])
|
59
60
|
end
|
60
61
|
|
61
62
|
def revoke!
|
@@ -69,7 +70,7 @@ class Thor
|
|
69
70
|
/(#{Regexp.escape(replacement)})(.*)(#{flag})/m
|
70
71
|
end
|
71
72
|
|
72
|
-
replace!(regexp, content)
|
73
|
+
replace!(regexp, content, true)
|
73
74
|
end
|
74
75
|
|
75
76
|
protected
|
@@ -88,11 +89,13 @@ class Thor
|
|
88
89
|
|
89
90
|
# Adds the content to the file.
|
90
91
|
#
|
91
|
-
def replace!(regexp, string)
|
92
|
+
def replace!(regexp, string, force)
|
92
93
|
unless base.options[:pretend]
|
93
94
|
content = File.binread(destination)
|
94
|
-
content.
|
95
|
-
|
95
|
+
if force || !content.include?(replacement)
|
96
|
+
content.gsub!(regexp, string)
|
97
|
+
File.open(destination, 'wb') { |file| file.write(content) }
|
98
|
+
end
|
96
99
|
end
|
97
100
|
end
|
98
101
|
|
data/lib/thor/base.rb
CHANGED
@@ -8,6 +8,9 @@ require 'thor/task'
|
|
8
8
|
require 'thor/util'
|
9
9
|
|
10
10
|
class Thor
|
11
|
+
autoload :Actions, 'thor/actions'
|
12
|
+
autoload :RakeCompat, 'thor/rake_compat'
|
13
|
+
|
11
14
|
# Shortcuts for help.
|
12
15
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
13
16
|
|
@@ -94,6 +97,18 @@ class Thor
|
|
94
97
|
module ClassMethods
|
95
98
|
attr_accessor :debugging
|
96
99
|
|
100
|
+
def attr_reader(*) #:nodoc:
|
101
|
+
no_tasks { super }
|
102
|
+
end
|
103
|
+
|
104
|
+
def attr_writer(*) #:nodoc:
|
105
|
+
no_tasks { super }
|
106
|
+
end
|
107
|
+
|
108
|
+
def attr_accessor(*) #:nodoc:
|
109
|
+
no_tasks { super }
|
110
|
+
end
|
111
|
+
|
97
112
|
# Adds an argument to the class and creates an attr_accessor for it.
|
98
113
|
#
|
99
114
|
# Arguments are different from options in several aspects. The first one
|
data/lib/thor/group.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
require 'thor/base'
|
2
|
+
|
1
3
|
# Thor has a special class called Thor::Group. The main difference to Thor class
|
2
4
|
# is that it invokes all tasks at once. It also include some methods that allows
|
3
5
|
# invocations to be done at the class method, which are not available to Thor
|
4
6
|
# tasks.
|
5
|
-
#
|
6
7
|
class Thor::Group
|
7
8
|
class << self
|
8
9
|
# The descrition for this Thor::Group. If none is provided, but a source root
|
data/lib/thor/runner.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'thor/core_ext/file_binary_read'
|
2
3
|
require 'open-uri'
|
3
4
|
require 'yaml'
|
4
5
|
require 'digest/md5'
|
@@ -32,7 +33,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
32
33
|
end
|
33
34
|
|
34
35
|
desc "install NAME", "Install an optionally named Thor file into your system tasks"
|
35
|
-
method_options :as => :string, :relative => :boolean
|
36
|
+
method_options :as => :string, :relative => :boolean, :force => :boolean
|
36
37
|
def install(name)
|
37
38
|
initialize_thorfiles
|
38
39
|
|
@@ -55,7 +56,9 @@ class Thor::Runner < Thor #:nodoc:
|
|
55
56
|
say "Your Thorfile contains:"
|
56
57
|
say contents
|
57
58
|
|
58
|
-
|
59
|
+
unless options["force"]
|
60
|
+
return false if no?("Do you wish to continue [y/N]?")
|
61
|
+
end
|
59
62
|
|
60
63
|
as = options["as"] || begin
|
61
64
|
first_line = contents.split("\n")[0]
|
data/lib/thor/task.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Thor
|
2
2
|
class Task < Struct.new(:name, :description, :usage, :options)
|
3
|
-
FILE_REGEXP = /^#{Regexp.escape(File.
|
3
|
+
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
|
4
4
|
|
5
5
|
# A dynamic task that handles method missing scenarios.
|
6
6
|
class Dynamic < Task
|
@@ -78,15 +78,15 @@ class Thor
|
|
78
78
|
(collection & [name.to_s, name.to_sym]).empty?
|
79
79
|
end
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
method_name = /`#{Regexp.escape(name.split(':').last)}'/
|
85
|
-
backtrace =~ method_name || backtrace =~ FILE_REGEXP
|
81
|
+
def sans_backtrace(backtrace, caller) #:nodoc:
|
82
|
+
saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
|
83
|
+
saned -= caller
|
86
84
|
end
|
87
85
|
|
88
86
|
def parse_argument_error(instance, e, caller) #:nodoc:
|
89
|
-
|
87
|
+
backtrace = sans_backtrace(e.backtrace, caller)
|
88
|
+
|
89
|
+
if backtrace.empty? && e.message =~ /wrong number of arguments/
|
90
90
|
if instance.is_a?(Thor::Group)
|
91
91
|
raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
|
92
92
|
else
|
data/lib/thor/version.rb
CHANGED
@@ -55,6 +55,35 @@ describe Thor::Actions::InjectIntoFile do
|
|
55
55
|
invoke! "doc/README", "\nmore content", :after => "__start__"
|
56
56
|
File.read(file).must == "__start__\nREADME\n__end__\n"
|
57
57
|
end
|
58
|
+
|
59
|
+
it "does not change the file if already include content" do
|
60
|
+
invoke! "doc/README", :before => "__end__" do
|
61
|
+
"more content\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
File.read(file).must == "__start__\nREADME\nmore content\n__end__\n"
|
65
|
+
|
66
|
+
invoke! "doc/README", :before => "__end__" do
|
67
|
+
"more content\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
File.read(file).must == "__start__\nREADME\nmore content\n__end__\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does change the file if already include content and :force == true" do
|
74
|
+
invoke! "doc/README", :before => "__end__" do
|
75
|
+
"more content\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
File.read(file).must == "__start__\nREADME\nmore content\n__end__\n"
|
79
|
+
|
80
|
+
invoke! "doc/README", :before => "__end__", :force => true do
|
81
|
+
"more content\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
File.read(file).must == "__start__\nREADME\nmore content\nmore content\n__end__\n"
|
85
|
+
end
|
86
|
+
|
58
87
|
end
|
59
88
|
|
60
89
|
describe "#revoke!" do
|
data/spec/base_spec.rb
CHANGED
@@ -233,4 +233,19 @@ describe Thor::Base do
|
|
233
233
|
}.must raise_error(Thor::UndefinedTaskError, /the 'what' task of MyScript is private/)
|
234
234
|
end
|
235
235
|
end
|
236
|
+
|
237
|
+
describe "attr_*" do
|
238
|
+
it "should not add attr_reader as a task" do
|
239
|
+
capture(:stderr){ MyScript.start(["another_attribute"]) }.must =~ /could not find/
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should not add attr_writer as a task" do
|
243
|
+
capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }.must =~ /could not find/
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should not add attr_accessor as a task" do
|
247
|
+
capture(:stderr){ MyScript.start(["some_attribute"]) }.must =~ /could not find/
|
248
|
+
capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }.must =~ /could not find/
|
249
|
+
end
|
250
|
+
end
|
236
251
|
end
|
data/spec/fixtures/script.thor
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/thor_spec.rb
CHANGED
@@ -133,7 +133,7 @@ describe Thor do
|
|
133
133
|
end
|
134
134
|
|
135
135
|
it "raises when an exception happens within the task call" do
|
136
|
-
lambda { MyScript.start(["call_myself_with_wrong_arity"
|
136
|
+
lambda { MyScript.start(["call_myself_with_wrong_arity"]) }.must raise_error(ArgumentError)
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
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.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yehuda Katz
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2010-
|
13
|
+
date: 2010-02-03 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|