transpec 1.9.0 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +4 -4
- data/README.md.erb +3 -3
- data/lib/transpec/syntax.rb +1 -1
- data/lib/transpec/syntax/have.rb +18 -266
- data/lib/transpec/syntax/have/dynamic_inspector.rb +119 -0
- data/lib/transpec/syntax/have/have_record.rb +105 -0
- data/lib/transpec/syntax/have/source_builder.rb +78 -0
- data/lib/transpec/syntax/its.rb +1 -1
- data/lib/transpec/syntax/method_stub.rb +1 -1
- data/lib/transpec/syntax/mixin/expect_base.rb +1 -1
- data/lib/transpec/syntax/mixin/have_matcher_owner.rb +1 -1
- data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +1 -1
- data/lib/transpec/syntax/mixin/send.rb +1 -1
- data/lib/transpec/syntax/mixin/should_base.rb +1 -1
- data/lib/transpec/syntax/oneliner_should.rb +15 -13
- data/lib/transpec/syntax/operator_matcher.rb +1 -1
- data/lib/transpec/syntax/should.rb +1 -1
- data/lib/transpec/syntax/should_receive.rb +1 -1
- data/lib/transpec/util.rb +1 -1
- data/lib/transpec/version.rb +1 -1
- data/spec/spec_helper.rb +14 -13
- data/spec/transpec/util_spec.rb +16 -0
- data/tasks/lib/transpec_demo.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c34e8b6406b750b87a6af37fd7ed8e1df46a235
|
4
|
+
data.tar.gz: 6d778d31bc2c9518fda218793f8a740268ab3dc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9e4b610441d631390420e67e391e759b1180e3260abe01ff78deb4fe3cefc57838f9d33da1a1cd5560d5e411a9ddd65f06c8c2fd287dce6a4203cacd737d5eb
|
7
|
+
data.tar.gz: 1ba96f0b28b40c05dd29257caead516bedfb4e4fac7fc4d51a56445f1e6734514385d1a27bd0610975d8ba4e513920b5d4e0c40807f6dc4c69c6d158bfa26bab
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
## Development
|
4
4
|
|
5
|
+
## v1.9.1
|
6
|
+
|
7
|
+
* Fix crash on `expect(obj).to non_matcher_object` in dynamic analysis ([#39](https://github.com/yujinakayama/transpec/issues/39))
|
8
|
+
|
5
9
|
## v1.9.0
|
6
10
|
|
7
11
|
* Support conversion of `and_return { value }` and `and_return` without arguments
|
data/README.md
CHANGED
@@ -41,7 +41,7 @@ describe Account do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
describe '#renew' do
|
44
|
-
context 'when the account is
|
44
|
+
context 'when the account is not closed' do
|
45
45
|
before do
|
46
46
|
account.stub(:closed?).and_return(false)
|
47
47
|
end
|
@@ -77,7 +77,7 @@ describe Account do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
describe '#renew' do
|
80
|
-
context 'when the account is
|
80
|
+
context 'when the account is not closed' do
|
81
81
|
before do
|
82
82
|
allow(account).to receive(:closed?).and_return(false)
|
83
83
|
end
|
@@ -106,13 +106,13 @@ Simply install `transpec` with `gem` command:
|
|
106
106
|
$ gem install transpec
|
107
107
|
```
|
108
108
|
|
109
|
-
|
109
|
+
Normally you don't need to add `transpec` to your `Gemfile` or `*.gemspec` since this isn't a tool to be used daily.
|
110
110
|
|
111
111
|
## Basic Usage
|
112
112
|
|
113
113
|
Before converting your specs:
|
114
114
|
|
115
|
-
* Make sure your project has `rspec` gem dependency **2.14** or later. If not, change your
|
115
|
+
* Make sure your project has `rspec` gem dependency **2.14** or later. If not, change your `Gemfile` or `*.gemspec` to do so.
|
116
116
|
* Run `rspec` and check if all the specs pass.
|
117
117
|
* Ensure the Git repository is clean. (You don't want to mix up your changes and Transpec's changes, do you?)
|
118
118
|
|
data/README.md.erb
CHANGED
@@ -43,7 +43,7 @@ describe Account do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
describe '#renew' do
|
46
|
-
context 'when the account is
|
46
|
+
context 'when the account is not closed' do
|
47
47
|
before do
|
48
48
|
account.stub(:closed?).and_return(false)
|
49
49
|
end
|
@@ -80,13 +80,13 @@ Simply install `transpec` with `gem` command:
|
|
80
80
|
$ gem install transpec
|
81
81
|
```
|
82
82
|
|
83
|
-
|
83
|
+
Normally you don't need to add `transpec` to your `Gemfile` or `*.gemspec` since this isn't a tool to be used daily.
|
84
84
|
|
85
85
|
## Basic Usage
|
86
86
|
|
87
87
|
Before converting your specs:
|
88
88
|
|
89
|
-
* Make sure your project has `rspec` gem dependency **<%= Transpec.required_rspec_version %>** or later. If not, change your
|
89
|
+
* Make sure your project has `rspec` gem dependency **<%= Transpec.required_rspec_version %>** or later. If not, change your `Gemfile` or `*.gemspec` to do so.
|
90
90
|
* Run `rspec` and check if all the specs pass.
|
91
91
|
* Ensure the Git repository is clean. (You don't want to mix up your changes and Transpec's changes, do you?)
|
92
92
|
|
data/lib/transpec/syntax.rb
CHANGED
data/lib/transpec/syntax/have.rb
CHANGED
@@ -7,6 +7,10 @@ require 'transpec/syntax/mixin/owned_matcher'
|
|
7
7
|
module Transpec
|
8
8
|
class Syntax
|
9
9
|
class Have < Syntax
|
10
|
+
require 'transpec/syntax/have/dynamic_inspector'
|
11
|
+
require 'transpec/syntax/have/source_builder'
|
12
|
+
require 'transpec/syntax/have/have_record'
|
13
|
+
|
10
14
|
include Mixin::Send, Mixin::OwnedMatcher
|
11
15
|
|
12
16
|
# String#count is not query method, and there's no way to determine
|
@@ -25,19 +29,21 @@ module Transpec
|
|
25
29
|
[:have, :have_exactly, :have_at_least, :have_at_most].include?(method_name)
|
26
30
|
end
|
27
31
|
|
28
|
-
|
32
|
+
define_dynamic_analysis_request do |rewriter|
|
29
33
|
DynamicInspector.register_request(self, rewriter)
|
30
34
|
end
|
31
35
|
|
32
36
|
def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
|
33
37
|
return if project_requires_collection_matcher?
|
34
|
-
|
35
|
-
replace(
|
38
|
+
|
39
|
+
replace(expectation.subject_range, replacement_subject_source) if explicit_subject?
|
40
|
+
replace(matcher_range, source_builder.replacement_matcher_source(parenthesize_matcher_arg))
|
41
|
+
|
36
42
|
register_record if explicit_subject?
|
37
43
|
end
|
38
44
|
|
39
45
|
def explicit_subject?
|
40
|
-
|
46
|
+
expectation.respond_to?(:subject_node)
|
41
47
|
end
|
42
48
|
|
43
49
|
alias_method :have_node, :node
|
@@ -51,10 +57,6 @@ module Transpec
|
|
51
57
|
items_node.children.size > 2
|
52
58
|
end
|
53
59
|
|
54
|
-
def have_method_name # rubocop:disable PredicateName
|
55
|
-
have_node.children[1]
|
56
|
-
end
|
57
|
-
|
58
60
|
def items_name
|
59
61
|
items_node.children[1]
|
60
62
|
end
|
@@ -93,35 +95,9 @@ module Transpec
|
|
93
95
|
QUERY_METHOD_PRIORITIES.first
|
94
96
|
end
|
95
97
|
|
96
|
-
def replacement_subject_source
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
def build_replacement_subject_source(original_subject_source)
|
101
|
-
source = original_subject_source.dup
|
102
|
-
if subject_is_owner_of_collection?
|
103
|
-
if collection_accessor_is_private?
|
104
|
-
source << ".send(#{collection_accessor.inspect}"
|
105
|
-
source << ", #{collection_accessor_args_body_source}" if items_method_has_arguments?
|
106
|
-
source << ')'
|
107
|
-
else
|
108
|
-
source << ".#{collection_accessor}#{collection_accessor_args_parentheses_source}"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
source << ".#{query_method}"
|
112
|
-
end
|
113
|
-
|
114
|
-
def replacement_matcher_source(parenthesize_arg)
|
115
|
-
build_replacement_matcher_source(size_source, parenthesize_arg)
|
116
|
-
end
|
117
|
-
|
118
|
-
def build_replacement_matcher_source(size_source, parenthesize_arg = true)
|
119
|
-
case @expectation.current_syntax_type
|
120
|
-
when :should
|
121
|
-
build_replacement_matcher_source_for_should(size_source)
|
122
|
-
when :expect
|
123
|
-
build_replacement_matcher_source_for_expect(size_source, parenthesize_arg)
|
124
|
-
end
|
98
|
+
def replacement_subject_source(original_subject_source = nil)
|
99
|
+
original_subject_source ||= expectation.subject_range.source
|
100
|
+
source_builder.replacement_subject_source(original_subject_source)
|
125
101
|
end
|
126
102
|
|
127
103
|
def size_source
|
@@ -130,247 +106,23 @@ module Transpec
|
|
130
106
|
|
131
107
|
private
|
132
108
|
|
109
|
+
def source_builder
|
110
|
+
@source_builder ||= SourceBuilder.new(self, size_source)
|
111
|
+
end
|
112
|
+
|
133
113
|
def runtime_subject_data
|
134
114
|
return @runtime_subject_data if instance_variable_defined?(:@runtime_subject_data)
|
135
|
-
node = explicit_subject? ?
|
115
|
+
node = explicit_subject? ? expectation.subject_node : expectation.node
|
136
116
|
@runtime_subject_data = runtime_node_data(node)
|
137
117
|
end
|
138
118
|
|
139
|
-
def build_replacement_matcher_source_for_should(size_source)
|
140
|
-
case have_method_name
|
141
|
-
when :have, :have_exactly then "== #{size_source}"
|
142
|
-
when :have_at_least then ">= #{size_source}"
|
143
|
-
when :have_at_most then "<= #{size_source}"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def build_replacement_matcher_source_for_expect(size_source, parenthesize_arg)
|
148
|
-
case have_method_name
|
149
|
-
when :have, :have_exactly
|
150
|
-
if parenthesize_arg
|
151
|
-
"eq(#{size_source})"
|
152
|
-
else
|
153
|
-
"eq #{size_source}"
|
154
|
-
end
|
155
|
-
when :have_at_least
|
156
|
-
"be >= #{size_source}"
|
157
|
-
when :have_at_most
|
158
|
-
"be <= #{size_source}"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
119
|
def matcher_range
|
163
120
|
expression_range.join(items_node.loc.expression)
|
164
121
|
end
|
165
122
|
|
166
|
-
def collection_accessor_args_parentheses_source
|
167
|
-
map = items_node.loc
|
168
|
-
range = map.selector.end.join(map.expression.end)
|
169
|
-
range.source
|
170
|
-
end
|
171
|
-
|
172
|
-
def collection_accessor_args_body_source
|
173
|
-
arg_nodes = items_node.children[2..-1]
|
174
|
-
range = arg_nodes.first.loc.expression.begin.join(arg_nodes.last.loc.expression.end)
|
175
|
-
range.source
|
176
|
-
end
|
177
|
-
|
178
123
|
def register_record
|
179
124
|
@report.records << HaveRecord.new(self)
|
180
125
|
end
|
181
|
-
|
182
|
-
class DynamicInspector
|
183
|
-
def self.register_request(have, rewriter)
|
184
|
-
new(have, rewriter).register_request
|
185
|
-
end
|
186
|
-
|
187
|
-
def initialize(have, rewriter)
|
188
|
-
@have = have
|
189
|
-
@rewriter = rewriter
|
190
|
-
end
|
191
|
-
|
192
|
-
def target_node
|
193
|
-
if @have.explicit_subject?
|
194
|
-
@have.expectation.subject_node
|
195
|
-
else
|
196
|
-
@have.expectation.node
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def target_type
|
201
|
-
if @have.explicit_subject?
|
202
|
-
:object
|
203
|
-
else
|
204
|
-
:context
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def register_request
|
209
|
-
key = :collection_accessor
|
210
|
-
code = collection_accessor_inspection_code
|
211
|
-
@rewriter.register_request(target_node, key, code, target_type)
|
212
|
-
|
213
|
-
# Give up inspecting query methods of collection accessor with arguments
|
214
|
-
# (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
|
215
|
-
unless @have.items_method_has_arguments?
|
216
|
-
key = :available_query_methods
|
217
|
-
code = available_query_methods_inspection_code
|
218
|
-
@rewriter.register_request(target_node, key, code, target_type)
|
219
|
-
end
|
220
|
-
|
221
|
-
key = :collection_accessor_is_private?
|
222
|
-
code = "#{subject_code}.private_methods.include?(#{@have.items_name.inspect})"
|
223
|
-
@rewriter.register_request(target_node, key, code, target_type)
|
224
|
-
|
225
|
-
key = :project_requires_collection_matcher?
|
226
|
-
code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
|
227
|
-
@rewriter.register_request(target_node, key, code, :context)
|
228
|
-
end
|
229
|
-
|
230
|
-
def subject_code
|
231
|
-
@have.explicit_subject? ? 'self' : 'subject'
|
232
|
-
end
|
233
|
-
|
234
|
-
# rubocop:disable MethodLength
|
235
|
-
def collection_accessor_inspection_code
|
236
|
-
# `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
|
237
|
-
# if the owner does not respond to any of #size, #count and #length.
|
238
|
-
#
|
239
|
-
# rubocop:disable LineLength
|
240
|
-
# https://github.com/rspec/rspec-expectations/blob/v2.14.3/lib/rspec/matchers/built_in/have.rb#L48-L58
|
241
|
-
# rubocop:enable LineLength
|
242
|
-
@collection_accessor_inspection_code ||= <<-END.gsub(/^\s+\|/, '').chomp
|
243
|
-
|begin
|
244
|
-
| exact_name = #{@have.items_name.inspect}
|
245
|
-
|
|
246
|
-
| inflector = if defined?(ActiveSupport::Inflector) &&
|
247
|
-
| ActiveSupport::Inflector.respond_to?(:pluralize)
|
248
|
-
| ActiveSupport::Inflector
|
249
|
-
| elsif defined?(Inflector)
|
250
|
-
| Inflector
|
251
|
-
| else
|
252
|
-
| nil
|
253
|
-
| end
|
254
|
-
|
|
255
|
-
| if inflector
|
256
|
-
| pluralized_name = inflector.pluralize(exact_name).to_sym
|
257
|
-
| respond_to_pluralized_name = #{subject_code}.respond_to?(pluralized_name)
|
258
|
-
| end
|
259
|
-
|
|
260
|
-
| respond_to_query_methods =
|
261
|
-
| !(#{subject_code}.methods & #{QUERY_METHOD_PRIORITIES.inspect}).empty?
|
262
|
-
|
|
263
|
-
| if #{subject_code}.respond_to?(exact_name)
|
264
|
-
| exact_name
|
265
|
-
| elsif respond_to_pluralized_name
|
266
|
-
| pluralized_name
|
267
|
-
| elsif respond_to_query_methods
|
268
|
-
| nil
|
269
|
-
| else
|
270
|
-
| exact_name
|
271
|
-
| end
|
272
|
-
|end
|
273
|
-
END
|
274
|
-
end
|
275
|
-
# rubocop:enable MethodLength
|
276
|
-
|
277
|
-
def available_query_methods_inspection_code
|
278
|
-
<<-END.gsub(/^\s+\|/, '').chomp
|
279
|
-
|collection_accessor = #{collection_accessor_inspection_code}
|
280
|
-
|target = if collection_accessor
|
281
|
-
| #{subject_code}.__send__(collection_accessor)
|
282
|
-
| else
|
283
|
-
| #{subject_code}
|
284
|
-
| end
|
285
|
-
|target.methods & #{QUERY_METHOD_PRIORITIES.inspect}
|
286
|
-
END
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
class HaveRecord < Record
|
291
|
-
def initialize(have)
|
292
|
-
@have = have
|
293
|
-
end
|
294
|
-
|
295
|
-
def original_syntax
|
296
|
-
@original_syntax ||= begin
|
297
|
-
type = @have.expectation.class.snake_case_name.to_sym
|
298
|
-
syntax = build_expectation(original_subject, type)
|
299
|
-
syntax << " #{@have.have_method_name}(n).#{original_items}"
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
def converted_syntax
|
304
|
-
@converted_syntax ||= begin
|
305
|
-
type = @have.expectation.current_syntax_type
|
306
|
-
syntax = build_expectation(converted_subject, type)
|
307
|
-
syntax << " #{@have.build_replacement_matcher_source('n')}"
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
def build_expectation(subject, type)
|
312
|
-
case type
|
313
|
-
when :should
|
314
|
-
syntax = "#{subject}.should"
|
315
|
-
syntax << '_not' unless positive?
|
316
|
-
when :expect
|
317
|
-
syntax = "expect(#{subject})."
|
318
|
-
syntax << (positive? ? 'to' : 'not_to')
|
319
|
-
end
|
320
|
-
|
321
|
-
syntax
|
322
|
-
end
|
323
|
-
|
324
|
-
def positive?
|
325
|
-
@have.expectation.positive?
|
326
|
-
end
|
327
|
-
|
328
|
-
def original_subject
|
329
|
-
if @have.subject_is_owner_of_collection?
|
330
|
-
'obj'
|
331
|
-
else
|
332
|
-
'collection'
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
def original_items
|
337
|
-
if @have.subject_is_owner_of_collection?
|
338
|
-
if @have.items_method_has_arguments?
|
339
|
-
"#{@have.collection_accessor}(...)"
|
340
|
-
else
|
341
|
-
@have.collection_accessor
|
342
|
-
end
|
343
|
-
else
|
344
|
-
'items'
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
def converted_subject
|
349
|
-
if @have.subject_is_owner_of_collection?
|
350
|
-
build_converted_subject('obj')
|
351
|
-
else
|
352
|
-
build_converted_subject('collection')
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
def build_converted_subject(subject)
|
357
|
-
subject << '.'
|
358
|
-
|
359
|
-
if @have.subject_is_owner_of_collection?
|
360
|
-
if @have.collection_accessor_is_private?
|
361
|
-
subject << "send(#{@have.collection_accessor.inspect}"
|
362
|
-
subject << ', ...' if @have.items_method_has_arguments?
|
363
|
-
subject << ')'
|
364
|
-
else
|
365
|
-
subject << "#{@have.collection_accessor}"
|
366
|
-
subject << '(...)' if @have.items_method_has_arguments?
|
367
|
-
end
|
368
|
-
subject << ".#{@have.query_method}"
|
369
|
-
else
|
370
|
-
subject << "#{@have.default_query_method}"
|
371
|
-
end
|
372
|
-
end
|
373
|
-
end
|
374
126
|
end
|
375
127
|
end
|
376
128
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax/have'
|
4
|
+
|
5
|
+
module Transpec
|
6
|
+
class Syntax
|
7
|
+
class Have
|
8
|
+
class DynamicInspector
|
9
|
+
def self.register_request(have, rewriter)
|
10
|
+
new(have, rewriter).register_request
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :have, :rewriter
|
14
|
+
|
15
|
+
def initialize(have, rewriter)
|
16
|
+
@have = have
|
17
|
+
@rewriter = rewriter
|
18
|
+
end
|
19
|
+
|
20
|
+
def target_node
|
21
|
+
if have.explicit_subject?
|
22
|
+
have.expectation.subject_node
|
23
|
+
else
|
24
|
+
have.expectation.node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def target_type
|
29
|
+
if have.explicit_subject?
|
30
|
+
:object
|
31
|
+
else
|
32
|
+
:context
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def register_request
|
37
|
+
key = :collection_accessor
|
38
|
+
code = collection_accessor_inspection_code
|
39
|
+
rewriter.register_request(target_node, key, code, target_type)
|
40
|
+
|
41
|
+
# Give up inspecting query methods of collection accessor with arguments
|
42
|
+
# (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
|
43
|
+
unless have.items_method_has_arguments?
|
44
|
+
key = :available_query_methods
|
45
|
+
code = available_query_methods_inspection_code
|
46
|
+
rewriter.register_request(target_node, key, code, target_type)
|
47
|
+
end
|
48
|
+
|
49
|
+
key = :collection_accessor_is_private?
|
50
|
+
code = "#{subject_code}.private_methods.include?(#{have.items_name.inspect})"
|
51
|
+
rewriter.register_request(target_node, key, code, target_type)
|
52
|
+
|
53
|
+
key = :project_requires_collection_matcher?
|
54
|
+
code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
|
55
|
+
rewriter.register_request(target_node, key, code, :context)
|
56
|
+
end
|
57
|
+
|
58
|
+
def subject_code
|
59
|
+
have.explicit_subject? ? 'self' : 'subject'
|
60
|
+
end
|
61
|
+
|
62
|
+
# rubocop:disable MethodLength
|
63
|
+
def collection_accessor_inspection_code
|
64
|
+
# `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
|
65
|
+
# if the owner does not respond to any of #size, #count and #length.
|
66
|
+
#
|
67
|
+
# rubocop:disable LineLength
|
68
|
+
# https://github.com/rspec/rspec-expectations/blob/v2.14.3/lib/rspec/matchers/built_in/have.rb#L48-L58
|
69
|
+
# rubocop:enable LineLength
|
70
|
+
@collection_accessor_inspection_code ||= <<-END.gsub(/^\s+\|/, '').chomp
|
71
|
+
|begin
|
72
|
+
| exact_name = #{have.items_name.inspect}
|
73
|
+
|
|
74
|
+
| inflector = if defined?(ActiveSupport::Inflector) &&
|
75
|
+
| ActiveSupport::Inflector.respond_to?(:pluralize)
|
76
|
+
| ActiveSupport::Inflector
|
77
|
+
| elsif defined?(Inflector)
|
78
|
+
| Inflector
|
79
|
+
| else
|
80
|
+
| nil
|
81
|
+
| end
|
82
|
+
|
|
83
|
+
| if inflector
|
84
|
+
| pluralized_name = inflector.pluralize(exact_name).to_sym
|
85
|
+
| respond_to_pluralized_name = #{subject_code}.respond_to?(pluralized_name)
|
86
|
+
| end
|
87
|
+
|
|
88
|
+
| respond_to_query_methods =
|
89
|
+
| !(#{subject_code}.methods & #{QUERY_METHOD_PRIORITIES.inspect}).empty?
|
90
|
+
|
|
91
|
+
| if #{subject_code}.respond_to?(exact_name)
|
92
|
+
| exact_name
|
93
|
+
| elsif respond_to_pluralized_name
|
94
|
+
| pluralized_name
|
95
|
+
| elsif respond_to_query_methods
|
96
|
+
| nil
|
97
|
+
| else
|
98
|
+
| exact_name
|
99
|
+
| end
|
100
|
+
|end
|
101
|
+
END
|
102
|
+
end
|
103
|
+
# rubocop:enable MethodLength
|
104
|
+
|
105
|
+
def available_query_methods_inspection_code
|
106
|
+
<<-END.gsub(/^\s+\|/, '').chomp
|
107
|
+
|collection_accessor = #{collection_accessor_inspection_code}
|
108
|
+
|target = if collection_accessor
|
109
|
+
| #{subject_code}.__send__(collection_accessor)
|
110
|
+
| else
|
111
|
+
| #{subject_code}
|
112
|
+
| end
|
113
|
+
|target.methods & #{QUERY_METHOD_PRIORITIES.inspect}
|
114
|
+
END
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax/have'
|
4
|
+
|
5
|
+
module Transpec
|
6
|
+
class Syntax
|
7
|
+
class Have
|
8
|
+
class HaveRecord < Record
|
9
|
+
attr_reader :have
|
10
|
+
|
11
|
+
def initialize(have)
|
12
|
+
@have = have
|
13
|
+
end
|
14
|
+
|
15
|
+
def original_syntax
|
16
|
+
@original_syntax ||= begin
|
17
|
+
type = have.expectation.class.snake_case_name.to_sym
|
18
|
+
syntax = build_expectation(original_subject, type)
|
19
|
+
syntax << " #{have.method_name}(n).#{original_items}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def converted_syntax
|
24
|
+
@converted_syntax ||= begin
|
25
|
+
type = have.expectation.current_syntax_type
|
26
|
+
syntax = build_expectation(converted_subject, type)
|
27
|
+
syntax << " #{source_builder.replacement_matcher_source}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_expectation(subject, type)
|
32
|
+
case type
|
33
|
+
when :should
|
34
|
+
syntax = "#{subject}.should"
|
35
|
+
syntax << '_not' unless positive?
|
36
|
+
when :expect
|
37
|
+
syntax = "expect(#{subject})."
|
38
|
+
syntax << (positive? ? 'to' : 'not_to')
|
39
|
+
end
|
40
|
+
|
41
|
+
syntax
|
42
|
+
end
|
43
|
+
|
44
|
+
def positive?
|
45
|
+
have.expectation.positive?
|
46
|
+
end
|
47
|
+
|
48
|
+
def original_subject
|
49
|
+
if have.subject_is_owner_of_collection?
|
50
|
+
'obj'
|
51
|
+
else
|
52
|
+
'collection'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def original_items
|
57
|
+
if have.subject_is_owner_of_collection?
|
58
|
+
if have.items_method_has_arguments?
|
59
|
+
"#{have.collection_accessor}(...)"
|
60
|
+
else
|
61
|
+
have.collection_accessor
|
62
|
+
end
|
63
|
+
else
|
64
|
+
'items'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def converted_subject
|
69
|
+
if @have.subject_is_owner_of_collection?
|
70
|
+
build_converted_subject('obj')
|
71
|
+
else
|
72
|
+
build_converted_subject('collection')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_converted_subject(subject)
|
77
|
+
if have.subject_is_owner_of_collection?
|
78
|
+
converted_owner_of_collection(subject)
|
79
|
+
else
|
80
|
+
subject << ".#{have.default_query_method}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def converted_owner_of_collection(subject)
|
85
|
+
subject << '.'
|
86
|
+
|
87
|
+
if have.collection_accessor_is_private?
|
88
|
+
subject << "send(#{have.collection_accessor.inspect}"
|
89
|
+
subject << ', ...' if have.items_method_has_arguments?
|
90
|
+
subject << ')'
|
91
|
+
else
|
92
|
+
subject << "#{have.collection_accessor}"
|
93
|
+
subject << '(...)' if have.items_method_has_arguments?
|
94
|
+
end
|
95
|
+
|
96
|
+
subject << ".#{have.query_method}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def source_builder
|
100
|
+
@source_builder ||= SourceBuilder.new(have, 'n')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax/have'
|
4
|
+
|
5
|
+
module Transpec
|
6
|
+
class Syntax
|
7
|
+
class Have
|
8
|
+
class SourceBuilder
|
9
|
+
attr_reader :have, :size_source
|
10
|
+
|
11
|
+
def initialize(have, size_source)
|
12
|
+
@have = have
|
13
|
+
@size_source = size_source
|
14
|
+
end
|
15
|
+
|
16
|
+
def replacement_subject_source(original_subject_source)
|
17
|
+
source = original_subject_source
|
18
|
+
if have.subject_is_owner_of_collection?
|
19
|
+
if have.collection_accessor_is_private?
|
20
|
+
source << ".send(#{have.collection_accessor.inspect}"
|
21
|
+
if have.items_method_has_arguments?
|
22
|
+
source << ", #{collection_accessor_args_body_source}"
|
23
|
+
end
|
24
|
+
source << ')'
|
25
|
+
else
|
26
|
+
source << ".#{have.collection_accessor}#{collection_accessor_args_parentheses_source}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
source << ".#{have.query_method}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def replacement_matcher_source(parenthesize_arg = true)
|
33
|
+
case have.expectation.current_syntax_type
|
34
|
+
when :should
|
35
|
+
replacement_matcher_source_for_should
|
36
|
+
when :expect
|
37
|
+
replacement_matcher_source_for_expect(parenthesize_arg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def replacement_matcher_source_for_should
|
42
|
+
case have.method_name
|
43
|
+
when :have, :have_exactly then "== #{size_source}"
|
44
|
+
when :have_at_least then ">= #{size_source}"
|
45
|
+
when :have_at_most then "<= #{size_source}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def replacement_matcher_source_for_expect(parenthesize_arg)
|
50
|
+
case have.method_name
|
51
|
+
when :have, :have_exactly
|
52
|
+
if parenthesize_arg
|
53
|
+
"eq(#{size_source})"
|
54
|
+
else
|
55
|
+
"eq #{size_source}"
|
56
|
+
end
|
57
|
+
when :have_at_least
|
58
|
+
"be >= #{size_source}"
|
59
|
+
when :have_at_most
|
60
|
+
"be <= #{size_source}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def collection_accessor_args_parentheses_source
|
65
|
+
map = have.items_node.loc
|
66
|
+
range = map.selector.end.join(map.expression.end)
|
67
|
+
range.source
|
68
|
+
end
|
69
|
+
|
70
|
+
def collection_accessor_args_body_source
|
71
|
+
arg_nodes = have.items_node.children[2..-1]
|
72
|
+
range = arg_nodes.first.loc.expression.begin.join(arg_nodes.last.loc.expression.end)
|
73
|
+
range.source
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/transpec/syntax/its.rb
CHANGED
@@ -13,7 +13,7 @@ module Transpec
|
|
13
13
|
receiver_node.nil? && method_name == :its
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
define_dynamic_analysis_request do |rewriter|
|
17
17
|
key = :project_requires_its?
|
18
18
|
code = 'defined?(RSpec::Its)'
|
19
19
|
rewriter.register_request(@node, key, code, :context)
|
@@ -42,7 +42,7 @@ module Transpec
|
|
42
42
|
!receiver_node.nil? && [:stub, :stub!, :stub_chain, :unstub, :unstub!].include?(method_name)
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
define_dynamic_analysis_request do |rewriter|
|
46
46
|
register_request_of_syntax_availability_inspection(
|
47
47
|
rewriter,
|
48
48
|
:allow_to_receive_available?,
|
@@ -13,7 +13,7 @@ module Transpec
|
|
13
13
|
include Send
|
14
14
|
|
15
15
|
included do
|
16
|
-
|
16
|
+
define_dynamic_analysis_request do |rewriter|
|
17
17
|
if Receive.dynamic_analysis_target_node?(matcher_node)
|
18
18
|
create_receive_matcher.register_request_for_dynamic_analysis(rewriter)
|
19
19
|
end
|
@@ -10,7 +10,7 @@ module Transpec
|
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
12
|
included do
|
13
|
-
|
13
|
+
define_dynamic_analysis_request do |rewriter|
|
14
14
|
if Have.dynamic_analysis_target_node?(matcher_node)
|
15
15
|
create_have_matcher.register_request_for_dynamic_analysis(rewriter)
|
16
16
|
end
|
@@ -12,7 +12,7 @@ module Transpec
|
|
12
12
|
include MonkeyPatch, ::AST::Sexp
|
13
13
|
|
14
14
|
included do
|
15
|
-
|
15
|
+
define_dynamic_analysis_request do |rewriter|
|
16
16
|
code = <<-END.gsub(/^\s+\|/, '').chomp
|
17
17
|
|if self.class.name == 'RSpec::Mocks::AnyInstance::Recorder'
|
18
18
|
| if respond_to?(:klass)
|
@@ -14,7 +14,7 @@ module Transpec
|
|
14
14
|
include Send, HaveMatcherOwner
|
15
15
|
|
16
16
|
included do
|
17
|
-
|
17
|
+
define_dynamic_analysis_request do |rewriter|
|
18
18
|
if OperatorMatcher.dynamic_analysis_target_node?(matcher_node)
|
19
19
|
create_operator_matcher.register_request_for_dynamic_analysis(rewriter)
|
20
20
|
end
|
@@ -38,7 +38,7 @@ module Transpec
|
|
38
38
|
|
39
39
|
insert_example_description!
|
40
40
|
|
41
|
-
subject_source = have_matcher.
|
41
|
+
subject_source = have_matcher.replacement_subject_source('subject')
|
42
42
|
insert_before(expression_range, "#{subject_source}.")
|
43
43
|
|
44
44
|
have_matcher.convert_to_standard_expectation!
|
@@ -53,7 +53,7 @@ module Transpec
|
|
53
53
|
|
54
54
|
insert_example_description!
|
55
55
|
|
56
|
-
subject_source = have_matcher.
|
56
|
+
subject_source = have_matcher.replacement_subject_source('subject')
|
57
57
|
expect_to_source = "expect(#{subject_source})."
|
58
58
|
expect_to_source << (positive? ? 'to' : negative_form)
|
59
59
|
replace(should_range, expect_to_source)
|
@@ -72,7 +72,7 @@ module Transpec
|
|
72
72
|
def build_description(size)
|
73
73
|
description = positive? ? 'has ' : 'does not have '
|
74
74
|
|
75
|
-
case have_matcher.
|
75
|
+
case have_matcher.method_name
|
76
76
|
when :have_at_least then description << 'at least '
|
77
77
|
when :have_at_most then description << 'at most '
|
78
78
|
end
|
@@ -161,17 +161,19 @@ module Transpec
|
|
161
161
|
end
|
162
162
|
|
163
163
|
class OnelinerShouldHaveRecord < Have::HaveRecord
|
164
|
+
attr_reader :should, :negative_form_of_to
|
165
|
+
|
164
166
|
def initialize(should, have, negative_form_of_to = nil)
|
167
|
+
super(have)
|
165
168
|
@should = should
|
166
|
-
@have = have
|
167
169
|
@negative_form_of_to = negative_form_of_to
|
168
170
|
end
|
169
171
|
|
170
172
|
def original_syntax
|
171
173
|
@original_syntax ||= begin
|
172
|
-
syntax =
|
173
|
-
syntax << " #{
|
174
|
-
syntax << (
|
174
|
+
syntax = should.example_has_description? ? "it '...' do" : 'it {'
|
175
|
+
syntax << " #{should.method_name} #{have.method_name}(n).#{original_items} "
|
176
|
+
syntax << (should.example_has_description? ? 'end' : '}')
|
175
177
|
end
|
176
178
|
end
|
177
179
|
|
@@ -181,25 +183,25 @@ module Transpec
|
|
181
183
|
syntax << ' '
|
182
184
|
syntax << converted_expectation
|
183
185
|
syntax << ' '
|
184
|
-
syntax <<
|
186
|
+
syntax << source_builder.replacement_matcher_source
|
185
187
|
syntax << ' end'
|
186
188
|
end
|
187
189
|
end
|
188
190
|
|
189
191
|
def converted_description
|
190
|
-
if
|
192
|
+
if should.example_has_description?
|
191
193
|
"it '...' do"
|
192
194
|
else
|
193
|
-
"it '#{
|
195
|
+
"it '#{should.build_description('n')}' do"
|
194
196
|
end
|
195
197
|
end
|
196
198
|
|
197
199
|
def converted_expectation
|
198
|
-
case
|
200
|
+
case should.current_syntax_type
|
199
201
|
when :should
|
200
|
-
"#{converted_subject}.#{
|
202
|
+
"#{converted_subject}.#{should.method_name}"
|
201
203
|
when :expect
|
202
|
-
"expect(#{converted_subject})." + (
|
204
|
+
"expect(#{converted_subject})." + (should.positive? ? 'to' : negative_form_of_to)
|
203
205
|
end
|
204
206
|
end
|
205
207
|
|
@@ -26,7 +26,7 @@ module Transpec
|
|
26
26
|
check_target_node_dynamically(node, runtime_data)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
define_dynamic_analysis_request do |rewriter|
|
30
30
|
if method_name == :=~
|
31
31
|
rewriter.register_request(arg_node, :arg_is_enumerable?, 'is_a?(Enumerable)')
|
32
32
|
end
|
@@ -17,7 +17,7 @@ module Transpec
|
|
17
17
|
!receiver_node.nil? && [:should_receive, :should_not_receive].include?(method_name)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
define_dynamic_analysis_request do |rewriter|
|
21
21
|
register_request_of_syntax_availability_inspection(
|
22
22
|
rewriter,
|
23
23
|
:expect_to_receive_available?,
|
data/lib/transpec/util.rb
CHANGED
data/lib/transpec/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -26,20 +26,21 @@ RSpec.configure do |config|
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
if ENV['TRAVIS']
|
33
|
-
|
34
|
-
|
35
|
-
elsif ENV['CI']
|
36
|
-
|
37
|
-
|
38
|
-
end
|
29
|
+
if ENV['TRAVIS'] || ENV['CI'] || ENV['COVERAGE']
|
30
|
+
require 'simplecov'
|
31
|
+
|
32
|
+
if ENV['TRAVIS']
|
33
|
+
require 'coveralls'
|
34
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
35
|
+
elsif ENV['CI']
|
36
|
+
require 'simplecov-rcov'
|
37
|
+
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
38
|
+
end
|
39
39
|
|
40
|
-
SimpleCov.start do
|
41
|
-
|
42
|
-
|
40
|
+
SimpleCov.start do
|
41
|
+
add_filter '/spec/'
|
42
|
+
add_filter '/vendor/bundle/'
|
43
|
+
end
|
43
44
|
end
|
44
45
|
|
45
46
|
Dir[File.join(File.dirname(__FILE__), 'support', '*')].each do |path|
|
data/spec/transpec/util_spec.rb
CHANGED
@@ -47,6 +47,22 @@ module Transpec
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
describe '#each_forward_chained_node' do
|
51
|
+
context 'when a non-send node is passed' do
|
52
|
+
let(:source) { ':foo' }
|
53
|
+
|
54
|
+
it 'does not yield' do
|
55
|
+
yielded = false
|
56
|
+
|
57
|
+
Util.each_forward_chained_node(ast) do
|
58
|
+
yielded = true
|
59
|
+
end
|
60
|
+
|
61
|
+
yielded.should be_false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
50
66
|
describe '#expand_range_to_adjacent_whitespaces' do
|
51
67
|
let(:node) { ast.each_node.find(&:block_type?) }
|
52
68
|
let(:range) { node.loc.begin }
|
data/tasks/lib/transpec_demo.rb
CHANGED
@@ -28,7 +28,7 @@ class TranspecDemo < TranspecTest
|
|
28
28
|
def run_demo(transpec_args = [])
|
29
29
|
in_project_dir do
|
30
30
|
with_clean_bundler_env do
|
31
|
-
sh File.join(Transpec.root, 'bin', 'transpec'), '--force'
|
31
|
+
sh File.join(Transpec.root, 'bin', 'transpec'), '--force', '--convert-stub-with-hash'
|
32
32
|
sh 'bundle exec rspec'
|
33
33
|
sh "git checkout --quiet -b #{DEMO_BRANCH}"
|
34
34
|
sh 'git commit --all --file .git/COMMIT_EDITMSG'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transpec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuji Nakayama
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -259,6 +259,9 @@ files:
|
|
259
259
|
- lib/transpec/syntax/example.rb
|
260
260
|
- lib/transpec/syntax/expect.rb
|
261
261
|
- lib/transpec/syntax/have.rb
|
262
|
+
- lib/transpec/syntax/have/dynamic_inspector.rb
|
263
|
+
- lib/transpec/syntax/have/have_record.rb
|
264
|
+
- lib/transpec/syntax/have/source_builder.rb
|
262
265
|
- lib/transpec/syntax/its.rb
|
263
266
|
- lib/transpec/syntax/matcher_definition.rb
|
264
267
|
- lib/transpec/syntax/method_stub.rb
|