thor 0.9.9 → 0.11.5

Sign up to get free protection for your applications and to get access to all the features.
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