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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +1 -3
- data/README.md +29 -13
- data/Rakefile +4 -7
- data/abstract_mapper.gemspec +3 -2
- data/config/metrics/STYLEGUIDE +14 -15
- data/lib/abstract_mapper.rb +4 -1
- data/lib/abstract_mapper/attributes.rb +65 -0
- data/lib/abstract_mapper/branch.rb +17 -7
- data/lib/abstract_mapper/builder.rb +7 -3
- data/lib/abstract_mapper/command.rb +68 -0
- data/lib/abstract_mapper/commands.rb +11 -31
- data/lib/abstract_mapper/errors/unknown_command.rb +1 -1
- data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
- data/lib/abstract_mapper/errors/wrong_rule.rb +2 -2
- data/lib/abstract_mapper/functions.rb +32 -5
- data/lib/abstract_mapper/node.rb +21 -10
- data/lib/abstract_mapper/optimizer.rb +3 -3
- data/lib/abstract_mapper/pair_rule.rb +2 -4
- data/lib/abstract_mapper/rspec.rb +1 -2
- data/lib/abstract_mapper/rule.rb +23 -2
- data/lib/abstract_mapper/rules.rb +3 -10
- data/lib/abstract_mapper/settings.rb +3 -3
- data/lib/abstract_mapper/sole_rule.rb +2 -4
- data/lib/abstract_mapper/version.rb +1 -1
- data/lib/rspec/doubles.rb +16 -0
- data/lib/rspec/nodes.rb +29 -16
- data/lib/rspec/rules.rb +3 -3
- data/spec/integration/faceter.rb +16 -7
- data/spec/integration/mapper_definition_spec.rb +1 -1
- data/spec/integration/rspec_examples_spec.rb +4 -30
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/abstract_mapper/branch_spec.rb +91 -14
- data/spec/unit/abstract_mapper/builder_spec.rb +66 -48
- data/spec/unit/abstract_mapper/command_spec.rb +92 -0
- data/spec/unit/abstract_mapper/commands_spec.rb +38 -79
- data/spec/unit/abstract_mapper/dsl_spec.rb +60 -55
- data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +1 -1
- data/spec/unit/abstract_mapper/functions/compact_spec.rb +3 -3
- data/spec/unit/abstract_mapper/functions/filter_spec.rb +1 -1
- data/spec/unit/abstract_mapper/functions/identity_spec.rb +21 -0
- data/spec/unit/abstract_mapper/functions/restrict_spec.rb +16 -0
- data/spec/unit/abstract_mapper/functions/subclass_spec.rb +1 -1
- data/spec/unit/abstract_mapper/node_spec.rb +119 -48
- data/spec/unit/abstract_mapper/optimizer_spec.rb +38 -32
- data/spec/unit/abstract_mapper/pair_rule_spec.rb +4 -11
- data/spec/unit/abstract_mapper/rule_spec.rb +31 -15
- data/spec/unit/abstract_mapper/rules_spec.rb +2 -2
- data/spec/unit/abstract_mapper/settings_spec.rb +80 -73
- data/spec/unit/abstract_mapper/sole_rule_spec.rb +3 -10
- data/spec/unit/abstract_mapper_spec.rb +13 -5
- metadata +33 -12
- data/lib/rspec/functions.rb +0 -25
- data/lib/rspec/mapper.rb +0 -40
@@ -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
|
data/lib/rspec/nodes.rb
CHANGED
@@ -7,9 +7,9 @@
|
|
7
7
|
shared_context :node do
|
8
8
|
|
9
9
|
def node
|
10
|
-
|
11
|
-
block__
|
12
|
-
described_class.new(
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
data/lib/rspec/rules.rb
CHANGED
@@ -16,7 +16,7 @@ shared_context :rule do
|
|
16
16
|
|
17
17
|
let(:rule) { described_class }
|
18
18
|
|
19
|
-
subject { rule.new(*nodes).call
|
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
|
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
|
47
|
+
is_expected.to eql(optimized), <<-REPORT.gsub(/.+\|/, "")
|
48
48
|
|
|
49
49
|
|#{rule}
|
50
50
|
|
|
data/spec/integration/faceter.rb
CHANGED
@@ -21,14 +21,10 @@ shared_context "Faceter" do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
class Rename < AbstractMapper::Node
|
24
|
-
|
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,
|
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(:
|
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) {
|
16
|
+
let(:attributes) { { keys: {} } }
|
29
17
|
end
|
30
18
|
|
31
19
|
it_behaves_like :mapping_immutable_input do
|
32
|
-
let(:attributes) {
|
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(:
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -2,12 +2,26 @@
|
|
2
2
|
|
3
3
|
describe AbstractMapper::Branch do
|
4
4
|
|
5
|
-
let(:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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(:
|
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(:
|
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 "#
|
82
|
+
describe "#update" do
|
43
83
|
|
44
|
-
subject { branch.
|
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
|
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 #
|
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(:
|
120
|
-
it
|
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
|
-
|
3
|
+
class AbstractMapper
|
4
4
|
|
5
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
15
|
+
describe ".commands=" do
|
22
16
|
|
23
|
-
|
24
|
-
expect { subject }.to change { test.commands }.to commands
|
25
|
-
end
|
17
|
+
subject { test.commands = commands }
|
26
18
|
|
27
|
-
|
19
|
+
it "sets the commands" do
|
20
|
+
expect { subject }.to change { test.commands }.to commands
|
21
|
+
end
|
28
22
|
|
29
|
-
|
23
|
+
end # describe .commands=
|
30
24
|
|
31
|
-
|
25
|
+
describe ".update" do
|
32
26
|
|
33
|
-
|
27
|
+
before { test.commands = commands }
|
34
28
|
|
35
|
-
|
29
|
+
context "by default" do
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
48
|
+
end # context
|
43
49
|
|
44
|
-
|
50
|
+
context "with a block" do
|
45
51
|
|
46
|
-
|
47
|
-
it { is_expected.to eql tree }
|
52
|
+
subject { test.update { bar { foo(foo: :FOO) { fail } } } }
|
48
53
|
|
49
|
-
|
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
|
-
|
59
|
+
end # context
|
52
60
|
|
53
|
-
|
61
|
+
end # describe .update
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
|
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 #
|
73
|
+
end # describe .new
|
61
74
|
|
62
|
-
|
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
|
-
|
83
|
+
it { is_expected.to be_frozen }
|
65
84
|
|
66
|
-
|
67
|
-
it { is_expected.to be_frozen }
|
85
|
+
end # describe #tree
|
68
86
|
|
69
|
-
|
87
|
+
describe "#respond_to?" do
|
70
88
|
|
71
|
-
|
89
|
+
subject { builder.respond_to? :arbitrary_method }
|
90
|
+
it { is_expected.to eql true }
|
72
91
|
|
73
|
-
|
74
|
-
it { is_expected.to eql true }
|
92
|
+
end # describe #respond_to?
|
75
93
|
|
76
|
-
end # describe
|
94
|
+
end # describe AbstractMapper::Builder
|
77
95
|
|
78
|
-
end
|
96
|
+
end
|