thor 0.9.9 → 0.11.5

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 (65) hide show
  1. data/CHANGELOG.rdoc +29 -4
  2. data/README.rdoc +234 -0
  3. data/Thorfile +57 -0
  4. data/VERSION +1 -0
  5. data/bin/rake2thor +4 -0
  6. data/bin/thor +1 -1
  7. data/lib/thor.rb +216 -119
  8. data/lib/thor/actions.rb +272 -0
  9. data/lib/thor/actions/create_file.rb +102 -0
  10. data/lib/thor/actions/directory.rb +87 -0
  11. data/lib/thor/actions/empty_directory.rb +133 -0
  12. data/lib/thor/actions/file_manipulation.rb +195 -0
  13. data/lib/thor/actions/inject_into_file.rb +78 -0
  14. data/lib/thor/base.rb +510 -0
  15. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  16. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  17. data/lib/thor/error.rb +25 -1
  18. data/lib/thor/group.rb +263 -0
  19. data/lib/thor/invocation.rb +178 -0
  20. data/lib/thor/parser.rb +4 -0
  21. data/lib/thor/parser/argument.rb +67 -0
  22. data/lib/thor/parser/arguments.rb +145 -0
  23. data/lib/thor/parser/option.rb +132 -0
  24. data/lib/thor/parser/options.rb +142 -0
  25. data/lib/thor/rake_compat.rb +67 -0
  26. data/lib/thor/runner.rb +232 -242
  27. data/lib/thor/shell.rb +72 -0
  28. data/lib/thor/shell/basic.rb +220 -0
  29. data/lib/thor/shell/color.rb +108 -0
  30. data/lib/thor/task.rb +97 -60
  31. data/lib/thor/util.rb +230 -55
  32. data/spec/actions/create_file_spec.rb +170 -0
  33. data/spec/actions/directory_spec.rb +118 -0
  34. data/spec/actions/empty_directory_spec.rb +91 -0
  35. data/spec/actions/file_manipulation_spec.rb +242 -0
  36. data/spec/actions/inject_into_file_spec.rb +80 -0
  37. data/spec/actions_spec.rb +291 -0
  38. data/spec/base_spec.rb +236 -0
  39. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  40. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  41. data/spec/fixtures/bundle/execute.rb +6 -0
  42. data/spec/fixtures/doc/config.rb +1 -0
  43. data/spec/group_spec.rb +177 -0
  44. data/spec/invocation_spec.rb +107 -0
  45. data/spec/parser/argument_spec.rb +47 -0
  46. data/spec/parser/arguments_spec.rb +64 -0
  47. data/spec/parser/option_spec.rb +212 -0
  48. data/spec/parser/options_spec.rb +255 -0
  49. data/spec/rake_compat_spec.rb +64 -0
  50. data/spec/runner_spec.rb +204 -0
  51. data/spec/shell/basic_spec.rb +206 -0
  52. data/spec/shell/color_spec.rb +41 -0
  53. data/spec/shell_spec.rb +25 -0
  54. data/spec/spec_helper.rb +52 -0
  55. data/spec/task_spec.rb +82 -0
  56. data/spec/thor_spec.rb +234 -0
  57. data/spec/util_spec.rb +196 -0
  58. metadata +69 -25
  59. data/README.markdown +0 -76
  60. data/Rakefile +0 -6
  61. data/lib/thor/options.rb +0 -242
  62. data/lib/thor/ordered_hash.rb +0 -64
  63. data/lib/thor/task_hash.rb +0 -22
  64. data/lib/thor/tasks.rb +0 -77
  65. data/lib/thor/tasks/package.rb +0 -18
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/parser'
3
+
4
+ describe Thor::Argument do
5
+
6
+ def argument(name, type=:string, default=nil, required=nil)
7
+ @argument ||= Thor::Argument.new(name, nil, required || default.nil?, type, default)
8
+ end
9
+
10
+ describe "errors" do
11
+ it "raises an error if name is not supplied" do
12
+ lambda {
13
+ argument(nil)
14
+ }.must raise_error(ArgumentError, "Argument name can't be nil.")
15
+ end
16
+
17
+ it "raises an error if type is unknown" do
18
+ lambda {
19
+ argument(:task, :unknown)
20
+ }.must raise_error(ArgumentError, "Type :unknown is not valid for arguments.")
21
+ end
22
+
23
+ it "raises an error if argument is required and have default values" do
24
+ lambda {
25
+ argument(:task, :string, "bar", true)
26
+ }.must raise_error(ArgumentError, "An argument cannot be required and have default value.")
27
+ end
28
+ end
29
+
30
+ describe "#usage" do
31
+ it "returns usage for string types" do
32
+ argument(:foo, :string).usage.must == "FOO"
33
+ end
34
+
35
+ it "returns usage for numeric types" do
36
+ argument(:foo, :numeric).usage.must == "N"
37
+ end
38
+
39
+ it "returns usage for array types" do
40
+ argument(:foo, :array).usage.must == "one two three"
41
+ end
42
+
43
+ it "returns usage for hash types" do
44
+ argument(:foo, :hash).usage.must == "key:value"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/parser'
3
+
4
+ describe Thor::Arguments do
5
+ def create(opts={})
6
+ arguments = opts.map do |type, default|
7
+ Thor::Argument.new(type.to_s, nil, default.nil?, type, default)
8
+ end
9
+
10
+ arguments.sort!{ |a,b| b.name <=> a.name }
11
+ @opt = Thor::Arguments.new(arguments)
12
+ end
13
+
14
+ def parse(*args)
15
+ @opt.parse(args)
16
+ end
17
+
18
+ describe "#parse" do
19
+ it "parses arguments in the given order" do
20
+ create :string => nil, :numeric => nil
21
+ parse("name", "13")["string"].must == "name"
22
+ parse("name", "13")["numeric"].must == 13
23
+ end
24
+
25
+ it "accepts hashes" do
26
+ create :string => nil, :hash => nil
27
+ parse("product", "title:string", "age:integer")["string"].must == "product"
28
+ parse("product", "title:string", "age:integer")["hash"].must == { "title" => "string", "age" => "integer"}
29
+ end
30
+
31
+ it "accepts arrays" do
32
+ create :string => nil, :array => nil
33
+ parse("product", "title", "age")["string"].must == "product"
34
+ parse("product", "title", "age")["array"].must == %w(title age)
35
+ end
36
+
37
+ describe "with no inputs" do
38
+ it "and no arguments returns an empty hash" do
39
+ create
40
+ parse.must == {}
41
+ end
42
+
43
+ it "and required arguments raises an error" do
44
+ create :string => nil, :numeric => nil
45
+ lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "no value provided for required arguments 'string', 'numeric'")
46
+ end
47
+
48
+ it "and default arguments returns default values" do
49
+ create :string => "name", :numeric => 13
50
+ parse.must == { "string" => "name", "numeric" => 13 }
51
+ end
52
+ end
53
+
54
+ it "returns the input if it's already parsed" do
55
+ create :string => nil, :hash => nil, :array => nil, :numeric => nil
56
+ parse("", 0, {}, []).must == { "string" => "", "numeric" => 0, "hash" => {}, "array" => [] }
57
+ end
58
+
59
+ it "returns the default value if none is provided" do
60
+ create :string => "foo", :numeric => 3.0
61
+ parse("bar").must == { "string" => "bar", "numeric" => 3.0 }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,212 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/parser'
3
+
4
+ describe Thor::Option do
5
+ def parse(key, value)
6
+ Thor::Option.parse(key, value)
7
+ end
8
+
9
+ def option(name, description=nil, required=false, type=nil, default=nil, banner=nil, group=nil, aliases=[])
10
+ @option ||= Thor::Option.new(name, description, required, type, default, banner, group, aliases)
11
+ end
12
+
13
+ describe "#parse" do
14
+
15
+ describe "with value as a symbol" do
16
+ describe "and symbol is a valid type" do
17
+ it "has type equals to the symbol" do
18
+ parse(:foo, :string).type.must == :string
19
+ parse(:foo, :numeric).type.must == :numeric
20
+ end
21
+
22
+ it "has not default value" do
23
+ parse(:foo, :string).default.must be_nil
24
+ parse(:foo, :numeric).default.must be_nil
25
+ end
26
+ end
27
+
28
+ describe "equals to :required" do
29
+ it "has type equals to :string" do
30
+ parse(:foo, :required).type.must == :string
31
+ end
32
+
33
+ it "has no default value" do
34
+ parse(:foo, :required).default.must be_nil
35
+ end
36
+ end
37
+
38
+ describe "equals to :optional" do
39
+ it "has type equals to :boolean" do
40
+ capture(:stderr){ parse(:foo, :optional).type.must == :boolean }
41
+ end
42
+
43
+ it "has no default value" do
44
+ capture(:stderr){ parse(:foo, :optional).default.must be_nil }
45
+ end
46
+ end
47
+
48
+ describe "and symbol is not a reserved key" do
49
+ it "has type equals to :string" do
50
+ parse(:foo, :bar).type.must == :string
51
+ end
52
+
53
+ it "has no default value" do
54
+ parse(:foo, :bar).default.must be_nil
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "with value as hash" do
60
+ it "has default type :hash" do
61
+ parse(:foo, :a => :b).type.must == :hash
62
+ end
63
+
64
+ it "has default value equals to the hash" do
65
+ parse(:foo, :a => :b).default.must == { :a => :b }
66
+ end
67
+ end
68
+
69
+ describe "with value as array" do
70
+ it "has default type :array" do
71
+ parse(:foo, [:a, :b]).type.must == :array
72
+ end
73
+
74
+ it "has default value equals to the array" do
75
+ parse(:foo, [:a, :b]).default.must == [:a, :b]
76
+ end
77
+ end
78
+
79
+ describe "with value as string" do
80
+ it "has default type :string" do
81
+ parse(:foo, "bar").type.must == :string
82
+ end
83
+
84
+ it "has default value equals to the string" do
85
+ parse(:foo, "bar").default.must == "bar"
86
+ end
87
+ end
88
+
89
+ describe "with value as numeric" do
90
+ it "has default type :numeric" do
91
+ parse(:foo, 2.0).type.must == :numeric
92
+ end
93
+
94
+ it "has default value equals to the numeric" do
95
+ parse(:foo, 2.0).default.must == 2.0
96
+ end
97
+ end
98
+
99
+ describe "with value as boolean" do
100
+ it "has default type :boolean" do
101
+ parse(:foo, true).type.must == :boolean
102
+ parse(:foo, false).type.must == :boolean
103
+ end
104
+
105
+ it "has default value equals to the boolean" do
106
+ parse(:foo, true).default.must == true
107
+ parse(:foo, false).default.must == false
108
+ end
109
+ end
110
+
111
+ describe "with key as a symbol" do
112
+ it "sets the name equals to the key" do
113
+ parse(:foo, true).name.must == "foo"
114
+ end
115
+ end
116
+
117
+ describe "with key as an array" do
118
+ it "sets the first items in the array to the name" do
119
+ parse([:foo, :bar, :baz], true).name.must == "foo"
120
+ end
121
+
122
+ it "sets all other items as aliases" do
123
+ parse([:foo, :bar, :baz], true).aliases.must == [:bar, :baz]
124
+ end
125
+ end
126
+ end
127
+
128
+ it "returns the switch name" do
129
+ option("foo").switch_name.must == "--foo"
130
+ option("--foo").switch_name.must == "--foo"
131
+ end
132
+
133
+ it "returns the human name" do
134
+ option("foo").human_name.must == "foo"
135
+ option("--foo").human_name.must == "foo"
136
+ end
137
+
138
+ it "converts underscores to dashes" do
139
+ option("foo_bar").switch_name.must == "--foo-bar"
140
+ end
141
+
142
+ it "can be required and have default values" do
143
+ option = option("foo", nil, true, :string, "bar")
144
+ option.default.must == "bar"
145
+ option.must be_required
146
+ end
147
+
148
+ it "cannot be required and have type boolean" do
149
+ lambda {
150
+ option("foo", nil, true, :boolean)
151
+ }.must raise_error(ArgumentError, "An option cannot be boolean and required.")
152
+ end
153
+
154
+ it "allows type predicates" do
155
+ parse(:foo, :string).must be_string
156
+ parse(:foo, :boolean).must be_boolean
157
+ parse(:foo, :numeric).must be_numeric
158
+ end
159
+
160
+ it "raises an error on method missing" do
161
+ lambda {
162
+ parse(:foo, :string).unknown?
163
+ }.must raise_error(NoMethodError)
164
+ end
165
+
166
+ describe "#usage" do
167
+
168
+ it "returns usage for string types" do
169
+ parse(:foo, :string).usage.must == "[--foo=FOO]"
170
+ end
171
+
172
+ it "returns usage for numeric types" do
173
+ parse(:foo, :numeric).usage.must == "[--foo=N]"
174
+ end
175
+
176
+ it "returns usage for array types" do
177
+ parse(:foo, :array).usage.must == "[--foo=one two three]"
178
+ end
179
+
180
+ it "returns usage for hash types" do
181
+ parse(:foo, :hash).usage.must == "[--foo=key:value]"
182
+ end
183
+
184
+ it "returns usage for boolean types" do
185
+ parse(:foo, :boolean).usage.must == "[--foo]"
186
+ end
187
+
188
+ it "uses padding when no aliases is given" do
189
+ parse(:foo, :boolean).usage(4).must == " [--foo]"
190
+ end
191
+
192
+ it "uses banner when supplied" do
193
+ option(:foo, nil, false, :string, nil, "BAR").usage.must == "[--foo=BAR]"
194
+ end
195
+
196
+ it "checkes when banner is an empty string" do
197
+ option(:foo, nil, false, :string, nil, "").usage.must == "[--foo]"
198
+ end
199
+
200
+ describe "with required values" do
201
+ it "does not show the usage between brackets" do
202
+ parse(:foo, :required).usage.must == "--foo=FOO"
203
+ end
204
+ end
205
+
206
+ describe "with aliases" do
207
+ it "does not show the usage between brackets" do
208
+ parse([:foo, "-f", "-b"], :required).usage.must == "-f, -b, --foo=FOO"
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,255 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/parser'
3
+
4
+ describe Thor::Options do
5
+ def create(opts)
6
+ opts.each do |key, value|
7
+ opts[key] = Thor::Option.parse(key, value) unless value.is_a?(Thor::Option)
8
+ end
9
+
10
+ @opt = Thor::Options.new(opts)
11
+ end
12
+
13
+ def parse(*args)
14
+ @opt.parse(args.flatten)
15
+ end
16
+
17
+ describe "#to_switches" do
18
+ it "turns true values into a flag" do
19
+ Thor::Options.to_switches(:color => true).must == "--color"
20
+ end
21
+
22
+ it "ignores nil" do
23
+ Thor::Options.to_switches(:color => nil).must == ""
24
+ end
25
+
26
+ it "ignores false" do
27
+ Thor::Options.to_switches(:color => false).must == ""
28
+ end
29
+
30
+ it "writes --name value for anything else" do
31
+ Thor::Options.to_switches(:format => "specdoc").must == '--format "specdoc"'
32
+ end
33
+
34
+ it "joins several values" do
35
+ switches = Thor::Options.to_switches(:color => true, :foo => "bar").split(' ').sort
36
+ switches.must == ['"bar"', "--color", "--foo"]
37
+ end
38
+
39
+ it "accepts arrays" do
40
+ Thor::Options.to_switches(:count => [1,2,3]).must == "--count 1 2 3"
41
+ end
42
+
43
+ it "accepts hashes" do
44
+ Thor::Options.to_switches(:count => {:a => :b}).must == "--count a:b"
45
+ end
46
+ end
47
+
48
+ describe "#parse" do
49
+ it "allows multiple aliases for a given switch" do
50
+ create ["--foo", "--bar", "--baz"] => :string
51
+ parse("--foo", "12")["foo"].must == "12"
52
+ parse("--bar", "12")["foo"].must == "12"
53
+ parse("--baz", "12")["foo"].must == "12"
54
+ end
55
+
56
+ it "allows custom short names" do
57
+ create "-f" => :string
58
+ parse("-f", "12").must == {"f" => "12"}
59
+ end
60
+
61
+ it "allows custom short-name aliases" do
62
+ create ["--bar", "-f"] => :string
63
+ parse("-f", "12").must == {"bar" => "12"}
64
+ end
65
+
66
+ it "accepts conjoined short switches" do
67
+ create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => true
68
+ opts = parse("-fba")
69
+ opts["foo"].must be_true
70
+ opts["bar"].must be_true
71
+ opts["app"].must be_true
72
+ end
73
+
74
+ it "accepts conjoined short switches with input" do
75
+ create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => :required
76
+ opts = parse "-fba", "12"
77
+ opts["foo"].must be_true
78
+ opts["bar"].must be_true
79
+ opts["app"].must == "12"
80
+ end
81
+
82
+ it "returns the default value if none is provided" do
83
+ create :foo => "baz", :bar => :required
84
+ parse("--bar=boom")["foo"].must == "baz"
85
+ end
86
+
87
+ describe "with no input" do
88
+ it "and no switches returns an empty hash" do
89
+ create({})
90
+ parse.must == {}
91
+ end
92
+
93
+ it "and several switches returns an empty hash" do
94
+ create "--foo" => :boolean, "--bar" => :string
95
+ parse.must == {}
96
+ end
97
+
98
+ it "and a required switch raises an error" do
99
+ create "--foo" => :required
100
+ lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "no value provided for required options '--foo'")
101
+ end
102
+ end
103
+
104
+ describe "with one required and one optional switch" do
105
+ before :each do
106
+ create "--foo" => :required, "--bar" => :boolean
107
+ end
108
+
109
+ it "raises an error if the required switch has no argument" do
110
+ lambda { parse("--foo") }.must raise_error(Thor::MalformattedArgumentError)
111
+ end
112
+
113
+ it "raises an error if the required switch isn't given" do
114
+ lambda { parse("--bar") }.must raise_error(Thor::RequiredArgumentMissingError)
115
+ end
116
+
117
+ it "raises an error if the required switch is set to nil" do
118
+ lambda { parse("--no-foo") }.must raise_error(Thor::RequiredArgumentMissingError)
119
+ end
120
+
121
+ it "does not raises an error if the required option has a default value" do
122
+ create :foo => Thor::Option.new("foo", nil, true, :string, "baz"), :bar => :boolean
123
+ lambda { parse("--bar") }.must_not raise_error
124
+ end
125
+ end
126
+
127
+ describe "with :string type" do
128
+ before(:each) do
129
+ create ["--foo", "-f"] => :required
130
+ end
131
+
132
+ it "accepts a switch <value> assignment" do
133
+ parse("--foo", "12")["foo"].must == "12"
134
+ end
135
+
136
+ it "accepts a switch=<value> assignment" do
137
+ parse("-f=12")["foo"].must == "12"
138
+ parse("--foo=12")["foo"].must == "12"
139
+ parse("--foo=bar=baz")["foo"].must == "bar=baz"
140
+ end
141
+
142
+ it "accepts a --no-switch format" do
143
+ create "--foo" => "bar"
144
+ parse("--no-foo")["foo"].must be_nil
145
+ end
146
+
147
+ it "accepts a --switch format on non required types" do
148
+ create "--foo" => "bar"
149
+ parse("--foo")["foo"].must == "foo"
150
+ end
151
+
152
+ it "overwrites earlier values with later values" do
153
+ parse("--foo=bar", "--foo", "12")["foo"].must == "12"
154
+ parse("--foo", "12", "--foo", "13")["foo"].must == "13"
155
+ end
156
+ end
157
+
158
+ describe "with :boolean type" do
159
+ before(:each) do
160
+ create "--foo" => false
161
+ end
162
+
163
+ it "accepts --opt assignment" do
164
+ parse("--foo")["foo"].must == true
165
+ parse("--foo", "--bar")["foo"].must == true
166
+ end
167
+
168
+ it "accepts --opt=value assignment" do
169
+ parse("--foo=true")["foo"].must == true
170
+ parse("--foo=false")["foo"].must == false
171
+ end
172
+
173
+ it "accepts --[no-]opt variant, setting false for value" do
174
+ parse("--no-foo")["foo"].must == false
175
+ end
176
+
177
+ it "accepts --[skip-]opt variant, setting false for value" do
178
+ parse("--skip-foo")["foo"].must == false
179
+ end
180
+
181
+ it "will prefer 'no-opt' variant over inverting 'opt' if explicitly set" do
182
+ create "--no-foo" => true
183
+ parse("--no-foo")["no-foo"].must == true
184
+ end
185
+
186
+ it "will prefer 'skip-opt' variant over inverting 'opt' if explicitly set" do
187
+ create "--skip-foo" => true
188
+ parse("--skip-foo")["skip-foo"].must == true
189
+ end
190
+
191
+ it "accepts inputs in the human name format" do
192
+ create :foo_bar => :boolean
193
+ parse("--foo-bar")["foo_bar"].must == true
194
+ parse("--no-foo-bar")["foo_bar"].must == false
195
+ parse("--skip-foo-bar")["foo_bar"].must == false
196
+ end
197
+ end
198
+
199
+ describe "with :hash type" do
200
+ before(:each) do
201
+ create "--attributes" => :hash
202
+ end
203
+
204
+ it "accepts a switch=<value> assignment" do
205
+ parse("--attributes=name:string", "age:integer")["attributes"].must == {"name" => "string", "age" => "integer"}
206
+ end
207
+
208
+ it "accepts a switch <value> assignment" do
209
+ parse("--attributes", "name:string", "age:integer")["attributes"].must == {"name" => "string", "age" => "integer"}
210
+ end
211
+
212
+ it "must not mix values with other switches" do
213
+ parse("--attributes", "name:string", "age:integer", "--baz", "cool")["attributes"].must == {"name" => "string", "age" => "integer"}
214
+ end
215
+ end
216
+
217
+ describe "with :array type" do
218
+ before(:each) do
219
+ create "--attributes" => :array
220
+ end
221
+
222
+ it "accepts a switch=<value> assignment" do
223
+ parse("--attributes=a", "b", "c")["attributes"].must == ["a", "b", "c"]
224
+ end
225
+
226
+ it "accepts a switch <value> assignment" do
227
+ parse("--attributes", "a", "b", "c")["attributes"].must == ["a", "b", "c"]
228
+ end
229
+
230
+ it "must not mix values with other switches" do
231
+ parse("--attributes", "a", "b", "c", "--baz", "cool")["attributes"].must == ["a", "b", "c"]
232
+ end
233
+ end
234
+
235
+ describe "with :numeric type" do
236
+ before(:each) do
237
+ create "n" => :numeric, "m" => 5
238
+ end
239
+
240
+ it "accepts a -nXY assignment" do
241
+ parse("-n12")["n"].must == 12
242
+ end
243
+
244
+ it "converts values to numeric types" do
245
+ parse("-n", "3", "-m", ".5").must == {"n" => 3, "m" => 0.5}
246
+ end
247
+
248
+ it "raises error when value isn't numeric" do
249
+ lambda { parse("-n", "foo") }.must raise_error(Thor::MalformattedArgumentError,
250
+ "expected numeric value for '-n'; got \"foo\"")
251
+ end
252
+ end
253
+
254
+ end
255
+ end