transpec 0.2.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +10 -0
  5. data/README.md +111 -56
  6. data/README.md.erb +117 -62
  7. data/lib/transpec/ast/node.rb +41 -0
  8. data/lib/transpec/base_rewriter.rb +55 -0
  9. data/lib/transpec/cli.rb +43 -153
  10. data/lib/transpec/configuration.rb +13 -9
  11. data/lib/transpec/{rewriter.rb → converter.rb} +44 -71
  12. data/lib/transpec/dynamic_analyzer/rewriter.rb +94 -0
  13. data/lib/transpec/dynamic_analyzer/runtime_data.rb +27 -0
  14. data/lib/transpec/dynamic_analyzer.rb +166 -0
  15. data/lib/transpec/file_finder.rb +53 -0
  16. data/lib/transpec/option_parser.rb +166 -0
  17. data/lib/transpec/{context.rb → static_context_inspector.rb} +2 -2
  18. data/lib/transpec/syntax/be_close.rb +7 -9
  19. data/lib/transpec/syntax/double.rb +6 -10
  20. data/lib/transpec/syntax/expect.rb +35 -0
  21. data/lib/transpec/syntax/have.rb +195 -0
  22. data/lib/transpec/syntax/method_stub.rb +22 -27
  23. data/lib/transpec/syntax/mixin/allow_no_message.rb +73 -0
  24. data/lib/transpec/syntax/mixin/any_instance.rb +22 -0
  25. data/lib/transpec/syntax/mixin/expectizable.rb +26 -0
  26. data/lib/transpec/syntax/mixin/have_matcher.rb +23 -0
  27. data/lib/transpec/syntax/mixin/monkey_patch.rb +37 -0
  28. data/lib/transpec/syntax/mixin/send.rb +109 -0
  29. data/lib/transpec/syntax/{matcher.rb → operator_matcher.rb} +27 -14
  30. data/lib/transpec/syntax/raise_error.rb +6 -10
  31. data/lib/transpec/syntax/rspec_configure.rb +29 -28
  32. data/lib/transpec/syntax/should.rb +45 -15
  33. data/lib/transpec/syntax/should_receive.rb +44 -16
  34. data/lib/transpec/syntax.rb +29 -21
  35. data/lib/transpec/util.rb +12 -2
  36. data/lib/transpec/version.rb +3 -3
  37. data/spec/spec_helper.rb +8 -6
  38. data/spec/support/cache_helper.rb +50 -0
  39. data/spec/support/shared_context.rb +49 -1
  40. data/spec/transpec/ast/node_spec.rb +65 -0
  41. data/spec/transpec/cli_spec.rb +33 -242
  42. data/spec/transpec/commit_message_spec.rb +2 -2
  43. data/spec/transpec/configuration_spec.rb +12 -8
  44. data/spec/transpec/{rewriter_spec.rb → converter_spec.rb} +198 -148
  45. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +183 -0
  46. data/spec/transpec/dynamic_analyzer_spec.rb +164 -0
  47. data/spec/transpec/file_finder_spec.rb +118 -0
  48. data/spec/transpec/option_parser_spec.rb +185 -0
  49. data/spec/transpec/{context_spec.rb → static_context_inspector_spec.rb} +27 -12
  50. data/spec/transpec/syntax/be_close_spec.rb +8 -4
  51. data/spec/transpec/syntax/double_spec.rb +105 -12
  52. data/spec/transpec/syntax/expect_spec.rb +83 -0
  53. data/spec/transpec/syntax/have_spec.rb +599 -0
  54. data/spec/transpec/syntax/method_stub_spec.rb +276 -115
  55. data/spec/transpec/syntax/{matcher_spec.rb → operator_matcher_spec.rb} +277 -98
  56. data/spec/transpec/syntax/raise_error_spec.rb +92 -46
  57. data/spec/transpec/syntax/should_receive_spec.rb +298 -92
  58. data/spec/transpec/syntax/should_spec.rb +230 -44
  59. data/spec/transpec/util_spec.rb +2 -9
  60. data/tasks/lib/transpec_demo.rb +1 -1
  61. data/tasks/lib/transpec_test.rb +5 -7
  62. data/tasks/test.rake +5 -1
  63. data/transpec.gemspec +1 -1
  64. metadata +46 -22
  65. data/lib/transpec/syntax/able_to_allow_no_message.rb +0 -73
  66. data/lib/transpec/syntax/able_to_target_any_instance.rb +0 -24
  67. data/lib/transpec/syntax/expectizable.rb +0 -27
  68. data/lib/transpec/syntax/send_node_syntax.rb +0 -57
data/README.md.erb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Transpec
4
4
 
5
- **Transpec** automatically converts your specs into latest [RSpec](http://rspec.info/) syntax with static analysis.
5
+ **Transpec** automatically converts your specs into latest [RSpec](http://rspec.info/) syntax with static and dynamic code analysis.
6
6
 
7
7
  This aims to facilitate smooth transition to RSpec 3.
8
8
 
@@ -61,12 +61,12 @@ END
61
61
  Transpec would convert it to the following form:
62
62
 
63
63
  ```ruby
64
- <%= Transpec::Rewriter.new.rewrite(example) -%>
64
+ <%= Transpec::Converter.new.convert(example) -%>
65
65
  ```
66
66
 
67
- ### Real Examples
67
+ ### Actual Examples
68
68
 
69
- You can see real conversion examples below:
69
+ You can see actual conversion examples below:
70
70
 
71
71
  * https://github.com/yujinakayama/guard/commit/transpec-demo
72
72
  * https://github.com/yujinakayama/mail/commit/transpec-demo
@@ -86,27 +86,32 @@ Before converting your specs:
86
86
  * Run `rspec` and check if all the specs pass.
87
87
  * Ensure the Git repository is clean. (You don't want to mix up your changes and Transpec's changes, right?)
88
88
 
89
- Then, run `transpec` (using `--commit-message` is recommended) in the project root directory:
89
+ Then, run `transpec` (using `--generate-commit-message` is recommended) in the project root directory:
90
90
 
91
91
  ```bash
92
92
  $ cd some-project
93
- $ transpec --commit-message
94
- Processing spec/spec_helper.rb
95
- Processing spec/spec_spec.rb
96
- Processing spec/support/file_helper.rb
97
- Processing spec/support/shared_context.rb
98
- Processing spec/transpec/ast/scanner_spec.rb
99
- Processing spec/transpec/ast/scope_stack_spec.rb
93
+ $ transpec --generate-commit-message
94
+ Copying project for dynamic analysis...
95
+ Running dynamic analysis with command "bundle exec rspec"...
96
+ ...............................................................................
97
+ ...................
98
+
99
+ Finished in 13.07 seconds
100
+ 100 examples, 0 failures
101
+
102
+ Converting spec/spec_helper.rb
103
+ Converting spec/support/cache_helper.rb
104
+ Converting spec/support/file_helper.rb
105
+ Converting spec/support/shared_context.rb
106
+ Converting spec/transpec/ast/node_spec.rb
100
107
  ```
101
108
 
102
- This will convert and overwrite all spec files in the `spec` directory.
109
+ This will run your specs, convert them, and overwrite all spec files in the `spec` directory.
103
110
 
104
111
  After the conversion, run `rspec` again and check whether all pass:
105
112
 
106
113
  ```bash
107
114
  $ bundle exec rspec
108
- # ...
109
- 843 examples, 0 failures
110
115
  ```
111
116
 
112
117
  If all pass, commit the changes with auto-generated message:
@@ -135,7 +140,23 @@ Processing spec/spec_spec.rb
135
140
  Processing spec/support/file_helper.rb
136
141
  ```
137
142
 
138
- ### `-m/--commit-message`
143
+ ### `-s/--skip-dynamic-analysis`
144
+
145
+ Skip dynamic analysis and convert with only static analysis. Note that specifying this option decreases the conversion accuracy.
146
+
147
+ ### `-c/--rspec-command`
148
+
149
+ Specify command to run RSpec that is used for dynamic analysis.
150
+
151
+ Transpec needs to run your specs in copied project directory for dynamic analysis.
152
+ If your project requires some special setup or commands to run specs, use this option.
153
+ `bundle exec rspec` is used by default.
154
+
155
+ ```bash
156
+ $ transpec --rspec-command "./some_special_setup.sh && bundle exec rspec"
157
+ ```
158
+
159
+ ### `-m/--generate-commit-message`
139
160
 
140
161
  Generate commit message that describes conversion summary.
141
162
  Currently only Git is supported.
@@ -146,24 +167,25 @@ When you commit, you need to run the following command to use the generated mess
146
167
  $ git commit -eF .git/COMMIT_EDITMSG
147
168
  ```
148
169
 
149
- ### `-d/--disable`
170
+ ### `-k/--keep`
150
171
 
151
- Disable specific conversions.
172
+ Keep specific syntaxes by disabling conversions.
152
173
 
153
174
  ```bash
154
- $ transpec --disable expect_to_receive,allow_to_receive
175
+ $ transpec --keep should_receive,stub
155
176
  ```
156
177
 
157
- #### Available conversion types
178
+ #### Available syntax types
158
179
 
159
- Conversion Type | Target Syntax | Converted Syntax
160
- --------------------|----------------------------------|----------------------------
180
+ Type | Target Syntax | Converted Syntax
181
+ -----------------|----------------------------------|----------------------------
161
182
  <%=
162
183
  conversion_type_table = <<END
163
- `expect_to_matcher` | `obj.should matcher` | `expect(obj).to matcher`
164
- `expect_to_receive` | `obj.should_receive` | `expect(obj).to receive`
165
- `allow_to_receive` | `obj.stub` | `allow(obj).to receive`
166
- `deprecated` | `obj.stub!`, `mock('foo')`, etc. | `obj.stub`, `double('foo')`
184
+ `should` | `obj.should matcher` | `expect(obj).to matcher`
185
+ `should_receive` | `obj.should_receive` | `expect(obj).to receive`
186
+ `stub` | `obj.stub` | `allow(obj).to receive`
187
+ `have_items` | `expect(obj).to have(x).items` | `expect(obj.size).to eq(x)`
188
+ `deprecated` | `obj.stub!`, `mock('foo')`, etc. | `obj.stub`, `double('foo')`
167
189
  END
168
190
 
169
191
  types_in_readme = conversion_type_table.lines.map do |line|
@@ -171,11 +193,11 @@ types_in_readme = conversion_type_table.lines.map do |line|
171
193
  first_column.gsub(/[^\w]/, '').to_sym
172
194
  end.sort
173
195
 
174
- types_in_code = Transpec::CLI::CONFIG_ATTRS_FOR_CLI_TYPES.keys.sort
196
+ types_in_code = Transpec::OptionParser.available_conversion_types.sort
175
197
 
176
198
  unless types_in_readme == types_in_code
177
199
  types_missing_description = types_in_code - types_in_readme
178
- fail "No descriptions for conversion types #{types_missing_description}"
200
+ fail "No descriptions for syntax types #{types_missing_description}"
179
201
  end
180
202
 
181
203
  conversion_type_table
@@ -215,7 +237,7 @@ END
215
237
  -%>
216
238
 
217
239
  <%=
218
- Transpec::Rewriter.new.rewrite(parenthesizing_example).gsub(
240
+ Transpec::Converter.new.convert(parenthesizing_example).gsub(
219
241
  'original spec',
220
242
  'converted spec'
221
243
  )
@@ -224,8 +246,8 @@ Transpec::Rewriter.new.rewrite(parenthesizing_example).gsub(
224
246
  <%=
225
247
  configuration = Transpec::Configuration.new
226
248
  configuration.parenthesize_matcher_arg = false
227
- rewriter = Transpec::Rewriter.new(configuration)
228
- rewriter.rewrite(parenthesizing_example)
249
+ converter = Transpec::Converter.new(configuration)
250
+ converter.convert(parenthesizing_example)
229
251
  .gsub(
230
252
  'original spec',
231
253
  'converted spec with -p/--no-parentheses-matcher-arg option'
@@ -238,7 +260,7 @@ rewriter.rewrite(parenthesizing_example)
238
260
  -%>
239
261
  ```
240
262
 
241
- ## Troubleshooting
263
+ ## Inconvertible Specs
242
264
 
243
265
  You might see the following warning while conversion:
244
266
 
@@ -266,11 +288,11 @@ end
266
288
 
267
289
  ### Reason
268
290
 
269
- * `should` is defined on `Kernel` (included by `Object`), so you can use `should` almost everywhere.
270
- * `expect` is defined on `RSpec::Matchers` (included by `RSpec::Core::ExampleGroup`), so you can use `expect` only where `self` is an instance of `RSpec::Core::ExampleGroup`.
291
+ * `should` is defined on `Kernel` module that is included by `Object` class, so you can use `should` almost everywhere.
292
+ * `expect` is defined on `RSpec::Matchers` module that is included by `RSpec::Core::ExampleGroup` class, so you can use `expect` only where `self` is an instance of `RSpec::Core::ExampleGroup` (i.e. in `it` blocks, `:each` hook blocks or included methods).
271
293
 
272
294
  With the above example, in the context of `1.should == 1`, the `self` is an instance of `MyAwesomeTestRunner`.
273
- So Transpec tracks contexts and skips conversion if the target syntax cannot be converted in a case like this.
295
+ Transpec tracks contexts and skips conversion if the target syntax cannot be converted in a case like this.
274
296
 
275
297
  ### Solution
276
298
 
@@ -291,8 +313,8 @@ expect(obj).not_to matcher
291
313
  expect(obj).to_not matcher # with `--negative-form to_not`
292
314
  ```
293
315
 
294
- * Disabled by: `--disable expect_to_matcher`
295
- * Related Information: [Myron Marston » RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)
316
+ * Disabled by: `--keep should`
317
+ * See also: [Myron Marston » RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)
296
318
 
297
319
  ### Operator matchers
298
320
 
@@ -312,7 +334,7 @@ expect('string').to match(/^str/)
312
334
  expect([1, 2, 3]).to match_array([2, 1, 3])
313
335
  ```
314
336
 
315
- * Related Information: [Myron Marston » RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#almost_all_matchers_are_supported)
337
+ * See also: [(Almost) All Matchers Are Supported - RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#almost_all_matchers_are_supported)
316
338
 
317
339
  ### `be_close` matcher
318
340
 
@@ -324,10 +346,43 @@ expect([1, 2, 3]).to match_array([2, 1, 3])
324
346
  (1.0 / 3.0).should be_within(0.001).of(0.333)
325
347
  ```
326
348
 
327
- * Disabled by: `--disable deprecated`
328
- * Related Information: [New be within matcher and RSpec.deprecate fix · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/pull/32)
349
+ * Disabled by: `--keep deprecated`
350
+ * See also: [New be within matcher and RSpec.deprecate fix · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/pull/32)
351
+
352
+ ### `have(n).items` matcher
353
+
354
+ ```ruby
355
+ # Targets
356
+ expect(collection).to have(3).items
357
+ expect(collection).to have_exactly(3).items
358
+ expect(collection).to have_at_least(3).items
359
+ expect(collection).to have_at_most(3).items
360
+ collection.should have(3).items
361
+
362
+ expect(team).to have(3).players
363
+
364
+ # Assume #players is a private method.
365
+ expect(team).to have(3).players
366
+
367
+ # Converted
368
+ expect(collection.size).to eq(3)
369
+ expect(collection.size).to be >= 3
370
+ expect(collection.size).to be <= 3
371
+ collection.size.should == 3 # with `--keep should`
372
+
373
+ expect(team.players.size).to eq(3)
374
+
375
+ # have(n).items matcher invokes #players even if it's a private method.
376
+ expect(team.send(:players).size).to eq(3)
377
+ ```
378
+
379
+ You have the option to continue using `have(n).items` matcher with [rspec-collection_matchers](https://github.com/rspec/rspec-collection_matchers) that is an external gem extracted from `rspec-expectations`.
380
+ If you choose so, disable this conversion with `--keep have_items`.
381
+
382
+ * Disabled by: `--keep have_items`
383
+ * See also: [Expectations: have(x).items matchers will be moved into an external gem - The Plan for RSpec 3](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#expectations__matchers_will_be_moved_into_an_external_gem)
329
384
 
330
- ### Expectations on Proc
385
+ ### Expectations on Block
331
386
 
332
387
  ```ruby
333
388
  # Targets
@@ -339,8 +394,8 @@ proc { do_something }.should raise_error
339
394
  expect { do_something }.to raise_error
340
395
  ```
341
396
 
342
- * Disabled by: `--disable expect_to_matcher`
343
- * Related Information: [Myron Marston » RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#unification_of_block_vs_value_syntaxes)
397
+ * Disabled by: `--keep should`
398
+ * See also: [Unification of Block vs. Value Syntaxes - RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#unification_of_block_vs_value_syntaxes)
344
399
 
345
400
  ### Negative error expectations with specific error
346
401
 
@@ -353,11 +408,11 @@ lambda { do_something }.should_not raise_error(SomeErrorClass)
353
408
 
354
409
  # Converted
355
410
  expect { do_something }.not_to raise_error
356
- lambda { do_something }.should_not raise_error # with `--disable expect_to_matcher`
411
+ lambda { do_something }.should_not raise_error # with `--keep should`
357
412
  ```
358
413
 
359
- * Disabled by: `--disable deprecated`
360
- * Related Information: [Consider deprecating `expect { }.not_to raise_error(SpecificErrorClass)` · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/issues/231)
414
+ * Disabled by: `--keep deprecated`
415
+ * See also: [Consider deprecating `expect { }.not_to raise_error(SpecificErrorClass)` · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/issues/231)
361
416
 
362
417
  ### Message expectations
363
418
 
@@ -371,8 +426,8 @@ expect(obj).to receive(:foo)
371
426
  expect_any_instance_of(SomeClass).to receive(:foo)
372
427
  ```
373
428
 
374
- * Disabled by: `--disable expect_to_receive`
375
- * Related Information: [RSpec's new message expectation syntax - Tea is awesome.](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)
429
+ * Disabled by: `--keep should_receive`
430
+ * See also: [RSpec's new message expectation syntax - Tea is awesome.](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)
376
431
 
377
432
  ### Message expectations that are actually method stubs
378
433
 
@@ -386,14 +441,14 @@ SomeClass.any_instance.should_receive(:foo).at_least(0)
386
441
 
387
442
  # Converted
388
443
  allow(obj).to receive(:foo)
389
- obj.stub(:foo) # with `--disable allow_to_receive`
444
+ obj.stub(:foo) # with `--keep stub`
390
445
 
391
446
  allow_any_instance_of(SomeClass).to receive(:foo)
392
- SomeClass.any_instance.stub(:foo) # with `--disable allow_to_receive`
447
+ SomeClass.any_instance.stub(:foo) # with `--keep stub`
393
448
  ```
394
449
 
395
- * Disabled by: `--disable deprecated`
396
- * Related Information: [Don't allow at_least(0) · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/133)
450
+ * Disabled by: `--keep deprecated`
451
+ * See also: [Don't allow at_least(0) · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/133)
397
452
 
398
453
  ### Method stubs
399
454
 
@@ -418,8 +473,8 @@ allow(obj).to receive(:bar).and_return(2)
418
473
  allow_any_instance_of(SomeClass).to receive(:foo)
419
474
  ```
420
475
 
421
- * Disabled by: `--disable allow_to_receive`
422
- * Related Information: [RSpec's new message expectation syntax - Tea is awesome.](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)
476
+ * Disabled by: `--keep stub`
477
+ * See also: [RSpec's new message expectation syntax - Tea is awesome.](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)
423
478
 
424
479
  ### Deprecated method stub aliases
425
480
 
@@ -429,12 +484,12 @@ obj.stub!(:foo)
429
484
  obj.unstub!(:foo)
430
485
 
431
486
  # Converted
432
- obj.stub(:foo) # with `--disable allow_to_receive`
487
+ obj.stub(:foo) # with `--keep stub`
433
488
  obj.unstub(:foo)
434
489
  ```
435
490
 
436
- * Disabled by: `--disable deprecated`
437
- * Related Information: [Consider deprecating and/or removing #stub! and #unstub! at some point · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/122)
491
+ * Disabled by: `--keep deprecated`
492
+ * See also: [Consider deprecating and/or removing #stub! and #unstub! at some point · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/122)
438
493
 
439
494
  ### Method stubs with deprecated specification of number of times
440
495
 
@@ -445,11 +500,11 @@ obj.stub(:foo).at_least(0)
445
500
 
446
501
  # Converted
447
502
  allow(obj).to receive(:foo)
448
- obj.stub(:foo) # with `--disable allow_to_receive`
503
+ obj.stub(:foo) # with `--keep stub`
449
504
  ```
450
505
 
451
- * Disabled by: `--disable deprecated`
452
- * Related Information: [Don't allow at_least(0) · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/133)
506
+ * Disabled by: `--keep deprecated`
507
+ * See also: [Don't allow at_least(0) · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/133)
453
508
 
454
509
  ### Deprecated test double aliases
455
510
 
@@ -462,8 +517,8 @@ mock('something')
462
517
  double('something')
463
518
  ```
464
519
 
465
- * Disabled by: `--disable deprecated`
466
- * Related Information: [Deprecate "stub" for doubles · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/214)
520
+ * Disabled by: `--keep deprecated`
521
+ * See also: [Deprecate "stub" for doubles · rspec/rspec-mocks](https://github.com/rspec/rspec-mocks/issues/214)
467
522
 
468
523
  ## Compatibility
469
524
 
@@ -5,6 +5,41 @@ require 'parser'
5
5
  module Transpec
6
6
  module AST
7
7
  class Node < Parser::AST::Node
8
+ def initialize(type, children = [], properties = {})
9
+ @properties = {}
10
+
11
+ super
12
+
13
+ each_child_node do |child_node|
14
+ child_node.parent_node = self
15
+ end
16
+ end
17
+
18
+ def parent_node
19
+ @properties[:parent_node]
20
+ end
21
+
22
+ def parent_node=(node)
23
+ @properties[:parent_node] = node
24
+ end
25
+
26
+ protected :parent_node=
27
+
28
+ def each_ancestor_node(&block)
29
+ return to_enum(__method__) unless block_given?
30
+
31
+ if parent_node
32
+ yield parent_node
33
+ parent_node.each_ancestor_node(&block)
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ def ancestor_nodes
40
+ each_ancestor_node.to_a
41
+ end
42
+
8
43
  def each_child_node
9
44
  return to_enum(__method__) unless block_given?
10
45
 
@@ -32,6 +67,12 @@ module Transpec
32
67
  def descendent_nodes
33
68
  each_descendent_node.to_a
34
69
  end
70
+
71
+ private
72
+
73
+ def properties
74
+ @properties ||= {}
75
+ end
35
76
  end
36
77
  end
37
78
  end
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/ast/builder'
4
+ require 'parser/current'
5
+
6
+ module Transpec
7
+ class BaseRewriter
8
+ def rewrite_file!(file_path)
9
+ source = File.read(file_path)
10
+ rewritten_source = rewrite(source, file_path)
11
+ return if source == rewritten_source
12
+ File.write(file_path, rewritten_source)
13
+ end
14
+
15
+ def rewrite(source, name = '(string)')
16
+ source_buffer = create_source_buffer(source, name)
17
+ ast = parse(source_buffer)
18
+
19
+ source_rewriter = Parser::Source::Rewriter.new(source_buffer)
20
+ failed_overlapping_rewrite = false
21
+ source_rewriter.diagnostics.consumer = proc do
22
+ failed_overlapping_rewrite = true
23
+ fail OverlappedRewriteError
24
+ end
25
+
26
+ process(ast, source_rewriter)
27
+
28
+ rewritten_source = source_rewriter.process
29
+ rewritten_source = rewrite(rewritten_source, name) if failed_overlapping_rewrite
30
+
31
+ rewritten_source
32
+ end
33
+
34
+ private
35
+
36
+ def process(ast, source_rewriter)
37
+ fail NotImplementedError
38
+ end
39
+
40
+ def create_source_buffer(source, name)
41
+ source_buffer = Parser::Source::Buffer.new(name)
42
+ source_buffer.source = source
43
+ source_buffer
44
+ end
45
+
46
+ def parse(source_buffer)
47
+ builder = AST::Builder.new
48
+ parser = Parser::CurrentRuby.new(builder)
49
+ ast = parser.parse(source_buffer)
50
+ ast
51
+ end
52
+
53
+ class OverlappedRewriteError < StandardError; end
54
+ end
55
+ end