metamorpher 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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