transpec 0.2.6 → 1.0.0
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/.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
|