abstract_mapper 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +34 -0
  4. data/Gemfile +1 -3
  5. data/README.md +29 -13
  6. data/Rakefile +4 -7
  7. data/abstract_mapper.gemspec +3 -2
  8. data/config/metrics/STYLEGUIDE +14 -15
  9. data/lib/abstract_mapper.rb +4 -1
  10. data/lib/abstract_mapper/attributes.rb +65 -0
  11. data/lib/abstract_mapper/branch.rb +17 -7
  12. data/lib/abstract_mapper/builder.rb +7 -3
  13. data/lib/abstract_mapper/command.rb +68 -0
  14. data/lib/abstract_mapper/commands.rb +11 -31
  15. data/lib/abstract_mapper/errors/unknown_command.rb +1 -1
  16. data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
  17. data/lib/abstract_mapper/errors/wrong_rule.rb +2 -2
  18. data/lib/abstract_mapper/functions.rb +32 -5
  19. data/lib/abstract_mapper/node.rb +21 -10
  20. data/lib/abstract_mapper/optimizer.rb +3 -3
  21. data/lib/abstract_mapper/pair_rule.rb +2 -4
  22. data/lib/abstract_mapper/rspec.rb +1 -2
  23. data/lib/abstract_mapper/rule.rb +23 -2
  24. data/lib/abstract_mapper/rules.rb +3 -10
  25. data/lib/abstract_mapper/settings.rb +3 -3
  26. data/lib/abstract_mapper/sole_rule.rb +2 -4
  27. data/lib/abstract_mapper/version.rb +1 -1
  28. data/lib/rspec/doubles.rb +16 -0
  29. data/lib/rspec/nodes.rb +29 -16
  30. data/lib/rspec/rules.rb +3 -3
  31. data/spec/integration/faceter.rb +16 -7
  32. data/spec/integration/mapper_definition_spec.rb +1 -1
  33. data/spec/integration/rspec_examples_spec.rb +4 -30
  34. data/spec/spec_helper.rb +1 -1
  35. data/spec/unit/abstract_mapper/branch_spec.rb +91 -14
  36. data/spec/unit/abstract_mapper/builder_spec.rb +66 -48
  37. data/spec/unit/abstract_mapper/command_spec.rb +92 -0
  38. data/spec/unit/abstract_mapper/commands_spec.rb +38 -79
  39. data/spec/unit/abstract_mapper/dsl_spec.rb +60 -55
  40. data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +1 -1
  41. data/spec/unit/abstract_mapper/functions/compact_spec.rb +3 -3
  42. data/spec/unit/abstract_mapper/functions/filter_spec.rb +1 -1
  43. data/spec/unit/abstract_mapper/functions/identity_spec.rb +21 -0
  44. data/spec/unit/abstract_mapper/functions/restrict_spec.rb +16 -0
  45. data/spec/unit/abstract_mapper/functions/subclass_spec.rb +1 -1
  46. data/spec/unit/abstract_mapper/node_spec.rb +119 -48
  47. data/spec/unit/abstract_mapper/optimizer_spec.rb +38 -32
  48. data/spec/unit/abstract_mapper/pair_rule_spec.rb +4 -11
  49. data/spec/unit/abstract_mapper/rule_spec.rb +31 -15
  50. data/spec/unit/abstract_mapper/rules_spec.rb +2 -2
  51. data/spec/unit/abstract_mapper/settings_spec.rb +80 -73
  52. data/spec/unit/abstract_mapper/sole_rule_spec.rb +3 -10
  53. data/spec/unit/abstract_mapper_spec.rb +13 -5
  54. metadata +33 -12
  55. data/lib/rspec/functions.rb +0 -25
  56. data/lib/rspec/mapper.rb +0 -40
@@ -4,6 +4,6 @@ class AbstractMapper
4
4
 
5
5
  # The semantic version of the module.
6
6
  # @see http://semver.org/ Semantic versioning 2.0
7
- VERSION = "0.0.1".freeze
7
+ VERSION = "0.0.2".freeze
8
8
 
9
9
  end # class AbstractMapper
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ # ==============================================================================
4
+ # Doubles for values to be frozen
5
+ # ==============================================================================
6
+
7
+ # Returns the double that can be freezed
8
+ #
9
+ # @param [Object, Array<Object>] args
10
+ #
11
+ # @return [Double]
12
+ #
13
+ def ice_double(*args)
14
+ options = args.last.is_a?(Hash) ? args.pop : {}
15
+ double(*args, options.merge(freeze: nil, frozen?: true))
16
+ end
@@ -7,9 +7,9 @@
7
7
  shared_context :node do
8
8
 
9
9
  def node
10
- attrs__ = defined?(attributes) ? attributes : []
11
- block__ = defined?(block) ? block : nil
12
- described_class.new(*attrs__, &block__)
10
+ attributes__ = defined?(attributes) ? attributes : {}
11
+ block__ = defined?(block) ? block : nil
12
+ described_class.new(attributes__, &block__)
13
13
  end
14
14
 
15
15
  end # shared context
@@ -40,19 +40,32 @@ shared_examples :mapping_immutable_input do
40
40
 
41
41
  include_context :node
42
42
 
43
- subject { node.transproc.call(input.dup.freeze) }
44
-
45
- it do
46
- is_expected.to eql(output), <<-REPORT.gsub(/.+\|/, "")
47
- |
48
- |#{node}
49
- |
50
- |Input: #{input}
51
- |
52
- |Output:
53
- | expected: #{output}
54
- | got: #{subject}
55
- REPORT
43
+ let(:immutable_input) do
44
+ begin
45
+ input.dup.freeze
46
+ rescue TypeError # in case input is a singleton that cannot be duplicated
47
+ input
48
+ end
49
+ end
50
+
51
+ subject(:transproc) { node.transproc }
52
+
53
+ it "[creates a composable transproc]" do
54
+ expect(transproc).to be_kind_of Transproc::Function
55
+ end
56
+
57
+ it "[maps the input]" do
58
+ expect(transproc[immutable_input])
59
+ .to eql(output), <<-REPORT.gsub(/.+\|/, "")
60
+ |
61
+ |#{node}
62
+ |
63
+ |Input: #{input}
64
+ |
65
+ |Output:
66
+ | expected: #{output}
67
+ | got: #{transproc[immutable_input]}
68
+ REPORT
56
69
  end
57
70
 
58
71
  end # shared examples
@@ -16,7 +16,7 @@ shared_context :rule do
16
16
 
17
17
  let(:rule) { described_class }
18
18
 
19
- subject { rule.new(*nodes).call.inspect }
19
+ subject { rule.new(*nodes).call }
20
20
 
21
21
  end # shared context
22
22
 
@@ -25,7 +25,7 @@ shared_examples :skipping_nodes do
25
25
  include_context :rule
26
26
 
27
27
  it do
28
- is_expected.to eql(nodes.inspect), <<-REPORT.gsub(/.+\|/, "")
28
+ is_expected.to eql(nodes), <<-REPORT.gsub(/.+\|/, "")
29
29
  |
30
30
  |#{rule}
31
31
  |
@@ -44,7 +44,7 @@ shared_examples :optimizing_nodes do
44
44
  include_context :rule
45
45
 
46
46
  it do
47
- is_expected.to eql(optimized.inspect), <<-REPORT.gsub(/.+\|/, "")
47
+ is_expected.to eql(optimized), <<-REPORT.gsub(/.+\|/, "")
48
48
  |
49
49
  |#{rule}
50
50
  |
@@ -21,14 +21,10 @@ shared_context "Faceter" do
21
21
  end
22
22
 
23
23
  class Rename < AbstractMapper::Node
24
- def initialize(old, options = {})
25
- @old = old
26
- @new = options.fetch(:to)
27
- super
28
- end
24
+ attribute :keys
29
25
 
30
26
  def transproc
31
- Functions[:rename_keys, @old => @new]
27
+ Functions[:rename_keys, keys]
32
28
  end
33
29
  end
34
30
 
@@ -43,13 +39,26 @@ shared_context "Faceter" do
43
39
  end
44
40
  end
45
41
 
42
+ class CompactRenames < AbstractMapper::PairRule
43
+ def optimize?
44
+ left.is_a?(Rename) && right.is_a?(Rename)
45
+ end
46
+
47
+ def optimize
48
+ Rename.new keys: nodes.map(&:keys).reduce(:merge)
49
+ end
50
+ end
51
+
46
52
  # Registry
47
53
  class Mapper < AbstractMapper
48
54
  configure do
49
55
  command :list, Faceter::List
50
- command :rename, Faceter::Rename
56
+ command :rename, Faceter::Rename do |name, options|
57
+ { keys: { name => options.fetch(:to) } }
58
+ end
51
59
 
52
60
  rule Faceter::CompactLists
61
+ rule Faceter::CompactRenames
53
62
  end
54
63
  end
55
64
 
@@ -24,7 +24,7 @@ describe "mapper definition" do
24
24
 
25
25
  it "works" do
26
26
  expect(mapper.tree.inspect)
27
- .to eql "<Root [<List [<Rename(:foo, {:to=>:baz})>, <Rename(:bar, {:to=>:qux})>]>]>"
27
+ .to eql "<Root [<List [<Rename(keys: {:foo=>:baz, :bar=>:qux})>]>]>"
28
28
  expect(mapper.call input).to eql output
29
29
  end
30
30
 
@@ -8,28 +8,17 @@ describe "rspec examples" do
8
8
 
9
9
  include_context "Faceter"
10
10
 
11
- describe "Faceter::Functions", "#rename_keys" do
12
-
13
- let(:described_class) { Faceter::Functions }
14
-
15
- it_behaves_like :transforming_immutable_data do
16
- let(:arguments) { [:rename_keys, foo: :bar] }
17
- let(:input) { { foo: :FOO, baz: :BAZ } }
18
- let(:output) { { bar: :FOO, baz: :BAZ } }
19
- end
20
-
21
- end # describe Faceter::Functions
22
-
23
11
  describe "Faceter::Rename" do
24
12
 
25
13
  let(:described_class) { Faceter::Rename }
26
14
 
27
15
  it_behaves_like :creating_immutable_node do
28
- let(:attributes) { [:foo, to: :bar] }
16
+ let(:attributes) { { keys: {} } }
29
17
  end
30
18
 
31
19
  it_behaves_like :mapping_immutable_input do
32
- let(:attributes) { [:foo, to: :bar] }
20
+ let(:attributes) { { keys: { foo: :bar } } }
21
+
33
22
  let(:input) { { foo: :FOO, baz: :BAZ } }
34
23
  let(:output) { { bar: :FOO, baz: :BAZ } }
35
24
  end
@@ -49,7 +38,7 @@ describe "rspec examples" do
49
38
  let(:described_class) { Faceter::CompactLists }
50
39
 
51
40
  it_behaves_like :skipping_nodes do
52
- let(:input) { [Faceter::Rename.new(:foo, to: :bar), Faceter::List.new] }
41
+ let(:input) { [Faceter::Rename.new(keys: {}), Faceter::List.new] }
53
42
  end
54
43
 
55
44
  it_behaves_like :optimizing_nodes do
@@ -59,19 +48,4 @@ describe "rspec examples" do
59
48
 
60
49
  end # describe Faceter::CompactLists
61
50
 
62
- describe "Faceter::Mapper" do
63
-
64
- let(:described_class) { Faceter::Mapper }
65
-
66
- it_behaves_like :defining_command do
67
- let(:name) { :list }
68
- let(:node) { Faceter::List }
69
- end
70
-
71
- it_behaves_like :defining_rule do
72
- let(:rule) { Faceter::CompactLists }
73
- end
74
-
75
- end # describe Faceter::Mapper
76
-
77
51
  end # describe mapper definition
@@ -13,7 +13,7 @@ require "abstract_mapper"
13
13
 
14
14
  RSpec.configure do |config|
15
15
  config.around do |example|
16
- class AbstractMapper::Test; end
16
+ module AbstractMapper::Test; end
17
17
  example.run
18
18
  AbstractMapper.send :remove_const, :Test
19
19
  end
@@ -2,12 +2,26 @@
2
2
 
3
3
  describe AbstractMapper::Branch do
4
4
 
5
- let(:branch) { test.new(:foo) { [node1, node2] } }
6
- let(:test) { AbstractMapper::Test::Foo = Class.new(described_class) }
7
- let(:tproc) { Transproc::Function.new -> v { "#{v}-1" }, {} }
8
- let(:node1) { instance_double AbstractMapper::Node, transproc: tproc }
9
- let(:node2) { instance_double AbstractMapper::Node, transproc: tproc }
10
- let(:node3) { instance_double AbstractMapper::Node, transproc: tproc }
5
+ let(:foo) do
6
+ class Foo < AbstractMapper::Node
7
+ attribute :foo
8
+
9
+ def transproc
10
+ AbstractMapper::Functions[-> v { "#{v}-1" }]
11
+ end
12
+ end
13
+ Foo
14
+ end
15
+
16
+ let(:node1) { foo.new foo: :FOO }
17
+ let(:node2) { foo.new foo: :BAR }
18
+ let(:node3) { foo.new foo: :BAZ }
19
+ let(:branch) { test.new(foo: :FOO) { [node1, node2] } }
20
+ let(:test) do
21
+ AbstractMapper::Test::Foo = Class.new(described_class) do
22
+ attribute :foo
23
+ end
24
+ end
11
25
 
12
26
  describe ".new" do
13
27
 
@@ -18,20 +32,46 @@ describe AbstractMapper::Branch do
18
32
 
19
33
  context "without a block" do
20
34
 
21
- subject { test.new(:foo).entries }
35
+ subject { test.new(foo: :FOO).entries }
22
36
  it { is_expected.to be_empty }
23
37
 
24
38
  end # context
25
39
 
26
40
  context "with a block" do
27
41
 
28
- subject { test.new(:foo) { [node1, node2] }.entries }
42
+ subject { test.new(foo: :FOO) { [node1, node2] }.entries }
29
43
  it { is_expected.to eql [node1, node2] }
30
44
 
31
45
  end # context
32
46
 
33
47
  end # describe .new
34
48
 
49
+ describe "#attributes" do
50
+
51
+ subject { branch.attributes }
52
+
53
+ context "initialized" do
54
+
55
+ it { is_expected.to eql(foo: :FOO) }
56
+
57
+ end # context
58
+
59
+ context "not initialized" do
60
+
61
+ let(:branch) { test.new }
62
+ it { is_expected.to eql(foo: nil) }
63
+
64
+ end # context
65
+
66
+ context "not defined" do
67
+
68
+ let(:branch) { described_class.new }
69
+ it { is_expected.to eql({}) }
70
+
71
+ end # context
72
+
73
+ end # describe #attributes
74
+
35
75
  describe "#block" do
36
76
 
37
77
  subject { branch.block }
@@ -39,21 +79,21 @@ describe AbstractMapper::Branch do
39
79
 
40
80
  end # describe #block
41
81
 
42
- describe "#rebuild" do
82
+ describe "#update" do
43
83
 
44
- subject { branch.rebuild { [node2, node3] } }
84
+ subject { branch.update { [node2, node3] } }
45
85
 
46
86
  it { is_expected.to be_kind_of test }
47
87
 
48
88
  it "preserves attributes" do
49
- expect(subject.attributes).to eql branch.attributes
89
+ expect(subject.attributes).to eql(foo: :FOO)
50
90
  end
51
91
 
52
92
  it "assings new subnodes from a block" do
53
93
  expect(subject.entries).to eql [node2, node3]
54
94
  end
55
95
 
56
- end # describe #rebuild
96
+ end # describe #update
57
97
 
58
98
  describe "#each" do
59
99
 
@@ -116,11 +156,48 @@ describe AbstractMapper::Branch do
116
156
 
117
157
  context "of the specific node" do
118
158
 
119
- let(:branch) { test.new(:foo) { [node1, node2] } }
120
- it { is_expected.to eql "Foo(:foo) [#{node1.inspect}, #{node2.inspect}]" }
159
+ let(:branch) { test.new(foo: :FOO) { [node1, node2] } }
160
+ it do
161
+ is_expected
162
+ .to eql "Foo(foo: :FOO) [#{node1.inspect}, #{node2.inspect}]"
163
+ end
121
164
 
122
165
  end # context
123
166
 
124
167
  end # describe #to_s
125
168
 
169
+ describe "#eql?" do
170
+
171
+ subject { branch.eql? other }
172
+
173
+ context "with the same type, attributes and subnodes" do
174
+
175
+ let(:other) { test.new(foo: :FOO) { [node1, node2] } }
176
+ it { is_expected.to eql true }
177
+
178
+ end # context
179
+
180
+ context "with another type" do
181
+
182
+ let(:other) { Class.new(test).new(foo: :FOO) { [node1, node2] } }
183
+ it { is_expected.to eql false }
184
+
185
+ end # context
186
+
187
+ context "with other attributes" do
188
+
189
+ let(:other) { test.new(foo: :BAR) { [node1, node2] } }
190
+ it { is_expected.to eql false }
191
+
192
+ end # context
193
+
194
+ context "with other subnodes" do
195
+
196
+ let(:other) { test.new(foo: :FOO) { [node1, node3] } }
197
+ it { is_expected.to eql false }
198
+
199
+ end # context
200
+
201
+ end # describe #eql?
202
+
126
203
  end # describe Branch
@@ -1,78 +1,96 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe AbstractMapper::Builder do
3
+ class AbstractMapper
4
4
 
5
- before do
6
- AbstractMapper::Test::Builder = Class.new(described_class)
7
- AbstractMapper::Test::Foo = Class.new(AbstractMapper::Node)
8
- AbstractMapper::Test::Bar = Class.new(AbstractMapper::Branch)
9
- end
5
+ describe AbstractMapper::Builder do
10
6
 
11
- let(:builder) { test.new tree }
12
- let(:test) { AbstractMapper::Test::Builder }
13
- let(:commands) { AbstractMapper::Commands.new(registry) }
14
- let(:tree) { AbstractMapper::Test::Foo.new }
15
- let(:registry) do
16
- { foo: AbstractMapper::Test::Foo, bar: AbstractMapper::Test::Bar }
17
- end
7
+ let!(:test) { Class.new(described_class) }
8
+ let!(:foo) { Test::Foo = Class.new(Node) { attribute :foo } }
9
+ let!(:bar) { Test::Bar = Class.new(Branch) }
18
10
 
19
- describe ".commands=" do
11
+ let(:builder) { test.new tree }
12
+ let(:tree) { Test::Foo.new }
13
+ let(:commands) { Commands.new << [:foo, Test::Foo] << [:bar, Test::Bar] }
20
14
 
21
- subject { test.commands = commands }
15
+ describe ".commands=" do
22
16
 
23
- it "sets the commands" do
24
- expect { subject }.to change { test.commands }.to commands
25
- end
17
+ subject { test.commands = commands }
26
18
 
27
- end # describe .commands=
19
+ it "sets the commands" do
20
+ expect { subject }.to change { test.commands }.to commands
21
+ end
28
22
 
29
- describe ".update" do
23
+ end # describe .commands=
30
24
 
31
- before { test.commands = commands }
25
+ describe ".update" do
32
26
 
33
- context "by default" do
27
+ before { test.commands = commands }
34
28
 
35
- subject { test.update }
29
+ context "by default" do
36
30
 
37
- it "is an empty branch" do
38
- expect(subject).to be_instance_of AbstractMapper::Branch
39
- expect(subject.entries).to be_empty
40
- end
31
+ subject { test.update }
32
+
33
+ it "is an empty branch" do
34
+ expect(subject).to be_instance_of AbstractMapper::Branch
35
+ expect(subject.entries).to be_empty
36
+ end
37
+
38
+ end # context
39
+
40
+ context "initialized" do
41
+
42
+ subject { test.update(tree) }
43
+
44
+ it "returns exactly the same tree" do
45
+ expect(subject.inspect).to eql(tree.inspect)
46
+ end
41
47
 
42
- end # context
48
+ end # context
43
49
 
44
- context "initialized" do
50
+ context "with a block" do
45
51
 
46
- subject { test.update(tree) }
47
- it { is_expected.to eql tree }
52
+ subject { test.update { bar { foo(foo: :FOO) { fail } } } }
48
53
 
49
- end # context
54
+ it "is built" do
55
+ expect(subject.inspect).to eql "<Root [<Bar [<Foo(foo: :FOO)>]>]>"
56
+ expect(subject.first.first.block).not_to be_nil
57
+ end
50
58
 
51
- context "with a block" do
59
+ end # context
52
60
 
53
- subject { test.update { bar { foo(:foo) { fail } } } }
61
+ end # describe .update
54
62
 
55
- it "is built" do
56
- expect(subject.inspect).to eql "<Root [<Bar [<Foo(:foo)>]>]>"
57
- expect(subject.first.first.block).not_to be_nil
63
+ describe ".new" do
64
+
65
+ subject { builder }
66
+
67
+ it { is_expected.to be_frozen }
68
+
69
+ it "doesn't freezes the tree" do
70
+ expect { subject }.not_to change { tree.frozen? }
58
71
  end
59
72
 
60
- end # context
73
+ end # describe .new
61
74
 
62
- end # describe .update
75
+ describe "#tree" do
76
+
77
+ subject { builder.tree }
78
+
79
+ it "returns exactly the same tree as initialized" do
80
+ expect(subject.inspect).to eql tree.inspect
81
+ end
63
82
 
64
- describe ".new" do
83
+ it { is_expected.to be_frozen }
65
84
 
66
- subject { builder }
67
- it { is_expected.to be_frozen }
85
+ end # describe #tree
68
86
 
69
- end # describe .new
87
+ describe "#respond_to?" do
70
88
 
71
- describe "#respond_to?" do
89
+ subject { builder.respond_to? :arbitrary_method }
90
+ it { is_expected.to eql true }
72
91
 
73
- subject { builder.respond_to? :arbitrary_method }
74
- it { is_expected.to eql true }
92
+ end # describe #respond_to?
75
93
 
76
- end # describe #respond_to?
94
+ end # describe AbstractMapper::Builder
77
95
 
78
- end # describe AbstractMapper::Builder
96
+ end