genspec 0.2.0.prerails3.2 → 0.2.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/.document +5 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +93 -0
- data/README.rdoc +161 -84
- data/Rakefile +8 -88
- data/genspec.gemspec +17 -63
- data/lib/genspec/generator_example_group.rb +109 -2
- data/lib/genspec/matchers/base.rb +54 -59
- data/lib/genspec/matchers/generation_method_matcher.rb +74 -17
- data/lib/genspec/matchers/output_matcher.rb +1 -1
- data/lib/genspec/matchers/result_matcher.rb +13 -3
- data/lib/genspec/matchers.rb +20 -19
- data/lib/genspec/shell.rb +19 -74
- data/lib/genspec/version.rb +12 -0
- data/lib/genspec.rb +15 -4
- data/spec/generators/migration_spec.rb +6 -4
- data/spec/generators/question_spec.rb +31 -0
- data/spec/generators/test_rails3_spec.rb +18 -3
- data/spec/spec_helper.rb +21 -5
- data/spec/support/generators/question/question_generator.rb +18 -0
- data/spec/support/generators/test_rails3/test_rails3_generator.rb +10 -2
- metadata +83 -27
- data/VERSION +0 -1
- data/pkg/genspec-0.1.1.gem +0 -0
- data/pkg/genspec-0.2.0.prerails3.1.gem +0 -0
- data/pkg/genspec-0.2.0.prerails3.2.gem +0 -0
- data/spec/environment_spec.rb +0 -18
@@ -16,10 +16,21 @@ module GenSpec
|
|
16
16
|
# pass :object => true at the end;
|
17
17
|
#
|
18
18
|
# Ex:
|
19
|
-
#
|
19
|
+
#
|
20
|
+
# with_args '--orm', 'active_record' do
|
21
|
+
# it "should use activerecord" do
|
22
|
+
# # . . .
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# with_args '--size', 5, :object => true do
|
27
|
+
# # . . .
|
28
|
+
# end
|
29
|
+
#
|
20
30
|
def with_args(*args, &block)
|
21
31
|
options = args.extract_options!
|
22
32
|
args = args.flatten.collect { |c| c.to_s } unless options[:object]
|
33
|
+
|
23
34
|
if block_given?
|
24
35
|
context "with arguments #{args.inspect}" do
|
25
36
|
with_args(args, options)
|
@@ -30,6 +41,68 @@ module GenSpec
|
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
44
|
+
# Sets the input stream for this generator.
|
45
|
+
#
|
46
|
+
# Ex:
|
47
|
+
#
|
48
|
+
# with_input <<-end_input do
|
49
|
+
# y
|
50
|
+
# n
|
51
|
+
# a
|
52
|
+
# end_input
|
53
|
+
# it "should overwrite, then skip, then overwrite all" do
|
54
|
+
# # . . .
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
def with_input(string, &block)
|
59
|
+
if block_given?
|
60
|
+
context "with input string #{string.inspect}" do
|
61
|
+
with_input string
|
62
|
+
instance_eval &block
|
63
|
+
end
|
64
|
+
else
|
65
|
+
metadata[:generator_input] = string
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Executes some code within the generator's source root
|
70
|
+
# prior to the generator actually running. Useful for
|
71
|
+
# setting up fixtures.
|
72
|
+
#
|
73
|
+
# Ex:
|
74
|
+
#
|
75
|
+
# within_source_root do
|
76
|
+
# touch "Gemfile"
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# Optionally, the block may receive a single argument,
|
80
|
+
# which is the full path to the temporary directory
|
81
|
+
# representing the source root:
|
82
|
+
#
|
83
|
+
# within_source_root do |tempdir|
|
84
|
+
# # . . .
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
def within_source_root(&block)
|
88
|
+
metadata[:generator_init_block] = block
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns an array of all init blocks from the topmost context down to this
|
92
|
+
# one, in that order. These blocks will be executed sequentially prior to
|
93
|
+
# each run of the generator.
|
94
|
+
def generator_init_blocks
|
95
|
+
result = []
|
96
|
+
result.concat superclass.generator_init_blocks if genspec_subclass?
|
97
|
+
result << metadata[:generator_init_block] if metadata[:generator_init_block]
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the generator arguments to be used for this context. If this context doesn't
|
102
|
+
# have any generator arguments, its superclass is checked, and so on until either the
|
103
|
+
# parent isn't a GenSpec or a set of arguments is found. Only the closest argument
|
104
|
+
# set is used; any sets specified above the discovered argument set are
|
105
|
+
# ignored.
|
33
106
|
def generator_args
|
34
107
|
return metadata[:generator_args] if metadata[:generator_args]
|
35
108
|
|
@@ -40,16 +113,45 @@ module GenSpec
|
|
40
113
|
end
|
41
114
|
end
|
42
115
|
|
116
|
+
# Returns the input stream to be used for this context. If this context doesn't
|
117
|
+
# have an input stream, its superclass is checked, and so on until either the
|
118
|
+
# parent isn't a GenSpec or an input stream is found. Only the closest input
|
119
|
+
# stream is used; any streams specified above the discovered input stream are
|
120
|
+
# ignored.
|
121
|
+
def generator_input
|
122
|
+
return metadata[:generator_input] if metadata[:generator_input]
|
123
|
+
|
124
|
+
metadata[:generator_input] = if genspec_subclass?
|
125
|
+
superclass.generator_input
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
43
131
|
alias with_arguments with_args
|
44
132
|
alias generator_arguments generator_args
|
45
133
|
|
134
|
+
# A hash containing the following:
|
135
|
+
#
|
136
|
+
# :described - the generator to be tested, or the string/symbol representing it
|
137
|
+
# :args - any arguments to be used when invoking the generator
|
138
|
+
# :input - a string to be used as an input stream, or nil
|
139
|
+
# :init_blocks - an array of blocks to be invoked prior to running the generator
|
140
|
+
#
|
141
|
+
# This hash represents the +subject+ of the spec and this is the object that will
|
142
|
+
# ultimately be passed into the GenSpec matchers.
|
143
|
+
#
|
46
144
|
def generator_descriptor
|
47
145
|
{
|
48
146
|
:described => target_generator,
|
49
|
-
:args => generator_args
|
147
|
+
:args => generator_args,
|
148
|
+
:input => generator_input,
|
149
|
+
:init_blocks => generator_init_blocks
|
50
150
|
}
|
51
151
|
end
|
52
152
|
|
153
|
+
# Traverses up the context tree to find the topmost description, which represents
|
154
|
+
# the controller to be tested or the string/symbol representing it.
|
53
155
|
def target_generator
|
54
156
|
if genspec_subclass?
|
55
157
|
superclass.target_generator
|
@@ -58,6 +160,11 @@ module GenSpec
|
|
58
160
|
end
|
59
161
|
end
|
60
162
|
|
163
|
+
# Returns true if this object's superclass is also a GenSpec.
|
164
|
+
#
|
165
|
+
# When a context is created, rspec creates a class inheriting from the context's
|
166
|
+
# parent. Therefore, this method can be used to recurse up to the highest-level
|
167
|
+
# spec that still tests a generator.
|
61
168
|
def genspec_subclass?
|
62
169
|
superclass.include?(GenSpec::GeneratorExampleGroup)
|
63
170
|
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module GenSpec
|
2
2
|
module Matchers
|
3
3
|
class Base
|
4
|
-
attr_reader :block, :generator, :args, :
|
5
|
-
delegate :source_root, :to => :generator
|
4
|
+
attr_reader :block, :generator, :args, :init_blocks
|
6
5
|
attr_reader :destination_root
|
7
6
|
attr_accessor :error
|
8
7
|
|
8
|
+
def source_root
|
9
|
+
generator.source_root
|
10
|
+
end
|
11
|
+
|
9
12
|
def initialize(&block)
|
10
13
|
@block = block if block_given?
|
11
14
|
@matched = false
|
@@ -18,20 +21,27 @@ module GenSpec
|
|
18
21
|
def matches?(generator)
|
19
22
|
@described = generator[:described]
|
20
23
|
@args = generator[:args]
|
24
|
+
@shell = GenSpec::Shell.new("", generator[:input] || "")
|
25
|
+
@init_blocks = generator[:init_blocks]
|
21
26
|
|
22
|
-
if @described.kind_of?(
|
23
|
-
@generator =
|
27
|
+
if @described.kind_of?(Class)
|
28
|
+
@generator = @described
|
24
29
|
else
|
25
|
-
|
30
|
+
if defined?(Rails)
|
31
|
+
@generator = Rails::Generators.find_by_namespace(@described)
|
32
|
+
else
|
33
|
+
@generator = Thor::Util.find_by_namespace(@described)
|
34
|
+
end
|
26
35
|
end
|
27
36
|
|
28
37
|
raise "Could not find generator: #{@described.inspect}" unless @generator
|
29
38
|
|
30
|
-
localize_generator!
|
31
39
|
inject_error_handlers!
|
32
40
|
invoking
|
33
41
|
invoke
|
34
42
|
matched?
|
43
|
+
ensure
|
44
|
+
complete
|
35
45
|
end
|
36
46
|
|
37
47
|
def matched?
|
@@ -47,7 +57,13 @@ module GenSpec
|
|
47
57
|
end
|
48
58
|
|
49
59
|
protected
|
60
|
+
# callback fired after matching process is complete, regardless of success, failure
|
61
|
+
# or error
|
62
|
+
def complete
|
63
|
+
end
|
64
|
+
|
50
65
|
# callback which fires just before a generator has been invoked.
|
66
|
+
# Allows matchers to inject whatever hooks they need into the generator.
|
51
67
|
def invoking
|
52
68
|
end
|
53
69
|
|
@@ -56,16 +72,6 @@ module GenSpec
|
|
56
72
|
def generated
|
57
73
|
end
|
58
74
|
|
59
|
-
def temporary_root
|
60
|
-
Dir.mktmpdir do |dir|
|
61
|
-
# need to copy a few files for some methods, ie route_resources
|
62
|
-
FileUtils.touch(File.join(dir, "Gemfile"))
|
63
|
-
|
64
|
-
# all set.
|
65
|
-
yield dir
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
75
|
def spec_file_contents(filename)
|
70
76
|
if @block
|
71
77
|
content = File.read(filename)
|
@@ -73,28 +79,16 @@ module GenSpec
|
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
76
|
-
|
82
|
+
def shell
|
83
|
+
@shell
|
84
|
+
end
|
85
|
+
|
77
86
|
# Causes errors not to be raised if a generator fails. Useful for testing output,
|
78
87
|
# rather than results.
|
79
88
|
def silence_errors!
|
80
89
|
@errors_silenced = true
|
81
90
|
end
|
82
91
|
|
83
|
-
def silence_thor!
|
84
|
-
@generator.instance_eval do
|
85
|
-
alias thor_method_added method_added
|
86
|
-
|
87
|
-
# to silence callbacks and errors about reserved keywords
|
88
|
-
def method_added(meth)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
yield
|
93
|
-
|
94
|
-
# un-silence errors and callbacks
|
95
|
-
@generator.instance_eval { alias thor_method_added method_added }
|
96
|
-
end
|
97
|
-
|
98
92
|
private
|
99
93
|
def check_for_errors
|
100
94
|
# generation is complete - check for errors and re-raise it if it's there
|
@@ -102,47 +96,48 @@ module GenSpec
|
|
102
96
|
end
|
103
97
|
|
104
98
|
def invoke
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
99
|
+
Dir.mktmpdir do |tempdir|
|
100
|
+
FileUtils.chdir tempdir do
|
101
|
+
init_blocks.each do |block|
|
102
|
+
block.call(tempdir)
|
103
|
+
end
|
104
|
+
|
105
|
+
@destination_root = tempdir
|
106
|
+
stdout, stderr, stdin = $stdout, $stderr, $stdin
|
107
|
+
$stdout, $stderr, $stdin = shell.output, shell.output, shell.input
|
108
|
+
|
109
|
+
begin
|
110
|
+
@generator.start(@args || [], {
|
111
|
+
:shell => @shell,
|
112
|
+
:destination_root => destination_root
|
113
|
+
})
|
114
|
+
ensure
|
115
|
+
$stdout, $stderr, $stdin = stdout, stderr, stdin
|
116
|
+
end
|
117
|
+
|
118
|
+
check_for_errors
|
119
|
+
generated
|
120
|
+
end
|
110
121
|
end
|
111
122
|
end
|
112
123
|
|
113
124
|
def inject_error_handlers!
|
114
|
-
|
115
|
-
|
125
|
+
interceptor = self
|
126
|
+
@generator.class_eval do
|
127
|
+
no_tasks do
|
116
128
|
def invoke_with_genspec_error_handler(*names, &block)
|
117
129
|
invoke_without_genspec_error_handler(*names, &block)
|
118
130
|
rescue Thor::Error => err
|
119
|
-
self.class.interceptor.error = err
|
131
|
+
# self.class.interceptor.error = err
|
132
|
+
interceptor.error = err
|
120
133
|
raise err
|
121
134
|
end
|
122
|
-
|
135
|
+
|
123
136
|
alias invoke_without_genspec_error_handler invoke
|
124
137
|
alias invoke invoke_with_genspec_error_handler
|
125
138
|
end
|
126
139
|
end
|
127
140
|
end
|
128
|
-
|
129
|
-
def localize_generator!
|
130
|
-
# subclass the generator in question so that aliasing its methods doesn't
|
131
|
-
# impact the root generator (which would be a Bad Thing for other specs)
|
132
|
-
gen = @generator
|
133
|
-
generator = Class.new(gen)
|
134
|
-
# we have to force the name in order to avoid nil errors within Thor.
|
135
|
-
generator.instance_eval "def self.name; #{gen.name.inspect}; end"
|
136
|
-
@generator = generator
|
137
|
-
|
138
|
-
|
139
|
-
# add self as the "interceptor" so that our generator's wrapper methods can
|
140
|
-
# gain access to this object.
|
141
|
-
def @generator.interceptor; @interceptor; end
|
142
|
-
def @generator.interceptor=(a); @interceptor = a; end
|
143
|
-
|
144
|
-
@generator.interceptor = self
|
145
|
-
end
|
146
141
|
end
|
147
142
|
end
|
148
143
|
end
|
@@ -1,4 +1,23 @@
|
|
1
1
|
class GenSpec::Matchers::GenerationMethodMatcher < GenSpec::Matchers::Base
|
2
|
+
# The modules whose public instance methods will be converted into GenSpec matchers.
|
3
|
+
# See #generation_methods for details.
|
4
|
+
#
|
5
|
+
# By default, this includes all of the following:
|
6
|
+
#
|
7
|
+
# * +Thor::Actions+
|
8
|
+
# * +Rails::Generators::Actions+
|
9
|
+
# * +Rails::Generators::Migration+
|
10
|
+
#
|
11
|
+
# If Rails has not been loaded, (e.g. you are testing Thor generators, not Rails generators),
|
12
|
+
# the Rails modules are silently ignored.
|
13
|
+
#
|
14
|
+
# You can add any additional modules to this list. Note that you should list them
|
15
|
+
# in the form of a String representing the module name, rather than adding the modules
|
16
|
+
# themselves. This allows you to add them prior to actually load them.
|
17
|
+
#
|
18
|
+
# This will only take effect _before_ the specs have been executed; it is best done from
|
19
|
+
# within the +spec_helper.rb+ file during the load process.
|
20
|
+
#
|
2
21
|
GENERATION_CLASSES = [ 'Thor::Actions', 'Rails::Generators::Actions', 'Rails::Generators::Migration' ]
|
3
22
|
|
4
23
|
attr_reader :method_name, :method_args
|
@@ -46,42 +65,80 @@ class GenSpec::Matchers::GenerationMethodMatcher < GenSpec::Matchers::Base
|
|
46
65
|
|
47
66
|
protected
|
48
67
|
def invoking
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
68
|
+
method_name = self.method_name
|
69
|
+
interceptor = self
|
70
|
+
generator.class_eval do
|
71
|
+
no_tasks do
|
72
|
+
define_method :"#{method_name}_with_intercept" do |*argus, &block|
|
73
|
+
expected_args = interceptor.method_args
|
53
74
|
if expected_args.length > 0
|
54
75
|
actual_args = argus[0...expected_args.length]
|
55
76
|
if actual_args == expected_args
|
56
|
-
|
77
|
+
interceptor.match!
|
57
78
|
else
|
58
|
-
|
79
|
+
# we've already matched the method, and there are no expected args.
|
80
|
+
interceptor.report_actual_args actual_args
|
59
81
|
end
|
60
82
|
else
|
61
|
-
|
62
|
-
self.class.interceptor.match!
|
83
|
+
interceptor.match!
|
63
84
|
end
|
64
|
-
|
65
|
-
#{method_name}_without_intercept
|
85
|
+
|
86
|
+
send(:"#{method_name}_without_intercept", *argus, &block)
|
66
87
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
88
|
+
|
89
|
+
alias_method_chain :"#{method_name}", :intercept
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def complete
|
95
|
+
# we couldn't subclass the generator anonymously because this dirties the
|
96
|
+
# rails generator search process. Instead we'll rely on manually de-aliasing
|
97
|
+
# the method being monitored.
|
98
|
+
method_name = self.method_name
|
99
|
+
generator.class_eval do
|
100
|
+
no_tasks do
|
101
|
+
alias_method :"#{method_name}", :"#{method_name}_without_intercept"
|
102
|
+
end
|
103
|
+
end
|
70
104
|
end
|
71
105
|
|
72
106
|
public
|
73
107
|
class << self
|
108
|
+
# Returns all public instance methods found in the modules listed in
|
109
|
+
# GENERATION_CLASSES. This is the list of methods that will be converted
|
110
|
+
# into matchers, which can be used like so:
|
111
|
+
#
|
112
|
+
# subject.should create_file(. . .)
|
113
|
+
#
|
114
|
+
# See also GENERATION_CLASSES
|
115
|
+
#
|
74
116
|
def generation_methods
|
75
117
|
GENERATION_CLASSES.inject([]) do |arr, mod|
|
76
|
-
|
118
|
+
if mod.kind_of?(String)
|
119
|
+
next arr if !defined?(Rails) && mod =~ /^Rails/
|
120
|
+
mod = mod.constantize
|
121
|
+
end
|
77
122
|
arr.concat mod.public_instance_methods.collect { |i| i.to_s }.reject { |i| i =~ /=/ }
|
78
123
|
arr
|
79
|
-
end
|
124
|
+
end.uniq.sort
|
80
125
|
end
|
81
126
|
|
127
|
+
# called from GenSpec::Matchers#call_action
|
128
|
+
#
|
129
|
+
# example:
|
130
|
+
# subject.should call_action(:create_file, ...)
|
131
|
+
#
|
132
|
+
# equivalent to:
|
133
|
+
# subject.should GenSpec::Matchers::GenerationMethodMatcher.for_method(:create_file, ...)
|
134
|
+
#
|
82
135
|
def for_method(which, *args, &block)
|
83
|
-
if generation_methods.include?(which.to_s)
|
84
|
-
|
136
|
+
if generation_methods.include?(which.to_s)
|
137
|
+
new(which, *args, &block)
|
138
|
+
else
|
139
|
+
raise "Could not find a matcher for '#{which.inspect}'!\n\n" \
|
140
|
+
"If this is a custom action, try adding the Thor Action module to GenSpec:\n\n" \
|
141
|
+
" GenSpec::Matchers::GenerationMethodMatcher::GENERATION_CLASSES << 'My::Actions'"
|
85
142
|
end
|
86
143
|
end
|
87
144
|
end
|
@@ -9,10 +9,20 @@ module GenSpec
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def generated
|
12
|
-
|
13
|
-
|
12
|
+
if filename
|
13
|
+
path = File.join(destination_root, filename)
|
14
|
+
if File.exist?(path)
|
15
|
+
match!
|
16
|
+
spec_file_contents(path)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
# there was no error, so in the context of
|
20
|
+
# "should generate", it most certainly
|
21
|
+
# generated.
|
14
22
|
match!
|
15
|
-
|
23
|
+
if block
|
24
|
+
block.call
|
25
|
+
end
|
16
26
|
end
|
17
27
|
end
|
18
28
|
|
data/lib/genspec/matchers.rb
CHANGED
@@ -7,21 +7,26 @@ module GenSpec
|
|
7
7
|
module Matchers
|
8
8
|
# Valid types: :dependency, :class_collisions, :file, :template, :complex_template, :directory, :readme,
|
9
9
|
# :migration_template, :route_resources
|
10
|
-
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
# subject.should generate(:file, ...)
|
13
|
+
# subject.should generate("vendor/plugins/will_paginate/init.rb")
|
14
|
+
#
|
15
|
+
def generate(kind = nil, *args, &block)
|
11
16
|
if kind.kind_of?(Symbol)
|
17
|
+
# subject.should generate(:file, ...)
|
12
18
|
call_action(kind, *args, &block)
|
13
19
|
else
|
20
|
+
# subject.should generate("vendor/plugins/will_paginate/init.rb")
|
14
21
|
GenSpec::Matchers::ResultMatcher.new(kind, &block)
|
15
22
|
end
|
16
23
|
end
|
17
24
|
|
25
|
+
# ex:
|
26
|
+
# subject.should call_action(:create_file, ...)
|
27
|
+
#
|
18
28
|
def call_action(kind, *args, &block)
|
19
|
-
|
20
|
-
raise "Could not find a matcher for '#{kind.inspect}'!\n\n" \
|
21
|
-
"If this is a custom action, try adding the Thor Action module to GenSpec:\n\n" \
|
22
|
-
"GenSpec::Matchers::GenerationMethodMatcher::GENERATION_CLASSES << 'My::Actions'"
|
23
|
-
end
|
24
|
-
matcher
|
29
|
+
GenSpec::Matchers::GenerationMethodMatcher.for_method(kind, *args, &block)
|
25
30
|
end
|
26
31
|
|
27
32
|
# This tests the content sent to the command line, instead of the generated product.
|
@@ -33,10 +38,16 @@ module GenSpec
|
|
33
38
|
class << self
|
34
39
|
def add_shorthand_methods(base)
|
35
40
|
instance_methods = base.instance_methods.collect { |m| m.to_s }
|
41
|
+
|
42
|
+
# ex:
|
43
|
+
# subject.should create_file(...)
|
44
|
+
# equivalent to:
|
45
|
+
# subject.should call_action(:create_file, ...)
|
46
|
+
|
36
47
|
GenSpec::Matchers::GenerationMethodMatcher.generation_methods.each do |method_name|
|
37
48
|
# don't overwrite existing methods. since the user expects this to fire FIRST,
|
38
|
-
# it's as if this method's been "overridden".
|
39
|
-
next if instance_methods.include?(method_name)
|
49
|
+
# it's as if this method's been "overridden". See #included and #extended.
|
50
|
+
next if instance_methods.include?(method_name.to_s)
|
40
51
|
base.class_eval <<-end_code
|
41
52
|
def #{method_name}(*args, &block) # def create_file(*args, &block)
|
42
53
|
call_action(#{method_name.inspect}, *args, &block) # call_action('create_file', *args, &block)
|
@@ -44,16 +55,6 @@ module GenSpec
|
|
44
55
|
end_code
|
45
56
|
end
|
46
57
|
end
|
47
|
-
|
48
|
-
# this is to delay definition of the generation method matchers (like #create_file) until
|
49
|
-
# after initialization, in order to facilitate custom Thor actions.
|
50
|
-
def included(base)
|
51
|
-
add_shorthand_methods(base)
|
52
|
-
end
|
53
|
-
|
54
|
-
def extended(base)
|
55
|
-
add_shorthand_methods(class << base; self; end)
|
56
|
-
end
|
57
58
|
end
|
58
59
|
end
|
59
60
|
end
|
data/lib/genspec/shell.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
|
+
require 'thor/shell/basic'
|
2
|
+
|
1
3
|
module GenSpec
|
2
4
|
# Just like a Thor::Shell::Basic except that input and output are both redirected to
|
3
5
|
# the specified streams. By default, these are initialized to instances of StringIO.
|
4
6
|
class Shell < Thor::Shell::Basic
|
5
|
-
attr_accessor :
|
7
|
+
attr_accessor :stdin, :stdout, :stderr
|
8
|
+
alias_method :input, :stdin
|
9
|
+
alias_method :input=, :stdin=
|
10
|
+
alias_method :output, :stdout
|
11
|
+
alias_method :output=, :stdout=
|
12
|
+
|
13
|
+
def ask(statement, color = nil)
|
14
|
+
say "#{statement} ", color
|
15
|
+
response = stdin.gets
|
16
|
+
if response
|
17
|
+
response.strip
|
18
|
+
else
|
19
|
+
raise "Asked '#{statement}', but input.gets returned nil!"
|
20
|
+
end
|
21
|
+
end
|
6
22
|
|
7
|
-
def initialize(output="", input="")
|
23
|
+
def initialize(output = "", input = "")
|
8
24
|
super()
|
9
25
|
new(output, input)
|
10
26
|
end
|
@@ -13,81 +29,10 @@ module GenSpec
|
|
13
29
|
def new(output="", input="")
|
14
30
|
init_stream(:output, output)
|
15
31
|
init_stream(:input, input)
|
32
|
+
@stderr = @stdout
|
16
33
|
self
|
17
34
|
end
|
18
|
-
|
19
|
-
# Ask something to the user and receives a response.
|
20
|
-
#
|
21
|
-
# ==== Example
|
22
|
-
# ask("What is your name?")
|
23
|
-
#
|
24
|
-
def ask(statement, color=nil)
|
25
|
-
say("#{statement} ", color)
|
26
|
-
input.gets.strip
|
27
|
-
end
|
28
|
-
|
29
|
-
# Say (print) something to the user. If the sentence ends with a whitespace
|
30
|
-
# or tab character, a new line is not appended (print + flush). Otherwise
|
31
|
-
# are passed straight to puts (behavior got from Highline).
|
32
|
-
#
|
33
|
-
# ==== Example
|
34
|
-
# say("I know you knew that.")
|
35
|
-
#
|
36
|
-
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
|
37
|
-
message = message.to_s
|
38
|
-
message = set_color(message, color) if color
|
39
|
-
|
40
|
-
if force_new_line
|
41
|
-
output.puts(message)
|
42
|
-
else
|
43
|
-
output.print(message)
|
44
|
-
output.flush
|
45
|
-
end
|
46
|
-
end
|
47
35
|
|
48
|
-
# Prints a table.
|
49
|
-
#
|
50
|
-
# ==== Parameters
|
51
|
-
# Array[Array[String, String, ...]]
|
52
|
-
#
|
53
|
-
# ==== Options
|
54
|
-
# ident<Integer>:: Ident the first column by ident value.
|
55
|
-
#
|
56
|
-
def print_table(table, options={})
|
57
|
-
return if table.empty?
|
58
|
-
|
59
|
-
formats, ident = [], options[:ident].to_i
|
60
|
-
options[:truncate] = terminal_width if options[:truncate] == true
|
61
|
-
|
62
|
-
0.upto(table.first.length - 2) do |i|
|
63
|
-
maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size
|
64
|
-
formats << "%-#{maxima + 2}s"
|
65
|
-
end
|
66
|
-
|
67
|
-
formats[0] = formats[0].insert(0, " " * ident)
|
68
|
-
formats << "%s"
|
69
|
-
|
70
|
-
table.each do |row|
|
71
|
-
sentence = ""
|
72
|
-
|
73
|
-
row.each_with_index do |column, i|
|
74
|
-
sentence << formats[i] % column.to_s
|
75
|
-
end
|
76
|
-
|
77
|
-
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
|
78
|
-
output.puts sentence
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Called if something goes wrong during the execution. This is used by Thor
|
83
|
-
# internally and should not be used inside your scripts. If someone went
|
84
|
-
# wrong, you can always raise an exception. If you raise a Thor::Error, it
|
85
|
-
# will be rescued and wrapped in the method below.
|
86
|
-
#
|
87
|
-
def error(statement)
|
88
|
-
output.puts statement
|
89
|
-
end
|
90
|
-
|
91
36
|
private
|
92
37
|
def init_stream(which, value)
|
93
38
|
if value.kind_of?(String)
|