transpec 0.2.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +10 -0
- data/README.md +111 -56
- data/README.md.erb +117 -62
- data/lib/transpec/ast/node.rb +41 -0
- data/lib/transpec/base_rewriter.rb +55 -0
- data/lib/transpec/cli.rb +43 -153
- data/lib/transpec/configuration.rb +13 -9
- data/lib/transpec/{rewriter.rb → converter.rb} +44 -71
- data/lib/transpec/dynamic_analyzer/rewriter.rb +94 -0
- data/lib/transpec/dynamic_analyzer/runtime_data.rb +27 -0
- data/lib/transpec/dynamic_analyzer.rb +166 -0
- data/lib/transpec/file_finder.rb +53 -0
- data/lib/transpec/option_parser.rb +166 -0
- data/lib/transpec/{context.rb → static_context_inspector.rb} +2 -2
- data/lib/transpec/syntax/be_close.rb +7 -9
- data/lib/transpec/syntax/double.rb +6 -10
- data/lib/transpec/syntax/expect.rb +35 -0
- data/lib/transpec/syntax/have.rb +195 -0
- data/lib/transpec/syntax/method_stub.rb +22 -27
- data/lib/transpec/syntax/mixin/allow_no_message.rb +73 -0
- data/lib/transpec/syntax/mixin/any_instance.rb +22 -0
- data/lib/transpec/syntax/mixin/expectizable.rb +26 -0
- data/lib/transpec/syntax/mixin/have_matcher.rb +23 -0
- data/lib/transpec/syntax/mixin/monkey_patch.rb +37 -0
- data/lib/transpec/syntax/mixin/send.rb +109 -0
- data/lib/transpec/syntax/{matcher.rb → operator_matcher.rb} +27 -14
- data/lib/transpec/syntax/raise_error.rb +6 -10
- data/lib/transpec/syntax/rspec_configure.rb +29 -28
- data/lib/transpec/syntax/should.rb +45 -15
- data/lib/transpec/syntax/should_receive.rb +44 -16
- data/lib/transpec/syntax.rb +29 -21
- data/lib/transpec/util.rb +12 -2
- data/lib/transpec/version.rb +3 -3
- data/spec/spec_helper.rb +8 -6
- data/spec/support/cache_helper.rb +50 -0
- data/spec/support/shared_context.rb +49 -1
- data/spec/transpec/ast/node_spec.rb +65 -0
- data/spec/transpec/cli_spec.rb +33 -242
- data/spec/transpec/commit_message_spec.rb +2 -2
- data/spec/transpec/configuration_spec.rb +12 -8
- data/spec/transpec/{rewriter_spec.rb → converter_spec.rb} +198 -148
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +183 -0
- data/spec/transpec/dynamic_analyzer_spec.rb +164 -0
- data/spec/transpec/file_finder_spec.rb +118 -0
- data/spec/transpec/option_parser_spec.rb +185 -0
- data/spec/transpec/{context_spec.rb → static_context_inspector_spec.rb} +27 -12
- data/spec/transpec/syntax/be_close_spec.rb +8 -4
- data/spec/transpec/syntax/double_spec.rb +105 -12
- data/spec/transpec/syntax/expect_spec.rb +83 -0
- data/spec/transpec/syntax/have_spec.rb +599 -0
- data/spec/transpec/syntax/method_stub_spec.rb +276 -115
- data/spec/transpec/syntax/{matcher_spec.rb → operator_matcher_spec.rb} +277 -98
- data/spec/transpec/syntax/raise_error_spec.rb +92 -46
- data/spec/transpec/syntax/should_receive_spec.rb +298 -92
- data/spec/transpec/syntax/should_spec.rb +230 -44
- data/spec/transpec/util_spec.rb +2 -9
- data/tasks/lib/transpec_demo.rb +1 -1
- data/tasks/lib/transpec_test.rb +5 -7
- data/tasks/test.rake +5 -1
- data/transpec.gemspec +1 -1
- metadata +46 -22
- data/lib/transpec/syntax/able_to_allow_no_message.rb +0 -73
- data/lib/transpec/syntax/able_to_target_any_instance.rb +0 -24
- data/lib/transpec/syntax/expectizable.rb +0 -27
- data/lib/transpec/syntax/send_node_syntax.rb +0 -57
data/lib/transpec/cli.rb
CHANGED
@@ -1,27 +1,17 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require 'transpec/configuration'
|
4
3
|
require 'transpec/commit_message'
|
5
|
-
require 'transpec/
|
4
|
+
require 'transpec/configuration'
|
5
|
+
require 'transpec/converter'
|
6
|
+
require 'transpec/dynamic_analyzer'
|
7
|
+
require 'transpec/file_finder'
|
8
|
+
require 'transpec/option_parser'
|
6
9
|
require 'transpec/report'
|
7
|
-
require 'transpec/rewriter'
|
8
|
-
require 'transpec/version'
|
9
|
-
require 'optparse'
|
10
|
-
require 'find'
|
11
10
|
require 'rainbow'
|
12
11
|
|
13
12
|
module Transpec
|
14
13
|
class CLI
|
15
|
-
|
16
|
-
expect_to_matcher: :convert_to_expect_to_matcher=,
|
17
|
-
expect_to_receive: :convert_to_expect_to_receive=,
|
18
|
-
allow_to_receive: :convert_to_allow_to_receive=,
|
19
|
-
deprecated: :replace_deprecated_method=
|
20
|
-
}
|
21
|
-
|
22
|
-
attr_reader :configuration, :forced, :generates_commit_message
|
23
|
-
alias_method :forced?, :forced
|
24
|
-
alias_method :generates_commit_message?, :generates_commit_message
|
14
|
+
attr_reader :configuration
|
25
15
|
|
26
16
|
def self.run(args = ARGV)
|
27
17
|
new.run(args)
|
@@ -29,172 +19,67 @@ module Transpec
|
|
29
19
|
|
30
20
|
def initialize
|
31
21
|
@configuration = Configuration.new
|
32
|
-
@forced = false
|
33
|
-
@generates_commit_message = false
|
34
22
|
@report = Report.new
|
35
23
|
end
|
36
24
|
|
37
25
|
def run(args)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
target_files(base_paths).each do |file_path|
|
45
|
-
process_file(file_path)
|
26
|
+
begin
|
27
|
+
paths = OptionParser.new(@configuration).parse(args)
|
28
|
+
fail_if_should_not_continue!
|
29
|
+
rescue => error
|
30
|
+
warn error.message
|
31
|
+
return false
|
46
32
|
end
|
47
33
|
|
34
|
+
process(paths)
|
35
|
+
|
48
36
|
display_summary
|
49
|
-
generate_commit_message if
|
37
|
+
generate_commit_message if @configuration.generate_commit_message?
|
38
|
+
display_final_guide
|
50
39
|
|
51
40
|
true
|
52
|
-
rescue => error
|
53
|
-
warn error.message
|
54
|
-
false
|
55
41
|
end
|
56
42
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
rewriter = Rewriter.new(@configuration, @report)
|
61
|
-
rewriter.rewrite_file!(file_path)
|
62
|
-
|
63
|
-
@report.invalid_context_errors.concat(rewriter.invalid_context_errors)
|
43
|
+
def process(paths)
|
44
|
+
runtime_data = nil
|
64
45
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
warn_syntax_error(error)
|
71
|
-
end
|
72
|
-
|
73
|
-
# rubocop:disable MethodLength
|
74
|
-
def parse_options(args)
|
75
|
-
parser = OptionParser.new
|
76
|
-
parser.banner = "Usage: transpec [options] [files or directories]\n\n"
|
77
|
-
|
78
|
-
parser.on(
|
79
|
-
'-f', '--force',
|
80
|
-
'Force processing even if the current Git',
|
81
|
-
'repository is not clean.'
|
82
|
-
) do
|
83
|
-
@forced = true
|
84
|
-
end
|
85
|
-
|
86
|
-
parser.on(
|
87
|
-
'-m', '--commit-message',
|
88
|
-
'Generate commit message that describes',
|
89
|
-
'conversion summary. Only Git is supported.'
|
90
|
-
) do
|
91
|
-
unless Git.inside_of_repository?
|
92
|
-
fail '-m/--commit-message option is specified but not in a Git repository'
|
93
|
-
end
|
94
|
-
|
95
|
-
@generates_commit_message = true
|
96
|
-
end
|
97
|
-
|
98
|
-
parser.on(
|
99
|
-
'-d', '--disable TYPE[,TYPE...]',
|
100
|
-
'Disable specific conversions.',
|
101
|
-
'Available conversion types:',
|
102
|
-
' expect_to_matcher (from `should`)',
|
103
|
-
' expect_to_receive (from `should_receive`)',
|
104
|
-
' allow_to_receive (from `stub`)',
|
105
|
-
' deprecated (e.g. from `stub!` to `stub`)',
|
106
|
-
'These are all enabled by default.'
|
107
|
-
) do |types|
|
108
|
-
types.split(',').each do |type|
|
109
|
-
config_attr = CONFIG_ATTRS_FOR_CLI_TYPES[type.to_sym]
|
110
|
-
fail ArgumentError, "Unknown conversion type #{type.inspect}" unless config_attr
|
111
|
-
@configuration.send(config_attr, false)
|
46
|
+
unless @configuration.skip_dynamic_analysis?
|
47
|
+
puts 'Copying project for dynamic analysis...'
|
48
|
+
DynamicAnalyzer.new(rspec_command: @configuration.rspec_command) do |analyzer|
|
49
|
+
puts "Running dynamic analysis with command \"#{analyzer.rspec_command}\"..."
|
50
|
+
runtime_data = analyzer.analyze(paths)
|
112
51
|
end
|
52
|
+
puts
|
113
53
|
end
|
114
54
|
|
115
|
-
|
116
|
-
|
117
|
-
'Specify negative form of `to` that is used',
|
118
|
-
'in `expect(...).to` syntax.',
|
119
|
-
'Either `not_to` or `to_not`.',
|
120
|
-
'Default: not_to'
|
121
|
-
) do |form|
|
122
|
-
@configuration.negative_form_of_to = form
|
55
|
+
FileFinder.find(paths).each do |file_path|
|
56
|
+
convert_file(file_path, runtime_data)
|
123
57
|
end
|
58
|
+
end
|
124
59
|
|
125
|
-
|
126
|
-
|
127
|
-
'Suppress parenthesizing argument of matcher',
|
128
|
-
'when converting operator to non-operator',
|
129
|
-
'in `expect` syntax. Note that it will be',
|
130
|
-
'parenthesized even if this option is',
|
131
|
-
'specified when parentheses are necessary to',
|
132
|
-
'keep the meaning of the expression.',
|
133
|
-
'By default, arguments of the following',
|
134
|
-
'operator matchers will be parenthesized.',
|
135
|
-
' `== 10` to `eq(10)`',
|
136
|
-
' `=~ /pattern/` to `match(/pattern/)`',
|
137
|
-
' `=~ [1, 2]` to `match_array([1, 2])`'
|
138
|
-
) do
|
139
|
-
@configuration.parenthesize_matcher_arg = false
|
140
|
-
end
|
60
|
+
def convert_file(file_path, runtime_data = nil)
|
61
|
+
puts "Converting #{file_path}"
|
141
62
|
|
142
|
-
|
143
|
-
|
144
|
-
end
|
63
|
+
converter = Converter.new(@configuration, runtime_data, @report)
|
64
|
+
converter.convert_file!(file_path)
|
145
65
|
|
146
|
-
|
147
|
-
puts Version.to_s
|
148
|
-
exit
|
149
|
-
end
|
66
|
+
@report.invalid_context_errors.concat(converter.invalid_context_errors)
|
150
67
|
|
151
|
-
|
152
|
-
|
153
|
-
args
|
154
|
-
end
|
155
|
-
# rubocop:enable MethodLength
|
156
|
-
|
157
|
-
def target_files(paths)
|
158
|
-
paths.reduce([]) do |file_paths, path|
|
159
|
-
if File.directory?(path)
|
160
|
-
file_paths.concat(ruby_files_in_directory(path))
|
161
|
-
elsif File.file?(path)
|
162
|
-
file_paths << path
|
163
|
-
elsif !File.exists?(path)
|
164
|
-
fail ArgumentError, "No such file or directory #{path.inspect}"
|
165
|
-
end
|
68
|
+
converter.invalid_context_errors.each do |error|
|
69
|
+
warn_invalid_context_error(error)
|
166
70
|
end
|
71
|
+
rescue Parser::SyntaxError => error
|
72
|
+
@report.syntax_errors << error
|
73
|
+
warn_syntax_error(error)
|
167
74
|
end
|
168
75
|
|
169
76
|
private
|
170
77
|
|
171
|
-
def base_target_paths(args)
|
172
|
-
return args unless args.empty?
|
173
|
-
return ['spec'] if Dir.exists?('spec')
|
174
|
-
fail ArgumentError, 'Specify target files or directories.'
|
175
|
-
end
|
176
|
-
|
177
|
-
def ruby_files_in_directory(directory_path)
|
178
|
-
ruby_file_paths = []
|
179
|
-
|
180
|
-
Find.find(directory_path) do |path|
|
181
|
-
next unless File.file?(path)
|
182
|
-
next unless File.extname(path) == '.rb'
|
183
|
-
ruby_file_paths << path
|
184
|
-
end
|
185
|
-
|
186
|
-
ruby_file_paths
|
187
|
-
end
|
188
|
-
|
189
78
|
def fail_if_should_not_continue!
|
190
|
-
return if forced?
|
191
|
-
|
192
|
-
# TODO: Check each repository of target files / directories,
|
193
|
-
# not only the current working directory.
|
79
|
+
return if @configuration.forced?
|
194
80
|
return unless Git.command_available?
|
195
81
|
return unless Git.inside_of_repository?
|
196
82
|
return if Git.clean?
|
197
|
-
|
198
83
|
fail 'The current Git repository is not clean. Aborting.'
|
199
84
|
end
|
200
85
|
|
@@ -223,6 +108,11 @@ module Transpec
|
|
223
108
|
puts ' git commit -eF .git/COMMIT_EDITMSG'
|
224
109
|
end
|
225
110
|
|
111
|
+
def display_final_guide
|
112
|
+
puts
|
113
|
+
puts "Done! Now run #{'rspec'.bright} and check if all the converted specs pass."
|
114
|
+
end
|
115
|
+
|
226
116
|
def warn_syntax_error(error)
|
227
117
|
warn "Syntax error at #{error.diagnostic.location}. Skipping the file.".color(:red)
|
228
118
|
end
|
@@ -5,23 +5,27 @@ module Transpec
|
|
5
5
|
NEGATIVE_FORMS_OF_TO = ['not_to', 'to_not'].freeze
|
6
6
|
|
7
7
|
PREDICATES = [
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
8
|
+
[:convert_should, true],
|
9
|
+
[:convert_should_receive, true],
|
10
|
+
[:convert_stub, true],
|
11
|
+
[:convert_have_items, true],
|
12
|
+
[:convert_deprecated_method, true],
|
13
|
+
[:parenthesize_matcher_arg, true],
|
14
|
+
[:forced, false],
|
15
|
+
[:skip_dynamic_analysis, false],
|
16
|
+
[:generate_commit_message, false]
|
13
17
|
].freeze
|
14
18
|
|
15
|
-
PREDICATES.each do |predicate|
|
19
|
+
PREDICATES.each do |predicate, _|
|
16
20
|
attr_accessor predicate
|
17
21
|
alias_method predicate.to_s + '?', predicate
|
18
22
|
end
|
19
23
|
|
20
|
-
attr_accessor :negative_form_of_to
|
24
|
+
attr_accessor :negative_form_of_to, :rspec_command
|
21
25
|
|
22
26
|
def initialize
|
23
|
-
PREDICATES.each do |predicate|
|
24
|
-
instance_variable_set('@' + predicate.to_s,
|
27
|
+
PREDICATES.each do |predicate, default_value|
|
28
|
+
instance_variable_set('@' + predicate.to_s, default_value)
|
25
29
|
end
|
26
30
|
|
27
31
|
self.negative_form_of_to = 'not_to'
|
@@ -1,83 +1,48 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'transpec/base_rewriter'
|
4
|
+
require 'transpec/configuration'
|
3
5
|
require 'transpec/report'
|
4
|
-
require 'transpec/ast/builder'
|
5
6
|
require 'transpec/ast/scanner'
|
6
|
-
require 'transpec/configuration'
|
7
7
|
require 'transpec/syntax'
|
8
8
|
require 'transpec/syntax/be_close'
|
9
9
|
require 'transpec/syntax/double'
|
10
|
-
require 'transpec/syntax/
|
10
|
+
require 'transpec/syntax/expect'
|
11
11
|
require 'transpec/syntax/method_stub'
|
12
12
|
require 'transpec/syntax/raise_error'
|
13
13
|
require 'transpec/syntax/rspec_configure'
|
14
14
|
require 'transpec/syntax/should'
|
15
15
|
require 'transpec/syntax/should_receive'
|
16
|
-
require 'parser/current'
|
17
16
|
|
18
17
|
module Transpec
|
19
|
-
class
|
20
|
-
attr_reader :report, :invalid_context_errors
|
18
|
+
class Converter < BaseRewriter
|
19
|
+
attr_reader :configuration, :runtime_data, :report, :invalid_context_errors
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
@report = report
|
25
|
-
@invalid_context_errors = []
|
26
|
-
end
|
21
|
+
alias_method :convert_file!, :rewrite_file!
|
22
|
+
alias_method :convert, :rewrite
|
27
23
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
def initialize(configuration = nil, runtime_data = nil, report = nil)
|
25
|
+
@configuration = configuration || Configuration.new
|
26
|
+
@runtime_data = runtime_data
|
27
|
+
@report = report || Report.new
|
28
|
+
@invalid_context_errors = []
|
33
29
|
end
|
34
30
|
|
35
|
-
def
|
36
|
-
source_buffer = create_source_buffer(source, name)
|
37
|
-
ast = parse(source_buffer)
|
38
|
-
|
39
|
-
@source_rewriter = Parser::Source::Rewriter.new(source_buffer)
|
40
|
-
failed_overlapping_rewrite = false
|
41
|
-
@source_rewriter.diagnostics.consumer = proc do
|
42
|
-
failed_overlapping_rewrite = true
|
43
|
-
fail OverlappedRewriteError
|
44
|
-
end
|
45
|
-
|
31
|
+
def process(ast, source_rewriter)
|
46
32
|
AST::Scanner.scan(ast) do |node, ancestor_nodes|
|
47
|
-
dispatch_node(node, ancestor_nodes)
|
48
|
-
end
|
49
|
-
|
50
|
-
rewritten_source = @source_rewriter.process
|
51
|
-
|
52
|
-
if failed_overlapping_rewrite
|
53
|
-
rewriter = self.class.new(@configuration, @report)
|
54
|
-
rewritten_source = rewriter.rewrite(rewritten_source, name)
|
33
|
+
dispatch_node(node, ancestor_nodes, source_rewriter)
|
55
34
|
end
|
56
|
-
|
57
|
-
rewritten_source
|
58
|
-
end
|
59
|
-
|
60
|
-
def create_source_buffer(source, name)
|
61
|
-
source_buffer = Parser::Source::Buffer.new(name)
|
62
|
-
source_buffer.source = source
|
63
|
-
source_buffer
|
64
35
|
end
|
65
36
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
ast = parser.parse(source_buffer)
|
70
|
-
ast
|
71
|
-
end
|
72
|
-
|
73
|
-
def dispatch_node(node, ancestor_nodes)
|
74
|
-
Syntax.all.each do |syntax_class|
|
75
|
-
next unless syntax_class.target_node?(node)
|
37
|
+
def dispatch_node(node, ancestor_nodes, source_rewriter)
|
38
|
+
Syntax.standalone_syntaxes.each do |syntax_class|
|
39
|
+
next unless syntax_class.target_node?(node, @runtime_data)
|
76
40
|
|
77
41
|
syntax = syntax_class.new(
|
78
42
|
node,
|
79
43
|
ancestor_nodes,
|
80
|
-
|
44
|
+
source_rewriter,
|
45
|
+
@runtime_data,
|
81
46
|
@report
|
82
47
|
)
|
83
48
|
|
@@ -92,50 +57,60 @@ module Transpec
|
|
92
57
|
end
|
93
58
|
|
94
59
|
def process_should(should)
|
95
|
-
if @configuration.
|
60
|
+
if @configuration.convert_should?
|
96
61
|
should.expectize!(
|
97
62
|
@configuration.negative_form_of_to,
|
98
63
|
@configuration.parenthesize_matcher_arg?
|
99
64
|
)
|
100
65
|
end
|
66
|
+
|
67
|
+
if should.have_matcher && @configuration.convert_have_items?
|
68
|
+
should.have_matcher.convert_to_standard_expectation!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def process_expect(expect)
|
73
|
+
if expect.have_matcher && @configuration.convert_have_items?
|
74
|
+
expect.have_matcher.convert_to_standard_expectation!
|
75
|
+
end
|
101
76
|
end
|
102
77
|
|
103
78
|
def process_should_receive(should_receive)
|
104
79
|
if should_receive.useless_expectation?
|
105
|
-
if @configuration.
|
106
|
-
if @configuration.
|
80
|
+
if @configuration.convert_deprecated_method?
|
81
|
+
if @configuration.convert_stub?
|
107
82
|
should_receive.allowize_useless_expectation!(@configuration.negative_form_of_to)
|
108
83
|
else
|
109
84
|
should_receive.stubize_useless_expectation!
|
110
85
|
end
|
111
|
-
elsif @configuration.
|
86
|
+
elsif @configuration.convert_should_receive?
|
112
87
|
should_receive.expectize!(@configuration.negative_form_of_to)
|
113
88
|
end
|
114
|
-
elsif @configuration.
|
89
|
+
elsif @configuration.convert_should_receive?
|
115
90
|
should_receive.expectize!(@configuration.negative_form_of_to)
|
116
91
|
end
|
117
92
|
end
|
118
93
|
|
119
94
|
def process_double(double)
|
120
|
-
double.convert_to_double! if @configuration.
|
95
|
+
double.convert_to_double! if @configuration.convert_deprecated_method?
|
121
96
|
end
|
122
97
|
|
123
98
|
def process_method_stub(method_stub)
|
124
|
-
if @configuration.
|
99
|
+
if @configuration.convert_stub?
|
125
100
|
method_stub.allowize!
|
126
|
-
elsif @configuration.
|
127
|
-
method_stub.
|
101
|
+
elsif @configuration.convert_deprecated_method?
|
102
|
+
method_stub.convert_deprecated_method!
|
128
103
|
end
|
129
104
|
|
130
|
-
method_stub.remove_allowance_for_no_message! if @configuration.
|
105
|
+
method_stub.remove_allowance_for_no_message! if @configuration.convert_deprecated_method?
|
131
106
|
end
|
132
107
|
|
133
108
|
def process_be_close(be_close)
|
134
|
-
be_close.convert_to_be_within! if @configuration.
|
109
|
+
be_close.convert_to_be_within! if @configuration.convert_deprecated_method?
|
135
110
|
end
|
136
111
|
|
137
112
|
def process_raise_error(raise_error)
|
138
|
-
if @configuration.
|
113
|
+
if @configuration.convert_deprecated_method?
|
139
114
|
raise_error.remove_error_specification_with_negative_expectation!
|
140
115
|
end
|
141
116
|
end
|
@@ -151,20 +126,18 @@ module Transpec
|
|
151
126
|
end
|
152
127
|
|
153
128
|
def need_to_modify_expectation_syntax_configuration?(rspec_configure)
|
154
|
-
return false unless @configuration.
|
129
|
+
return false unless @configuration.convert_should?
|
155
130
|
rspec_configure.expectation_syntaxes == [:should]
|
156
131
|
rescue Syntax::RSpecConfigure::UnknownSyntaxError
|
157
132
|
false
|
158
133
|
end
|
159
134
|
|
160
135
|
def need_to_modify_mock_syntax_configuration?(rspec_configure)
|
161
|
-
return false if !@configuration.
|
162
|
-
!@configuration.
|
136
|
+
return false if !@configuration.convert_should_receive? &&
|
137
|
+
!@configuration.convert_stub?
|
163
138
|
rspec_configure.mock_syntaxes == [:should]
|
164
139
|
rescue Syntax::RSpecConfigure::UnknownSyntaxError
|
165
140
|
false
|
166
141
|
end
|
167
|
-
|
168
|
-
class OverlappedRewriteError < StandardError; end
|
169
142
|
end
|
170
143
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/base_rewriter'
|
4
|
+
require 'transpec/util'
|
5
|
+
require 'transpec/ast/scanner'
|
6
|
+
|
7
|
+
module Transpec
|
8
|
+
class DynamicAnalyzer
|
9
|
+
class Rewriter < BaseRewriter
|
10
|
+
include Util
|
11
|
+
|
12
|
+
def process(ast, source_rewriter)
|
13
|
+
# TODO: Currently multitheading is not considered...
|
14
|
+
clear_requests!
|
15
|
+
collect_requests(ast)
|
16
|
+
process_requests(source_rewriter)
|
17
|
+
end
|
18
|
+
|
19
|
+
def requests
|
20
|
+
@requests ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_requests!
|
24
|
+
@requests = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def register_request(node, key, instance_eval_string, eval_target_type = :object)
|
28
|
+
unless EVAL_TARGET_TYPES.include?(eval_target_type)
|
29
|
+
fail "Target type must be any of #{EVAL_TARGET_TYPES}"
|
30
|
+
end
|
31
|
+
|
32
|
+
requests[node] ||= {}
|
33
|
+
requests[node][key] = [eval_target_type, instance_eval_string]
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def collect_requests(ast)
|
39
|
+
AST::Scanner.scan(ast) do |node, ancestor_nodes|
|
40
|
+
Syntax.standalone_syntaxes.each do |syntax_class|
|
41
|
+
syntax_class.register_request_for_dynamic_analysis(node, self)
|
42
|
+
next unless syntax_class.target_node?(node)
|
43
|
+
syntax = syntax_class.new(node, ancestor_nodes)
|
44
|
+
syntax.register_request_for_dynamic_analysis(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_requests(source_rewriter)
|
50
|
+
requests.each do |node, analysis_codes|
|
51
|
+
inject_analysis_method(node, analysis_codes, source_rewriter)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def inject_analysis_method(node, analysis_codes, source_rewriter)
|
56
|
+
source_range = node.loc.expression
|
57
|
+
|
58
|
+
front = "#{ANALYSIS_METHOD}(("
|
59
|
+
rear = format(
|
60
|
+
'), self, %s, __FILE__, %d, %d)',
|
61
|
+
hash_literal(analysis_codes), source_range.begin_pos, source_range.end_pos
|
62
|
+
)
|
63
|
+
rear = "\n" + indentation_of_line(source_range.end) + rear if contain_here_document?(node)
|
64
|
+
|
65
|
+
parent_node = node.parent_node
|
66
|
+
|
67
|
+
if parent_node && parent_node.type == :block && parent_node.children.first.equal?(node)
|
68
|
+
source_range = node.parent_node.loc.expression
|
69
|
+
end
|
70
|
+
|
71
|
+
source_rewriter.insert_before(source_range, front)
|
72
|
+
source_rewriter.insert_after(source_range, rear)
|
73
|
+
rescue OverlappedRewriteError # rubocop:disable HandleExceptions
|
74
|
+
end
|
75
|
+
|
76
|
+
# Hash#inspect generates invalid literal with following example:
|
77
|
+
#
|
78
|
+
# > eval({ :predicate? => 1 }.inspect)
|
79
|
+
# SyntaxError: (eval):1: syntax error, unexpected =>
|
80
|
+
# {:predicate?=>1}
|
81
|
+
# ^
|
82
|
+
def hash_literal(hash)
|
83
|
+
literal = '{ '
|
84
|
+
|
85
|
+
hash.each_with_index do |(key, value), index|
|
86
|
+
literal << ', ' unless index == 0
|
87
|
+
literal << "#{key.inspect} => #{value.inspect}"
|
88
|
+
end
|
89
|
+
|
90
|
+
literal << ' }'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Transpec
|
6
|
+
class DynamicAnalyzer
|
7
|
+
class RuntimeData
|
8
|
+
attr_reader :hash
|
9
|
+
|
10
|
+
def initialize(hash = {})
|
11
|
+
@hash = hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](node)
|
15
|
+
@hash[node_id(node)]
|
16
|
+
end
|
17
|
+
|
18
|
+
def node_id(node)
|
19
|
+
source_range = node.loc.expression
|
20
|
+
source_buffer = source_range.source_buffer
|
21
|
+
absolute_path = File.expand_path(source_buffer.name)
|
22
|
+
relative_path = Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
|
23
|
+
[relative_path, source_range.begin_pos, source_range.end_pos].join('_')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|