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