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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa52c57bc733f1dfe6a1c635a6338bd54cc39c0d
4
- data.tar.gz: 54c31463aff3d7f8a0f52588da49914a4cb597d9
3
+ metadata.gz: 2c34e8b6406b750b87a6af37fd7ed8e1df46a235
4
+ data.tar.gz: 6d778d31bc2c9518fda218793f8a740268ab3dc7
5
5
  SHA512:
6
- metadata.gz: af49f12b94003b1b14f426044d349c03d76c53a8ca78470e75765e1e15f1a1b3c431d2a5c64a0fae7329a74053d80759a0ada4c9282b251eac112a85bd71cdf9
7
- data.tar.gz: 9c160cd00df22d3a0f0f52c1318ca10ebafd3b1fe6b8ddf4d4215a71121fd6448edba07ba649945d0e0aad187b7addb35760d7f4a714320f5cb706cf1ff38429
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 renewable and not closed' do
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 renewable and not closed' do
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
- Usually you don't need to add `transpec` to your `*.gemspec` or `Gemfile` since this isn't a tool to be used daily.
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 `*.gemspec` or `Gemfile` to do so.
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 renewable and not closed' do
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
- Usually you don't need to add `transpec` to your `*.gemspec` or `Gemfile` since this isn't a tool to be used daily.
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 `*.gemspec` or `Gemfile` to do so.
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
 
@@ -61,7 +61,7 @@ module Transpec
61
61
  extend ActiveSupport::Concern
62
62
 
63
63
  module ClassMethods
64
- def add_dynamic_analysis_request(&block)
64
+ def define_dynamic_analysis_request(&block)
65
65
  dynamic_analysis_requests << block
66
66
  end
67
67
 
@@ -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
- add_dynamic_analysis_request do |rewriter|
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
- replace(@expectation.subject_range, replacement_subject_source) if explicit_subject?
35
- replace(matcher_range, replacement_matcher_source(parenthesize_matcher_arg))
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
- @expectation.respond_to?(:subject_node)
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
- build_replacement_subject_source(@expectation.subject_range.source)
98
- end
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? ? @expectation.subject_node : @expectation.node
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
@@ -13,7 +13,7 @@ module Transpec
13
13
  receiver_node.nil? && method_name == :its
14
14
  end
15
15
 
16
- add_dynamic_analysis_request do |rewriter|
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
- add_dynamic_analysis_request do |rewriter|
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
- add_dynamic_analysis_request do |rewriter|
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
- add_dynamic_analysis_request do |rewriter|
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
- add_dynamic_analysis_request do |rewriter|
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)
@@ -45,7 +45,7 @@ module Transpec
45
45
  end
46
46
 
47
47
  included do
48
- add_dynamic_analysis_request do |rewriter|
48
+ define_dynamic_analysis_request do |rewriter|
49
49
  if receiver_node
50
50
  target_node = receiver_node
51
51
  target_object_type = :object
@@ -14,7 +14,7 @@ module Transpec
14
14
  include Send, HaveMatcherOwner
15
15
 
16
16
  included do
17
- add_dynamic_analysis_request do |rewriter|
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.build_replacement_subject_source('subject')
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.build_replacement_subject_source('subject')
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.have_method_name
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 = @should.example_has_description? ? "it '...' do" : 'it {'
173
- syntax << " #{@should.method_name} #{@have.have_method_name}(n).#{original_items} "
174
- syntax << (@should.example_has_description? ? 'end' : '}')
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 << @have.build_replacement_matcher_source('n')
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 @should.example_has_description?
192
+ if should.example_has_description?
191
193
  "it '...' do"
192
194
  else
193
- "it '#{@should.build_description('n')}' do"
195
+ "it '#{should.build_description('n')}' do"
194
196
  end
195
197
  end
196
198
 
197
199
  def converted_expectation
198
- case @should.current_syntax_type
200
+ case should.current_syntax_type
199
201
  when :should
200
- "#{converted_subject}.#{@should.method_name}"
202
+ "#{converted_subject}.#{should.method_name}"
201
203
  when :expect
202
- "expect(#{converted_subject})." + (@should.positive? ? 'to' : @negative_form_of_to)
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
- add_dynamic_analysis_request do |rewriter|
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
@@ -22,7 +22,7 @@ module Transpec
22
22
  @current_syntax_type = :should
23
23
  end
24
24
 
25
- add_dynamic_analysis_request do |rewriter|
25
+ define_dynamic_analysis_request do |rewriter|
26
26
  register_request_of_syntax_availability_inspection(
27
27
  rewriter,
28
28
  :expect_available?,
@@ -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
- add_dynamic_analysis_request do |rewriter|
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
@@ -84,7 +84,7 @@ module Transpec
84
84
  loop do
85
85
  child_node = parent_node.children.first
86
86
 
87
- return unless child_node
87
+ return if !child_node || !child_node.is_a?(AST::Node)
88
88
  return unless [:send, :block].include?(child_node.type)
89
89
 
90
90
  if mode == :parent_as_second_arg
@@ -5,7 +5,7 @@ module Transpec
5
5
  module Version
6
6
  MAJOR = 1
7
7
  MINOR = 9
8
- PATCH = 0
8
+ PATCH = 1
9
9
 
10
10
  def self.to_s
11
11
  [MAJOR, MINOR, PATCH].join('.')
data/spec/spec_helper.rb CHANGED
@@ -26,20 +26,21 @@ RSpec.configure do |config|
26
26
  end
27
27
  end
28
28
 
29
- require 'simplecov'
30
- SimpleCov.coverage_dir(File.join('spec', 'coverage'))
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
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
- add_filter '/spec/'
42
- add_filter '/vendor/bundle/'
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|
@@ -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 }
@@ -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.0
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-08 00:00:00.000000000 Z
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