transpec 0.2.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +10 -0
- data/README.md +111 -56
- data/README.md.erb +117 -62
- data/lib/transpec/ast/node.rb +41 -0
- data/lib/transpec/base_rewriter.rb +55 -0
- data/lib/transpec/cli.rb +43 -153
- data/lib/transpec/configuration.rb +13 -9
- data/lib/transpec/{rewriter.rb → converter.rb} +44 -71
- data/lib/transpec/dynamic_analyzer/rewriter.rb +94 -0
- data/lib/transpec/dynamic_analyzer/runtime_data.rb +27 -0
- data/lib/transpec/dynamic_analyzer.rb +166 -0
- data/lib/transpec/file_finder.rb +53 -0
- data/lib/transpec/option_parser.rb +166 -0
- data/lib/transpec/{context.rb → static_context_inspector.rb} +2 -2
- data/lib/transpec/syntax/be_close.rb +7 -9
- data/lib/transpec/syntax/double.rb +6 -10
- data/lib/transpec/syntax/expect.rb +35 -0
- data/lib/transpec/syntax/have.rb +195 -0
- data/lib/transpec/syntax/method_stub.rb +22 -27
- data/lib/transpec/syntax/mixin/allow_no_message.rb +73 -0
- data/lib/transpec/syntax/mixin/any_instance.rb +22 -0
- data/lib/transpec/syntax/mixin/expectizable.rb +26 -0
- data/lib/transpec/syntax/mixin/have_matcher.rb +23 -0
- data/lib/transpec/syntax/mixin/monkey_patch.rb +37 -0
- data/lib/transpec/syntax/mixin/send.rb +109 -0
- data/lib/transpec/syntax/{matcher.rb → operator_matcher.rb} +27 -14
- data/lib/transpec/syntax/raise_error.rb +6 -10
- data/lib/transpec/syntax/rspec_configure.rb +29 -28
- data/lib/transpec/syntax/should.rb +45 -15
- data/lib/transpec/syntax/should_receive.rb +44 -16
- data/lib/transpec/syntax.rb +29 -21
- data/lib/transpec/util.rb +12 -2
- data/lib/transpec/version.rb +3 -3
- data/spec/spec_helper.rb +8 -6
- data/spec/support/cache_helper.rb +50 -0
- data/spec/support/shared_context.rb +49 -1
- data/spec/transpec/ast/node_spec.rb +65 -0
- data/spec/transpec/cli_spec.rb +33 -242
- data/spec/transpec/commit_message_spec.rb +2 -2
- data/spec/transpec/configuration_spec.rb +12 -8
- data/spec/transpec/{rewriter_spec.rb → converter_spec.rb} +198 -148
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +183 -0
- data/spec/transpec/dynamic_analyzer_spec.rb +164 -0
- data/spec/transpec/file_finder_spec.rb +118 -0
- data/spec/transpec/option_parser_spec.rb +185 -0
- data/spec/transpec/{context_spec.rb → static_context_inspector_spec.rb} +27 -12
- data/spec/transpec/syntax/be_close_spec.rb +8 -4
- data/spec/transpec/syntax/double_spec.rb +105 -12
- data/spec/transpec/syntax/expect_spec.rb +83 -0
- data/spec/transpec/syntax/have_spec.rb +599 -0
- data/spec/transpec/syntax/method_stub_spec.rb +276 -115
- data/spec/transpec/syntax/{matcher_spec.rb → operator_matcher_spec.rb} +277 -98
- data/spec/transpec/syntax/raise_error_spec.rb +92 -46
- data/spec/transpec/syntax/should_receive_spec.rb +298 -92
- data/spec/transpec/syntax/should_spec.rb +230 -44
- data/spec/transpec/util_spec.rb +2 -9
- data/tasks/lib/transpec_demo.rb +1 -1
- data/tasks/lib/transpec_test.rb +5 -7
- data/tasks/test.rake +5 -1
- data/transpec.gemspec +1 -1
- metadata +46 -22
- data/lib/transpec/syntax/able_to_allow_no_message.rb +0 -73
- data/lib/transpec/syntax/able_to_target_any_instance.rb +0 -24
- data/lib/transpec/syntax/expectizable.rb +0 -27
- data/lib/transpec/syntax/send_node_syntax.rb +0 -57
data/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::
|
64
|
+
<%= Transpec::Converter.new.convert(example) -%>
|
65
65
|
```
|
66
66
|
|
67
|
-
###
|
67
|
+
### Actual Examples
|
68
68
|
|
69
|
-
You can see
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
### `-
|
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
|
-
### `-
|
170
|
+
### `-k/--keep`
|
150
171
|
|
151
|
-
|
172
|
+
Keep specific syntaxes by disabling conversions.
|
152
173
|
|
153
174
|
```bash
|
154
|
-
$ transpec --
|
175
|
+
$ transpec --keep should_receive,stub
|
155
176
|
```
|
156
177
|
|
157
|
-
#### Available
|
178
|
+
#### Available syntax types
|
158
179
|
|
159
|
-
|
160
|
-
|
180
|
+
Type | Target Syntax | Converted Syntax
|
181
|
+
-----------------|----------------------------------|----------------------------
|
161
182
|
<%=
|
162
183
|
conversion_type_table = <<END
|
163
|
-
`
|
164
|
-
`
|
165
|
-
`
|
166
|
-
`
|
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::
|
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
|
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::
|
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
|
-
|
228
|
-
|
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
|
-
##
|
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`
|
270
|
-
* `expect` is defined on `RSpec::Matchers`
|
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
|
-
|
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: `--
|
295
|
-
*
|
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
|
-
*
|
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: `--
|
328
|
-
*
|
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
|
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: `--
|
343
|
-
*
|
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 `--
|
411
|
+
lambda { do_something }.should_not raise_error # with `--keep should`
|
357
412
|
```
|
358
413
|
|
359
|
-
* Disabled by: `--
|
360
|
-
*
|
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: `--
|
375
|
-
*
|
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 `--
|
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 `--
|
447
|
+
SomeClass.any_instance.stub(:foo) # with `--keep stub`
|
393
448
|
```
|
394
449
|
|
395
|
-
* Disabled by: `--
|
396
|
-
*
|
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: `--
|
422
|
-
*
|
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 `--
|
487
|
+
obj.stub(:foo) # with `--keep stub`
|
433
488
|
obj.unstub(:foo)
|
434
489
|
```
|
435
490
|
|
436
|
-
* Disabled by: `--
|
437
|
-
*
|
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 `--
|
503
|
+
obj.stub(:foo) # with `--keep stub`
|
449
504
|
```
|
450
505
|
|
451
|
-
* Disabled by: `--
|
452
|
-
*
|
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: `--
|
466
|
-
*
|
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
|
|
data/lib/transpec/ast/node.rb
CHANGED
@@ -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
|