defmastership 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.gitlab-ci.yml +0 -1
  4. data/.rubocop.yml +1 -1
  5. data/Rakefile +1 -2
  6. data/bin/defmastership +36 -24
  7. data/cucumber.yml +2 -0
  8. data/defmastership.gemspec +14 -8
  9. data/features/changeref.feature +82 -129
  10. data/features/definition_checksum.feature +298 -0
  11. data/features/definition_version.feature +24 -0
  12. data/features/export.feature +49 -31
  13. data/features/modify.feature +165 -0
  14. data/features/rename_included_files.feature +121 -0
  15. data/lib/defmastership.rb +14 -3
  16. data/lib/defmastership/batch_modifier.rb +35 -0
  17. data/lib/defmastership/{ref_changer.rb → change_ref_line_modifier.rb} +18 -35
  18. data/lib/defmastership/change_ref_modifier.rb +15 -0
  19. data/lib/defmastership/constants.rb +14 -1
  20. data/lib/defmastership/csv_formatter.rb +25 -19
  21. data/lib/defmastership/csv_formatter_body.rb +19 -11
  22. data/lib/defmastership/csv_formatter_header.rb +15 -10
  23. data/lib/defmastership/definition.rb +14 -3
  24. data/lib/defmastership/definition_parser.rb +46 -0
  25. data/lib/defmastership/document.rb +59 -85
  26. data/lib/defmastership/filters.rb +30 -0
  27. data/lib/defmastership/line_modifier_base.rb +29 -0
  28. data/lib/defmastership/modifier_base.rb +29 -0
  29. data/lib/defmastership/rename_included_files_line_modifier.rb +126 -0
  30. data/lib/defmastership/rename_included_files_modifier.rb +15 -0
  31. data/lib/defmastership/update_def_checksum_line_modifier.rb +38 -0
  32. data/lib/defmastership/update_def_checksum_modifier.rb +21 -0
  33. data/lib/defmastership/version.rb +1 -1
  34. data/spec/spec_helper.rb +1 -0
  35. data/spec/unit/defmastership/batch_modifier_spec.rb +123 -0
  36. data/spec/unit/defmastership/{ref_changer_spec.rb → change_ref_line_modifier_spec.rb} +48 -26
  37. data/spec/unit/defmastership/change_ref_modifier_spec.rb +76 -0
  38. data/spec/unit/defmastership/comment_filter_spec.rb +8 -4
  39. data/spec/unit/defmastership/csv_formatter_body_spec.rb +88 -82
  40. data/spec/unit/defmastership/csv_formatter_header_spec.rb +68 -22
  41. data/spec/unit/defmastership/csv_formatter_spec.rb +208 -110
  42. data/spec/unit/defmastership/definition_parser_spec.rb +63 -0
  43. data/spec/unit/defmastership/definition_spec.rb +45 -4
  44. data/spec/unit/defmastership/document_spec.rb +236 -35
  45. data/spec/unit/defmastership/rename_included_files_line_modifier_spec.rb +203 -0
  46. data/spec/unit/defmastership/rename_included_files_modifier_spec.rb +67 -0
  47. data/spec/unit/defmastership/update_def_checksum_line_modifier_spec.rb +78 -0
  48. data/spec/unit/defmastership/update_def_checksum_modifier_spec.rb +75 -0
  49. metadata +47 -16
  50. data/Gemfile.lock +0 -140
  51. data/lib/defmastership/batch_changer.rb +0 -41
  52. data/lib/defmastership/project_ref_changer.rb +0 -28
  53. data/spec/unit/defmastership/batch_changer_spec.rb +0 -109
  54. data/spec/unit/defmastership/project_ref_changer_spec.rb +0 -80
@@ -4,6 +4,8 @@
4
4
  require('defmastership')
5
5
 
6
6
  RSpec.describe(DefMastership::Document) do
7
+ subject(:document) { described_class.new }
8
+
7
9
  describe '.new' do
8
10
  it { is_expected.not_to(be(nil)) }
9
11
  it { is_expected.to(have_attributes(definitions: [])) }
@@ -14,8 +16,6 @@ RSpec.describe(DefMastership::Document) do
14
16
  end
15
17
 
16
18
  describe '#parse' do
17
- subject(:document) { described_class.new }
18
-
19
19
  context 'with valid definitions' do
20
20
  let(:definition) { instance_double(DefMastership::Definition, 'definition') }
21
21
 
@@ -31,22 +31,66 @@ RSpec.describe(DefMastership::Document) do
31
31
  let(:input_lines) { ['[define, requirement, TOTO-0001]'] }
32
32
 
33
33
  before do
34
- allow(DefMastership::Definition).to(receive(:new).with(
35
- matchdata_including(
36
- type: 'requirement',
37
- reference: 'TOTO-0001'
38
- )
39
- ).and_return(definition))
34
+ allow(DefMastership::Definition).to(
35
+ receive(:new).with(
36
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001')
37
+ ).and_return(definition)
38
+ )
40
39
  document.parse(input_lines)
41
40
  end
42
41
 
43
42
  it do
44
- expect(DefMastership::Definition).to(have_received(:new).with(
45
- matchdata_including(type: 'requirement', reference: 'TOTO-0001')
46
- ))
43
+ expect(DefMastership::Definition).to(
44
+ have_received(:new).with(
45
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001')
46
+ )
47
+ )
47
48
  end
48
49
 
49
50
  it { expect(document).to(have_attributes(definitions: [definition])) }
51
+ it { expect(definition).to(have_received(:labels)) }
52
+ end
53
+
54
+ context 'when simple definition line with explicit checksum' do
55
+ let(:input_lines) { ['[define, requirement, TOTO-0001(~ab12)]'] }
56
+
57
+ before do
58
+ allow(DefMastership::Definition).to(
59
+ receive(:new).with(
60
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', explicit_checksum: '~ab12')
61
+ ).and_return(definition)
62
+ )
63
+ document.parse(input_lines)
64
+ end
65
+
66
+ it do
67
+ expect(DefMastership::Definition).to(
68
+ have_received(:new).with(
69
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', explicit_checksum: '~ab12')
70
+ )
71
+ )
72
+ end
73
+ end
74
+
75
+ context 'when simple definition line with explicit version' do
76
+ let(:input_lines) { ['[define, requirement, TOTO-0001(pouet)]'] }
77
+
78
+ before do
79
+ allow(DefMastership::Definition).to(
80
+ receive(:new).with(
81
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', explicit_version: 'pouet')
82
+ ).and_return(definition)
83
+ )
84
+ document.parse(input_lines)
85
+ end
86
+
87
+ it do
88
+ expect(DefMastership::Definition).to(
89
+ have_received(:new).with(
90
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', explicit_version: 'pouet')
91
+ )
92
+ )
93
+ end
50
94
  end
51
95
 
52
96
  context 'when complete definition with content' do
@@ -133,11 +177,11 @@ RSpec.describe(DefMastership::Document) do
133
177
  end
134
178
 
135
179
  it do
136
- expect(DefMastership::Definition).to(have_received(:new).with(matchdata_including(
137
- type: 'requirement',
138
- reference: 'TOTO-0001',
139
- labels: 'label1, label2'
140
- )))
180
+ expect(DefMastership::Definition).to(
181
+ have_received(:new).with(
182
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', labels: 'label1, label2')
183
+ )
184
+ )
141
185
  end
142
186
 
143
187
  it { expect(document.labels).to(eq(Set['bla1', 'bla2'])) }
@@ -158,11 +202,11 @@ RSpec.describe(DefMastership::Document) do
158
202
  end
159
203
 
160
204
  it do
161
- expect(DefMastership::Definition).to(have_received(:new).with(matchdata_including(
162
- type: 'requirement',
163
- reference: 'TOTO-0001',
164
- labels: 'label1,label2'
165
- )))
205
+ expect(DefMastership::Definition).to(
206
+ have_received(:new).with(
207
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001', labels: 'label1,label2')
208
+ )
209
+ )
166
210
  end
167
211
 
168
212
  it { expect(document.labels).to(eq(Set['bla1', 'bla2'])) }
@@ -209,8 +253,10 @@ RSpec.describe(DefMastership::Document) do
209
253
  end
210
254
 
211
255
  before do
212
- allow(definition).to(receive(:add_eref).with(:implements, 'SYSTEM-0012, SYSTEM-0014')
213
- .and_return(definition))
256
+ allow(definition).to(
257
+ receive(:add_eref).with(:implements, 'SYSTEM-0012, SYSTEM-0014')
258
+ .and_return(definition)
259
+ )
214
260
  document.parse(input_lines)
215
261
  end
216
262
 
@@ -289,18 +335,20 @@ RSpec.describe(DefMastership::Document) do
289
335
  end
290
336
 
291
337
  before do
292
- allow(DefMastership::Definition).to(receive(:new).with(matchdata_including(
293
- type: 'requirement',
294
- reference: 'TOTO-0001'
295
- )).and_return(definition))
338
+ allow(DefMastership::Definition).to(
339
+ receive(:new).with(
340
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001')
341
+ ).and_return(definition)
342
+ )
296
343
  document.parse(input_lines)
297
344
  end
298
345
 
299
346
  it do
300
- expect(DefMastership::Definition).to(have_received(:new).with(matchdata_including(
301
- type: 'requirement',
302
- reference: 'TOTO-0001'
303
- )))
347
+ expect(DefMastership::Definition).to(
348
+ have_received(:new).with(
349
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001')
350
+ )
351
+ )
304
352
  end
305
353
  end
306
354
  end
@@ -365,8 +413,9 @@ RSpec.describe(DefMastership::Document) do
365
413
  end
366
414
 
367
415
  before do
368
- allow(DefMastership::Definition).to(receive(:new)
369
- .and_raise('not a valide definition'))
416
+ allow(DefMastership::Definition).to(
417
+ receive(:new).and_raise('not a valide definition')
418
+ )
370
419
  end
371
420
 
372
421
  it do
@@ -385,8 +434,9 @@ RSpec.describe(DefMastership::Document) do
385
434
  end
386
435
 
387
436
  before do
388
- allow(DefMastership::Definition).to(receive(:new)
389
- .and_raise('not a valide definition'))
437
+ allow(DefMastership::Definition).to(
438
+ receive(:new).and_raise('not a valide definition')
439
+ )
390
440
  end
391
441
 
392
442
  it do
@@ -396,4 +446,155 @@ RSpec.describe(DefMastership::Document) do
396
446
  end
397
447
  end
398
448
  end
449
+
450
+ describe '#parse_file_with_preprocessor' do
451
+ let(:definition) { instance_double(DefMastership::Definition, 'definition') }
452
+ let(:input_lines) { ['[define, requirement, TOTO-0001]'] }
453
+ let(:adoc_doc) { instance_double(Asciidoctor::Document, 'adoc_doc') }
454
+ let(:adoc_reader) { instance_double(Asciidoctor::Reader, 'adoc_reader') }
455
+
456
+ before do
457
+ allow(Asciidoctor).to(
458
+ receive(:load_file).with('the_file.adoc', { parse: false, safe: :unsafe }).and_return(adoc_doc)
459
+ )
460
+ allow(adoc_doc).to(receive(:reader).and_return(adoc_reader))
461
+ allow(adoc_reader).to(receive(:read_lines).and_return(input_lines))
462
+ allow(DefMastership::Definition).to(receive(:new).and_return(definition))
463
+ allow(definition).to(receive(:<<).and_return(definition))
464
+ allow(definition).to(receive(:labels).and_return(Set.new))
465
+ document.parse_file_with_preprocessor('the_file.adoc')
466
+ end
467
+
468
+ it { expect(Asciidoctor).to(have_received(:load_file).with('the_file.adoc', { parse: false, safe: :unsafe })) }
469
+ it { expect(adoc_doc).to(have_received(:reader).with(no_args)) }
470
+ it { expect(adoc_reader).to(have_received(:read_lines).with(no_args)) }
471
+
472
+ it do
473
+ expect(DefMastership::Definition).to(
474
+ have_received(:new).with(
475
+ matchdata_including(type: 'requirement', reference: 'TOTO-0001')
476
+ )
477
+ )
478
+ end
479
+ end
480
+
481
+ describe '#wrong_explicit_checksum?' do
482
+ let(:def1) { instance_double(DefMastership::Definition, 'definition') }
483
+ let(:def2) { instance_double(DefMastership::Definition, 'definition') }
484
+ let(:input_lines) do
485
+ [
486
+ '[define, requirement, TOTO-0001]',
487
+ 'def one',
488
+ '',
489
+ '[define, requirement, TOTO-0002]',
490
+ 'def two'
491
+ ]
492
+ end
493
+
494
+ before do
495
+ allow(DefMastership::Definition).to(receive(:new).twice.and_return(def1, def2))
496
+ allow(def1).to(receive(:labels)).and_return([])
497
+ allow(def2).to(receive(:labels)).and_return([])
498
+ allow(def1).to(receive(:<<).and_return(def1))
499
+ allow(def2).to(receive(:<<).and_return(def2))
500
+ end
501
+
502
+ context 'when no wrong explicit checksum' do
503
+ before do
504
+ allow(def1).to(receive(:wrong_explicit_checksum)).and_return(nil)
505
+ allow(def2).to(receive(:wrong_explicit_checksum)).and_return(nil)
506
+ document.parse(input_lines)
507
+ document.wrong_explicit_checksum?
508
+ end
509
+
510
+ it { expect(def1).to(have_received(:wrong_explicit_checksum)) }
511
+ it { expect(def2).to(have_received(:wrong_explicit_checksum)) }
512
+ it { expect(document.wrong_explicit_checksum?).to(eq(false)) }
513
+ end
514
+
515
+ context 'when one req has wrong explicit checksum' do
516
+ before do
517
+ allow(def1).to(receive(:wrong_explicit_checksum)).and_return(nil)
518
+ allow(def2).to(receive(:wrong_explicit_checksum)).and_return('toto')
519
+ document.parse(input_lines)
520
+ document.wrong_explicit_checksum?
521
+ end
522
+
523
+ it { expect(document.wrong_explicit_checksum?).to(eq(true)) }
524
+ end
525
+ end
526
+
527
+ describe '#explicit_version?' do
528
+ let(:def1) { instance_double(DefMastership::Definition, 'definition') }
529
+ let(:def2) { instance_double(DefMastership::Definition, 'definition') }
530
+ let(:input_lines) do
531
+ [
532
+ '[define, requirement, TOTO-0001]',
533
+ 'def one',
534
+ '',
535
+ '[define, requirement, TOTO-0002]',
536
+ 'def two'
537
+ ]
538
+ end
539
+
540
+ before do
541
+ allow(DefMastership::Definition).to(receive(:new).twice.and_return(def1, def2))
542
+ allow(def1).to(receive(:labels)).and_return([])
543
+ allow(def2).to(receive(:labels)).and_return([])
544
+ allow(def1).to(receive(:<<).and_return(def1))
545
+ allow(def2).to(receive(:<<).and_return(def2))
546
+ end
547
+
548
+ context 'when no explicit version' do
549
+ before do
550
+ allow(def1).to(receive(:explicit_version)).and_return(nil)
551
+ allow(def2).to(receive(:explicit_version)).and_return(nil)
552
+ document.parse(input_lines)
553
+ document.explicit_version?
554
+ end
555
+
556
+ it { expect(def1).to(have_received(:explicit_version)) }
557
+ it { expect(def2).to(have_received(:explicit_version)) }
558
+ it { expect(document.explicit_version?).to(eq(false)) }
559
+ end
560
+
561
+ context 'when one req has explicit version' do
562
+ before do
563
+ allow(def1).to(receive(:explicit_version)).and_return(nil)
564
+ allow(def2).to(receive(:explicit_version)).and_return('toto')
565
+ document.parse(input_lines)
566
+ document.explicit_version?
567
+ end
568
+
569
+ it { expect(document.explicit_version?).to(eq(true)) }
570
+ end
571
+ end
572
+
573
+ describe '#ref_to_def?' do
574
+ let(:def1) { instance_double(DefMastership::Definition, 'definition') }
575
+ let(:def2) { instance_double(DefMastership::Definition, 'definition') }
576
+ let(:input_lines) do
577
+ [
578
+ '[define, requirement, TOTO-0001]',
579
+ 'def one',
580
+ '',
581
+ '[define, requirement, TOTO-0002(~1234)]',
582
+ 'def two'
583
+ ]
584
+ end
585
+
586
+ before do
587
+ allow(DefMastership::Definition).to(receive(:new).twice.and_return(def1, def2))
588
+ allow(def1).to(receive(:labels)).and_return([])
589
+ allow(def2).to(receive(:labels)).and_return([])
590
+ allow(def1).to(receive(:<<).and_return(def1))
591
+ allow(def2).to(receive(:<<).and_return(def2))
592
+ allow(def1).to(receive(:reference).and_return('TOTO-0001'))
593
+ allow(def2).to(receive(:reference).and_return('TOTO-0002'))
594
+ document.parse(input_lines)
595
+ end
596
+
597
+ it { expect(document.ref_to_def('TOTO-0001')).to(eq(def1)) }
598
+ it { expect(document.ref_to_def('TOTO-0002')).to(eq(def2)) }
599
+ end
399
600
  end
@@ -0,0 +1,203 @@
1
+ # Copyright (c) 2021 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ require('defmastership')
5
+
6
+ RSpec.describe(DefMastership::RenameIncludedFilesLineModifier) do
7
+ subject(:includeschanger) { described_class.new }
8
+
9
+ describe '.new' do
10
+ it { is_expected.not_to(be(nil)) }
11
+ it { is_expected.to(have_attributes(from_regexp: '')) }
12
+ it { is_expected.to(have_attributes(to_template: '')) }
13
+ it { is_expected.to(have_attributes(changes: [])) }
14
+ it { expect { includeschanger.user_defined_attribute }.to(raise_error(NoMethodError)) }
15
+ end
16
+
17
+ describe '.from_config' do
18
+ subject(:includeschanger) do
19
+ described_class.from_config(
20
+ from_regexp: 'Whatever.+',
21
+ to_template: 'Whatever'
22
+ )
23
+ end
24
+
25
+ it { is_expected.not_to(be(nil)) }
26
+ it { is_expected.to(have_attributes(from_regexp: 'Whatever.+')) }
27
+ it { is_expected.to(have_attributes(to_template: 'Whatever')) }
28
+ end
29
+
30
+ describe '#replace' do
31
+ context 'when NOT valid include' do
32
+ before do
33
+ includeschanger.from_config(
34
+ from_regexp: 'orig',
35
+ cancel_if_match: 'REF',
36
+ to_template: 'dest'
37
+ )
38
+ allow(File).to(receive(:rename))
39
+ end
40
+
41
+ context 'when the include statement do not match' do
42
+ before do
43
+ ['[define,requirement,REFERENCE]', '--'].each do |line|
44
+ includeschanger.replace(line)
45
+ end
46
+ end
47
+
48
+ it do
49
+ expect(includeschanger.replace('badinclude::orig[]'))
50
+ .to(eq('badinclude::orig[]'))
51
+ end
52
+
53
+ it do
54
+ includeschanger.replace('badinclude::orig[]')
55
+ expect(includeschanger).to(have_attributes(changes: []))
56
+ end
57
+ end
58
+
59
+ context 'when the include is not in a definition' do
60
+ before do
61
+ ['[define,requirement,REFERENCE]', '--', 'text', '--'].each do |line|
62
+ includeschanger.replace(line)
63
+ end
64
+ end
65
+
66
+ it do
67
+ expect(includeschanger.replace('include::orig[]'))
68
+ .to(eq('include::orig[]'))
69
+ end
70
+
71
+ it do
72
+ includeschanger.replace('include::orig[]')
73
+ expect(includeschanger).to(have_attributes(changes: []))
74
+ end
75
+ end
76
+
77
+ context 'when the cancel regexp is met' do
78
+ before do
79
+ ['[define,requirement,REFERENCE]', '--'].each do |line|
80
+ includeschanger.replace(line)
81
+ end
82
+ end
83
+
84
+ it do
85
+ expect(includeschanger.replace('include::REFERENCE_orig[]'))
86
+ .to(eq('include::REFERENCE_orig[]'))
87
+ end
88
+
89
+ it do
90
+ includeschanger.replace('include::REFERENCE_orig[]')
91
+ expect(includeschanger).to(have_attributes(changes: []))
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'when valid include' do
97
+ before do
98
+ allow(File).to(receive(:rename))
99
+ end
100
+
101
+ context 'when really simple rule' do
102
+ before do
103
+ includeschanger.from_config(
104
+ from_regexp: 'orig',
105
+ cancel_if_match: 'REF',
106
+ to_template: 'dest'
107
+ )
108
+ includeschanger.replace('[define,requirement,REFERENCE]')
109
+ includeschanger.replace('--')
110
+ end
111
+
112
+ it do
113
+ expect(includeschanger.replace('include::orig[]'))
114
+ .to(eq('include::dest[]'))
115
+ end
116
+
117
+ it do
118
+ includeschanger.replace('include::orig[]')
119
+ expect(File).to(have_received(:rename).with('orig', 'dest'))
120
+ end
121
+
122
+ it do
123
+ includeschanger.replace('include::orig[]')
124
+ expect(includeschanger).to(have_attributes(changes: [%w[orig dest]]))
125
+ end
126
+
127
+ it do
128
+ expect(includeschanger.replace('include::toto/orig[]'))
129
+ .to(eq('include::toto/dest[]'))
130
+ end
131
+
132
+ it do
133
+ includeschanger.replace('include::toto/orig[]')
134
+ expect(File).to(have_received(:rename).with('toto/orig', 'toto/dest'))
135
+ end
136
+
137
+ it do
138
+ includeschanger.replace('include::toto/orig[]')
139
+ expect(includeschanger).to(have_attributes(changes: [%w[toto/orig toto/dest]]))
140
+ end
141
+ end
142
+
143
+ context 'when complex from_regexp' do
144
+ before do
145
+ includeschanger.from_config(
146
+ from_regexp: '(?<origin>.*\.extension)',
147
+ to_template: '%<reference>s_%<origin>s'
148
+ )
149
+ includeschanger.replace('[define,requirement,REF]')
150
+ end
151
+
152
+ it do
153
+ expect(includeschanger.replace('include::any_path/one_file.extension[]'))
154
+ .to(eq('include::any_path/REF_one_file.extension[]'))
155
+ end
156
+
157
+ it do
158
+ includeschanger.replace('include::any_path/one_file.extension[]')
159
+ expect(File).to(have_received(:rename).with('any_path/one_file.extension', 'any_path/REF_one_file.extension'))
160
+ end
161
+
162
+ it do
163
+ changes = [%w[any_path/one_file.extension any_path/REF_one_file.extension]]
164
+ includeschanger.replace('include::any_path/one_file.extension[]')
165
+ expect(includeschanger).to(have_attributes(changes: changes))
166
+ end
167
+ end
168
+
169
+ context 'when path with variable' do
170
+ before do
171
+ includeschanger.from_config(
172
+ from_regexp: 'orig',
173
+ to_template: 'dest'
174
+ )
175
+ includeschanger.replace(':any: one')
176
+ includeschanger.replace(':path: two')
177
+ includeschanger.replace('[define,requirement,REFERENCE]')
178
+ includeschanger.replace('--')
179
+ end
180
+
181
+ it do
182
+ expect(includeschanger.replace('include::{any}/orig[]'))
183
+ .to(eq('include::{any}/dest[]'))
184
+ end
185
+
186
+ it do
187
+ includeschanger.replace('include::{any}/orig[]')
188
+ expect(File).to(have_received(:rename).with('one/orig', 'one/dest'))
189
+ end
190
+
191
+ it do
192
+ includeschanger.replace('include::{any}_{path}/orig[]')
193
+ expect(File).to(have_received(:rename).with('one_two/orig', 'one_two/dest'))
194
+ end
195
+
196
+ it do
197
+ includeschanger.replace('include::{any}_{path}/orig[]')
198
+ expect(includeschanger).to(have_attributes(changes: [%w[one_two/orig one_two/dest]]))
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end