transpec 1.9.0 → 1.9.1
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.
- 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
|