metamorpher 0.1.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.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +3 -0
  6. data/Gemfile +5 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +541 -0
  9. data/Rakefile +23 -0
  10. data/examples/refactorings/rails/where_first/app.rb +50 -0
  11. data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_mocks.rb +31 -0
  12. data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_not_called_expectations.rb +14 -0
  13. data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_strict_mocks.rb +27 -0
  14. data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_to_find_by.rb +14 -0
  15. data/examples/refactorings/rails/where_first/sample_controller.rb +184 -0
  16. data/lib/metamorpher/builders/ast/builder.rb +50 -0
  17. data/lib/metamorpher/builders/ast/derivation_builder.rb +20 -0
  18. data/lib/metamorpher/builders/ast/greedy_variable_builder.rb +29 -0
  19. data/lib/metamorpher/builders/ast/literal_builder.rb +31 -0
  20. data/lib/metamorpher/builders/ast/variable_builder.rb +29 -0
  21. data/lib/metamorpher/builders/ast.rb +11 -0
  22. data/lib/metamorpher/builders/ruby/builder.rb +38 -0
  23. data/lib/metamorpher/builders/ruby/deriving_visitor.rb +13 -0
  24. data/lib/metamorpher/builders/ruby/ensuring_visitor.rb +13 -0
  25. data/lib/metamorpher/builders/ruby/term.rb +35 -0
  26. data/lib/metamorpher/builders/ruby/uppercase_constant_rewriter.rb +31 -0
  27. data/lib/metamorpher/builders/ruby/uppercase_rewriter.rb +28 -0
  28. data/lib/metamorpher/builders/ruby/variable_replacement_visitor.rb +32 -0
  29. data/lib/metamorpher/builders/ruby.rb +11 -0
  30. data/lib/metamorpher/drivers/parse_error.rb +5 -0
  31. data/lib/metamorpher/drivers/ruby.rb +78 -0
  32. data/lib/metamorpher/matcher/match.rb +26 -0
  33. data/lib/metamorpher/matcher/matching.rb +61 -0
  34. data/lib/metamorpher/matcher/no_match.rb +18 -0
  35. data/lib/metamorpher/matcher.rb +6 -0
  36. data/lib/metamorpher/refactorer/merger.rb +18 -0
  37. data/lib/metamorpher/refactorer/site.rb +29 -0
  38. data/lib/metamorpher/refactorer.rb +48 -0
  39. data/lib/metamorpher/rewriter/replacement.rb +18 -0
  40. data/lib/metamorpher/rewriter/rule.rb +38 -0
  41. data/lib/metamorpher/rewriter/substitution.rb +45 -0
  42. data/lib/metamorpher/rewriter/traverser.rb +26 -0
  43. data/lib/metamorpher/rewriter.rb +12 -0
  44. data/lib/metamorpher/support/map_at.rb +8 -0
  45. data/lib/metamorpher/terms/derived.rb +13 -0
  46. data/lib/metamorpher/terms/literal.rb +47 -0
  47. data/lib/metamorpher/terms/term.rb +40 -0
  48. data/lib/metamorpher/terms/variable.rb +17 -0
  49. data/lib/metamorpher/version.rb +3 -0
  50. data/lib/metamorpher/visitable/visitable.rb +7 -0
  51. data/lib/metamorpher/visitable/visitor.rb +21 -0
  52. data/lib/metamorpher.rb +30 -0
  53. data/metamorpher.gemspec +30 -0
  54. data/spec/integration/ast/builder_spec.rb +13 -0
  55. data/spec/integration/ast/matcher_spec.rb +132 -0
  56. data/spec/integration/ast/rewriter_spec.rb +138 -0
  57. data/spec/integration/ruby/builder_spec.rb +125 -0
  58. data/spec/integration/ruby/refactorer_spec.rb +192 -0
  59. data/spec/spec_helper.rb +29 -0
  60. data/spec/support/helpers/silence_stream.rb +10 -0
  61. data/spec/support/matchers/have_matched_matcher.rb +22 -0
  62. data/spec/support/matchers/have_substitution_matcher.rb +15 -0
  63. data/spec/support/shared_examples/shared_examples_for_derivation_builders.rb +53 -0
  64. data/spec/support/shared_examples/shared_examples_for_greedy_variable_builders.rb +49 -0
  65. data/spec/support/shared_examples/shared_examples_for_literal_builders.rb +93 -0
  66. data/spec/support/shared_examples/shared_examples_for_variable_builders.rb +49 -0
  67. data/spec/unit/builders/ast/derivation_builder_spec.rb +5 -0
  68. data/spec/unit/builders/ast/greedy_variable_builder_spec.rb +9 -0
  69. data/spec/unit/builders/ast/literal_builder_spec.rb +9 -0
  70. data/spec/unit/builders/ast/variable_builder_spec.rb +9 -0
  71. data/spec/unit/builders/ruby/variable_replacement_visitor_spec.rb +48 -0
  72. data/spec/unit/drivers/ruby_spec.rb +91 -0
  73. data/spec/unit/matcher/matching_spec.rb +230 -0
  74. data/spec/unit/metamorpher_spec.rb +22 -0
  75. data/spec/unit/refactorer/merger_spec.rb +84 -0
  76. data/spec/unit/refactorer/site_spec.rb +52 -0
  77. data/spec/unit/rewriter/replacement_spec.rb +73 -0
  78. data/spec/unit/rewriter/substitution_spec.rb +97 -0
  79. data/spec/unit/rewriter/traverser_spec.rb +51 -0
  80. data/spec/unit/support/map_at_spec.rb +18 -0
  81. data/spec/unit/terms/literal_spec.rb +60 -0
  82. data/spec/unit/terms/term_spec.rb +59 -0
  83. data/spec/unit/visitable/visitor_spec.rb +35 -0
  84. metadata +269 -0
@@ -0,0 +1,15 @@
1
+ require "rspec/expectations"
2
+
3
+ RSpec::Matchers.define :have_substitution do |expected|
4
+ match do |actual|
5
+ actual.substitution == expected
6
+ end
7
+
8
+ failure_message_for_should do |actual|
9
+ "expected the substitution #{actual.substitution}, but got #{expected}"
10
+ end
11
+
12
+ failure_message_for_should_not do |actual|
13
+ "expected to not receive the substitution #{actual.substitution}"
14
+ end
15
+ end
@@ -0,0 +1,53 @@
1
+ require "metamorpher/terms/literal"
2
+
3
+ module Metamorpher
4
+ module Terms
5
+ shared_examples "a derivation builder" do
6
+ describe "derivation!" do
7
+ it "should create an instance of Derivation" do
8
+ built = subject.derivation!(:method) do |method|
9
+ Literal.new(name: (method.name.to_s + "s").to_sym)
10
+ end
11
+
12
+ expect(built.base).to eq([:method])
13
+ expect(built.derivation.call(Literal.new(name: :dog))).to eq(Literal.new(name: :dogs))
14
+ end
15
+
16
+ it "should capture all arguments as the base" do
17
+ built = subject.derivation!(:key, :value) {}
18
+
19
+ expect(built.base).to eq([:key, :value])
20
+ end
21
+
22
+ it "should provide a builder for use in the block" do
23
+ built = subject.derivation!(:key, :value) do |key, value, builder|
24
+ builder.pair(key, value)
25
+ end
26
+
27
+ derived = built.derivation.call(
28
+ Literal.new(name: :dog),
29
+ Literal.new(name: :lassie)
30
+ )
31
+
32
+ paired = Literal.new(
33
+ name: :pair,
34
+ children: [
35
+ Literal.new(name: :dog),
36
+ Literal.new(name: :lassie)
37
+ ]
38
+ )
39
+
40
+ expect(derived).to eq(paired)
41
+ end
42
+
43
+ it "should raise if no arguments are passed" do
44
+ expect { subject.derivation! { nil } }.to raise_error(ArgumentError)
45
+ end
46
+
47
+ it "should raise if no block is passed" do
48
+ expect { subject.derivation!(:method) }.to raise_error(ArgumentError)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ require "metamorpher/terms/variable"
2
+
3
+ module Metamorpher
4
+ module Terms
5
+ shared_examples "a greedy variable builder" do
6
+ describe "greedy_variable!" do
7
+ it "should create an instance of Variable with greedy? set to true" do
8
+ actual = subject.greedy_variable!(:a)
9
+ expected = Variable.new(name: :a, greedy?: true)
10
+
11
+ expect(actual).to eq(expected)
12
+ end
13
+
14
+ it "should create condition from block" do
15
+ built = subject.greedy_variable!(:a) { |term| term > 0 }
16
+
17
+ expect(built.name).to eq(:a)
18
+ expect(built.condition.call(1)).to be_true
19
+ expect(built.condition.call(-1)).to be_false
20
+ end
21
+
22
+ it "should not allow children" do
23
+ expect { subject.greedy_variable!(:a, 1) }.to raise_error(ArgumentError)
24
+ end
25
+ end
26
+
27
+ describe "greedy variable shorthand" do
28
+ it "should create an instance of Variable with greedy? set to true" do
29
+ actual = subject.A_
30
+ expected = Variable.new(name: :a, greedy?: true)
31
+
32
+ expect(actual).to eq(expected)
33
+ end
34
+
35
+ it "should create condition from block" do
36
+ built = subject.A_ { |term| term > 0 }
37
+
38
+ expect(built.name).to eq(:a)
39
+ expect(built.condition.call(1)).to be_true
40
+ expect(built.condition.call(-1)).to be_false
41
+ end
42
+
43
+ it "should not allow children" do
44
+ expect { subject.A_(1) }.to raise_error(ArgumentError)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,93 @@
1
+ require "metamorpher/terms/literal"
2
+ require "metamorpher/terms/variable"
3
+
4
+ module Metamorpher
5
+ module Terms
6
+ shared_examples_for "a literal builder" do
7
+ describe "literal!" do
8
+ it "should create an instance of Literal" do
9
+ actual = subject.literal!(:a)
10
+ expected = Literal.new(name: :a)
11
+
12
+ expect(actual).to eq(expected)
13
+ end
14
+
15
+ it "should work with numeric names" do
16
+ actual = subject.literal!(4)
17
+ expected = Literal.new(name: 4)
18
+
19
+ expect(actual).to eq(expected)
20
+ end
21
+
22
+ it "should capture single children" do
23
+ actual = subject.literal!(:inc, subject.literal!(1))
24
+ expected = Literal.new(name: :inc, children: [Literal.new(name: 1)])
25
+
26
+ expect(actual).to eq(expected)
27
+ end
28
+
29
+ it "should capture several children" do
30
+ actual = subject.literal!(:add, subject.literal!(1), subject.literal!(2))
31
+ expected = Literal.new(
32
+ name: :add,
33
+ children: [
34
+ Literal.new(name: 1),
35
+ Literal.new(name: 2)
36
+ ]
37
+ )
38
+
39
+ expect(actual).to eq(expected)
40
+ end
41
+
42
+ it "should automatically convert children to literals" do
43
+ actual = subject.literal!(:add, 1, 2)
44
+ expected = Literal.new(
45
+ name: :add,
46
+ children: [
47
+ Literal.new(name: 1),
48
+ Literal.new(name: 2)
49
+ ]
50
+ )
51
+
52
+ expect(actual).to eq(expected)
53
+ end
54
+
55
+ it "should not automatically convert children that are already terms" do
56
+ actual = subject.literal!(:inc, Variable.new(name: :a))
57
+ expected = Literal.new(name: :inc, children: [Variable.new(name: :a)])
58
+
59
+ expect(actual).to eq(expected)
60
+ end
61
+ end
62
+
63
+ describe "literal shorthand" do
64
+ it "should create an instance of Literal" do
65
+ actual = subject.a
66
+ expected = Literal.new(name: :a)
67
+
68
+ expect(actual).to eq(expected)
69
+ end
70
+
71
+ it "should work for longer names" do
72
+ actual = subject.this_is_a_longer_name
73
+ expected = Literal.new(name: :this_is_a_longer_name)
74
+
75
+ expect(actual).to eq(expected)
76
+ end
77
+
78
+ it "should capture children" do
79
+ actual = subject.add(1, 2)
80
+ expected = Literal.new(
81
+ name: :add,
82
+ children: [
83
+ Literal.new(name: 1),
84
+ Literal.new(name: 2)
85
+ ]
86
+ )
87
+
88
+ expect(actual).to eq(expected)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,49 @@
1
+ require "metamorpher/terms/variable"
2
+
3
+ module Metamorpher
4
+ module Terms
5
+ shared_examples "a variable builder" do
6
+ describe "variable!" do
7
+ it "should create an instance of Variable" do
8
+ actual = subject.variable!(:a)
9
+ expected = Variable.new(name: :a)
10
+
11
+ expect(actual).to eq(expected)
12
+ end
13
+
14
+ it "should create condition from block" do
15
+ built = subject.variable!(:a) { |term| term > 0 }
16
+
17
+ expect(built.name).to eq(:a)
18
+ expect(built.condition.call(1)).to be_true
19
+ expect(built.condition.call(-1)).to be_false
20
+ end
21
+
22
+ it "should not allow children" do
23
+ expect { subject.variable!(:a, 1) }.to raise_error(ArgumentError)
24
+ end
25
+ end
26
+
27
+ describe "variable shorthand" do
28
+ it "should create an instance of Variable" do
29
+ actual = subject.A
30
+ expected = Variable.new(name: :a)
31
+
32
+ expect(actual).to eq(expected)
33
+ end
34
+
35
+ it "should create condition from block" do
36
+ built = subject.A { |term| term > 0 }
37
+
38
+ expect(built.name).to eq(:a)
39
+ expect(built.condition.call(1)).to be_true
40
+ expect(built.condition.call(-1)).to be_false
41
+ end
42
+
43
+ it "should not allow children" do
44
+ expect { subject.A(1) }.to raise_error(ArgumentError)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ require "metamorpher/builders/ast/derivation_builder"
2
+
3
+ describe Metamorpher::Builders::AST::DerivationBuilder do
4
+ it_behaves_like "a derivation builder"
5
+ end
@@ -0,0 +1,9 @@
1
+ require "metamorpher/builders/ast/greedy_variable_builder"
2
+
3
+ describe Metamorpher::Builders::AST::GreedyVariableBuilder do
4
+ it_behaves_like "a greedy variable builder"
5
+
6
+ it "should raise when incorrect shorthand is used" do
7
+ expect { subject.inc }.to raise_error(NoMethodError)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require "metamorpher/builders/ast/literal_builder"
2
+
3
+ describe Metamorpher::Builders::AST::LiteralBuilder do
4
+ it_behaves_like "a literal builder"
5
+
6
+ it "should raise when variable shorthand is used" do
7
+ expect { subject.INC }.to raise_error(NoMethodError)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require "metamorpher/builders/ast/variable_builder"
2
+
3
+ describe Metamorpher::Builders::AST::VariableBuilder do
4
+ it_behaves_like "a variable builder"
5
+
6
+ it "should raise when incorrect shorthand is used" do
7
+ expect { subject.inc }.to raise_error(NoMethodError)
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ require "metamorpher/builders/ruby/variable_replacement_visitor"
2
+
3
+ module Metamorpher
4
+ module Builders
5
+ module Ruby
6
+ describe VariableReplacementVisitor do
7
+ let(:builder) { Metamorpher::Builders::AST::Builder.new }
8
+ let(:replacement) { builder.literal!(:bar) }
9
+ subject { VariableReplacementVisitor.new(:foo, replacement) }
10
+
11
+ it "should replace a variable with the correct name" do
12
+ original = builder.variable!(:foo)
13
+ replaced = subject.visit(original)
14
+
15
+ expect(replaced).to eq(replacement)
16
+ end
17
+
18
+ it "should replace a nested variable" do
19
+ original = builder.literal!(:+, 2, builder.variable!(:foo))
20
+ replaced = subject.visit(original)
21
+
22
+ expect(replaced).to eq(original.replace(original.children.last.path, replacement))
23
+ end
24
+
25
+ it "should not replace a variable with a different name" do
26
+ original = builder.variable!(:bar)
27
+ replaced = subject.visit(original)
28
+
29
+ expect(replaced).to eq(original)
30
+ end
31
+
32
+ it "should not replace a literal" do
33
+ original = builder.literal!(:foo)
34
+ replaced = subject.visit(original)
35
+
36
+ expect(replaced).to eq(original)
37
+ end
38
+
39
+ it "should not replace a derived" do
40
+ original = builder.derivation!(:foo) {}
41
+ replaced = subject.visit(original)
42
+
43
+ expect(replaced).to eq(original)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,91 @@
1
+ require "metamorpher/drivers/ruby"
2
+ require "metamorpher/builders/ast/builder"
3
+
4
+ module Metamorpher
5
+ module Drivers
6
+ describe Ruby do
7
+ let(:builder) { Builders::AST::Builder.new }
8
+
9
+ describe "for a simple program" do
10
+ let(:source) { "1 + 2" }
11
+ let(:literal) { builder.literal!(:send, builder.int(1), :+, builder.int(2)) }
12
+
13
+ it "should parse a simple program to literals" do
14
+ expect(subject.parse(source)).to eq(literal)
15
+ end
16
+
17
+ it "should unparse valid literals to source" do
18
+ expect(subject.unparse(literal)).to eq(source)
19
+ end
20
+
21
+ it "should provide source location of literals" do
22
+ subject.parse(source)
23
+
24
+ expect(subject.source_location_for(literal)).to eq(0..4)
25
+ expect(subject.source_location_for(literal.children.first)).to eq(0..0)
26
+ expect(subject.source_location_for(literal.children.last)).to eq(4..4)
27
+ end
28
+ end
29
+
30
+ describe "for program containing identical statements" do
31
+ let(:source) { "1 + 1" }
32
+ let(:literal) { builder.literal!(:send, builder.int(1), :+, builder.int(1)) }
33
+
34
+ it "should provide different source locations for syntactically equal literals" do
35
+ subject.parse(source)
36
+
37
+ expect(subject.source_location_for(literal.children.first)).to eq(0..0)
38
+ expect(subject.source_location_for(literal.children.last)).to eq(4..4)
39
+ end
40
+ end
41
+
42
+ describe "for program that parses to an AST containing nils" do
43
+ let(:source) { "LEFT + RIGHT" }
44
+ let(:literal) do
45
+ builder.literal!(
46
+ :send,
47
+ builder.const(nil, :LEFT),
48
+ :+,
49
+ builder.const(nil, :RIGHT)
50
+ )
51
+ end
52
+
53
+ it "should parse a simple program to literals" do
54
+ expect(subject.parse(source)).to eq(literal)
55
+ end
56
+
57
+ it "should unparse valid literals to source" do
58
+ expect(subject.unparse(literal)).to eq(source)
59
+ end
60
+ end
61
+
62
+ %w(nil true false self).each do |keyword|
63
+ describe "for a program containing the '#{keyword}' keyword" do
64
+ let(:source) { "a = #{keyword}" }
65
+ let(:literal) { builder.lvasgn(:a, keyword.to_sym) }
66
+
67
+ it "should parse to the correct literal" do
68
+ expect(subject.parse(source)).to eq(literal)
69
+ end
70
+
71
+ it "should unparse to the correct source" do
72
+ expect(subject.unparse(literal)).to eq(source)
73
+ end
74
+ end
75
+
76
+ describe "for a program that is the '#{keyword}' keyword" do
77
+ let(:source) { keyword }
78
+ let(:literal) { builder.literal! keyword.to_sym }
79
+
80
+ it "should parse to the correct literal" do
81
+ expect(subject.parse(source)).to eq(literal)
82
+ end
83
+
84
+ it "should unparse to the correct source" do
85
+ expect(subject.unparse(literal)).to eq(source)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,230 @@
1
+ require "metamorpher/terms/variable"
2
+ require "metamorpher/terms/literal"
3
+ require "metamorpher/terms/derived"
4
+
5
+ module Metamorpher
6
+ module Terms
7
+ describe Variable do
8
+ let(:root) do
9
+ Literal.new(
10
+ name: :root,
11
+ children: [
12
+ Literal.new(name: :first_child),
13
+ Literal.new(name: :second_child),
14
+ Literal.new(name: :third_child)
15
+ ]
16
+ )
17
+ end
18
+
19
+ let(:first_child) { root.children[0] }
20
+ let(:second_child) { root.children[1] }
21
+ let(:third_child) { root.children[2] }
22
+
23
+ describe "unconditional, non-greedy variable" do
24
+ subject { Variable.new(name: :type) }
25
+
26
+ it "should match any literal" do
27
+ expect(subject.match(root)).to have_matched(root)
28
+ end
29
+
30
+ it "should include the match in the substitution" do
31
+ expect(subject.match(root)).to have_substitution(type: root)
32
+ end
33
+
34
+ it "shouldn't match nil" do
35
+ expect(subject.match(nil)).not_to have_matched
36
+ end
37
+ end
38
+
39
+ describe "conditional, non-greedy variable" do
40
+ subject { Variable.new(name: :type, condition: -> (l) { l.name == :third_child }) }
41
+
42
+ it "should match when literal matches condition" do
43
+ expect(subject.match(third_child)).to have_matched(third_child)
44
+ end
45
+
46
+ it "shouldn't match when literal doesn't match condition" do
47
+ expect(subject.match(second_child)).not_to have_matched
48
+ end
49
+
50
+ it "shouldn't match nil" do
51
+ expect(subject.match(nil)).not_to have_matched
52
+ end
53
+ end
54
+
55
+ describe "unconditional, greedy variable" do
56
+ subject { Variable.new(name: :type, greedy?: true) }
57
+
58
+ it "should match against literal and all younger siblings" do
59
+ result = subject.match(second_child)
60
+
61
+ expect(result).to have_matched([second_child, third_child])
62
+ expect(result).to have_substitution(type: [second_child, third_child])
63
+ end
64
+
65
+ it "should match against literal when there is no parent" do
66
+ result = subject.match(root)
67
+
68
+ expect(result).to have_matched([root])
69
+ expect(result).to have_substitution(type: [root])
70
+ end
71
+
72
+ it "should allow parent to match when there are a different number of children" do
73
+ wrapped_subject = Literal.new(name: :root, children: [subject])
74
+ result = wrapped_subject.match(root)
75
+
76
+ expect(result).to have_matched(root)
77
+ expect(result).to have_substitution(type: root.children)
78
+ end
79
+
80
+ it "shouldn't match nil" do
81
+ expect(subject.match(nil)).not_to have_matched
82
+ end
83
+ end
84
+
85
+ describe "conditional, greedy variable" do
86
+ subject { Variable.new(name: :type, greedy?: true, condition: -> (ls) { ls.size == 2 }) }
87
+
88
+ it "should match when literal matches condition" do
89
+ result = subject.match(second_child)
90
+
91
+ expect(result).to have_matched([second_child, third_child])
92
+ expect(result).to have_substitution(type: [second_child, third_child])
93
+ end
94
+
95
+ it "should not match when literal doesn't match condition" do
96
+ expect(subject.match(third_child)).not_to have_matched
97
+ end
98
+
99
+ it "shouldn't match nil" do
100
+ expect(subject.match(nil)).not_to have_matched
101
+ end
102
+ end
103
+
104
+ describe Literal do
105
+ describe "with no children" do
106
+ let(:literal) { Literal.new(name: :root) }
107
+
108
+ it "should match when names match" do
109
+ matchee = Literal.new(name: :root)
110
+
111
+ expect(literal.match(matchee)).to have_matched(matchee)
112
+ end
113
+
114
+ it "shouldn't match when names don't match" do
115
+ matchee = Literal.new(name: :not_found)
116
+
117
+ expect(literal.match(matchee)).not_to have_matched
118
+ end
119
+
120
+ it "should be able to match nils" do
121
+ literal = Literal.new(name: nil)
122
+ matchee = Literal.new(name: nil)
123
+
124
+ expect(literal.match(matchee)).to have_matched(matchee)
125
+ end
126
+ end
127
+
128
+ describe "with children" do
129
+ let(:root) do
130
+ Literal.new(
131
+ name: :root,
132
+ children: [
133
+ Literal.new(name: :first_child),
134
+ Literal.new(name: :second_child),
135
+ Literal.new(name: :third_child)
136
+ ]
137
+ )
138
+ end
139
+
140
+ let(:first_child) { root.children[0] }
141
+ let(:second_child) { root.children[1] }
142
+ let(:third_child) { root.children[2] }
143
+
144
+ it "should match when children match" do
145
+ matchee = Literal.new(
146
+ name: :root,
147
+ children: [
148
+ Literal.new(name: :first_child),
149
+ Literal.new(name: :second_child),
150
+ Literal.new(name: :third_child)
151
+ ]
152
+ )
153
+
154
+ expect(root.match(matchee)).to have_matched(matchee)
155
+ end
156
+
157
+ it "should not match when children don't match" do
158
+ matchee = Literal.new(
159
+ name: :root,
160
+ children: [
161
+ Literal.new(name: :primer_hijo),
162
+ Literal.new(name: :segundo_hijo),
163
+ Literal.new(name: :tercero_hijo)
164
+ ]
165
+ )
166
+
167
+ expect(root.match(matchee)).not_to have_matched
168
+ end
169
+
170
+ it "should not match when there are too few children" do
171
+ matchee = Literal.new(
172
+ name: :root,
173
+ children: [
174
+ Literal.new(name: :first_child),
175
+ Literal.new(name: :second_child)
176
+ ]
177
+ )
178
+
179
+ expect(root.match(matchee)).not_to have_matched
180
+ end
181
+
182
+ it "should not match when there are too many children" do
183
+ matchee = Literal.new(
184
+ name: :root,
185
+ children: [
186
+ Literal.new(name: :first_child),
187
+ Literal.new(name: :second_child),
188
+ Literal.new(name: :third_child),
189
+ Literal.new(name: :fourth_child)
190
+ ]
191
+ )
192
+
193
+ expect(root.match(matchee)).not_to have_matched
194
+ end
195
+
196
+ it "builds substitution from children" do
197
+ root = Literal.new(
198
+ name: :root,
199
+ children: [
200
+ Variable.new(name: :first),
201
+ Literal.new(name: :second_child),
202
+ Variable.new(name: :last)
203
+ ]
204
+ )
205
+
206
+ matchee = Literal.new(
207
+ name: :root,
208
+ children: [
209
+ Literal.new(name: :first_child),
210
+ Literal.new(name: :second_child),
211
+ Literal.new(name: :third_child)
212
+ ]
213
+ )
214
+
215
+ expect(root.match(matchee)).to have_substitution(first: first_child, last: third_child)
216
+ end
217
+ end
218
+ end
219
+
220
+ describe Derived do
221
+ it "should raise" do
222
+ root = Derived.new
223
+ matchee = Literal.new(name: :root)
224
+
225
+ expect { root.match(matchee) }.to raise_error(Matcher::MatchingError)
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,22 @@
1
+ require "metamorpher"
2
+
3
+ module Metamorpher
4
+ describe Metamorpher do
5
+ it "should provide a Ruby builder by default" do
6
+ expect(subject.builder).to be_kind_of(Builders::Ruby::Builder)
7
+ end
8
+
9
+ describe "configure" do
10
+ it "should be possible to change to an AST builder" do
11
+ subject.configure(builder: :ast)
12
+ expect(subject.builder).to be_kind_of(Builders::AST::Builder)
13
+ end
14
+
15
+ it "should be possible to change back to the Ruby builder" do
16
+ subject.configure(builder: :ast)
17
+ subject.configure(builder: :ruby)
18
+ expect(subject.builder).to be_kind_of(Builders::Ruby::Builder)
19
+ end
20
+ end
21
+ end
22
+ end