abstract_mapper 0.0.1 → 0.0.2

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 (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