thor 0.12.3 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
- require 'rdoc/task'
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::Task.new do |rdoc|
30
- rdoc.main = "README.rdoc"
31
- rdoc.rdoc_dir = "rdoc"
32
- rdoc.title = GEM_NAME
33
- rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
34
- rdoc.rdoc_files.include('lib/**/*.rb')
35
- rdoc.options << '--line-numbers' << '--inline-source'
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
@@ -1,6 +1,4 @@
1
1
  require 'thor/base'
2
- require 'thor/group'
3
- require 'thor/actions'
4
2
 
5
3
  # TODO: Update thor to allow for git-style CLI (git bisect run)
6
4
  class Thor
@@ -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.gsub!(regexp, string)
95
- File.open(destination, 'wb') { |file| file.write(content) }
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
 
@@ -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
@@ -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
@@ -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
- return false if no?("Do you wish to continue [y/N]?")
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]
@@ -1,6 +1,6 @@
1
1
  class Thor
2
2
  class Task < Struct.new(:name, :description, :usage, :options)
3
- FILE_REGEXP = /^#{Regexp.escape(File.expand_path(__FILE__))}:[\w:]+ `run'$/
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
- # For Ruby <= 1.8.7, we have to match the method name that we are trying to call.
82
- # In Ruby >= 1.9.1, we have to match the method run in this file.
83
- def backtrace_match?(backtrace) #:nodoc:
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
- if e.message =~ /wrong number of arguments/ && backtrace_match?(e.backtrace.first.to_s)
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
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.12.3".freeze
2
+ VERSION = "0.13.0".freeze
3
3
  end
@@ -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
@@ -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
@@ -1,4 +1,8 @@
1
1
  class MyScript < Thor
2
+ attr_accessor :some_attribute
3
+ attr_writer :another_attribute
4
+ attr_reader :another_attribute
5
+
2
6
  group :script
3
7
  default_task :example_default_task
4
8
 
@@ -2,6 +2,7 @@ $TESTING=true
2
2
 
3
3
  $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
4
4
  require 'thor'
5
+ require 'thor/group'
5
6
  require 'stringio'
6
7
 
7
8
  require 'rubygems'
@@ -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", "--debug"]) }.must raise_error
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.12.3
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-01-18 00:00:00 +01:00
13
+ date: 2010-02-03 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies: []
16
16