transmogrifier 0.0.1

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.
@@ -0,0 +1,46 @@
1
+ require "transmogrifier"
2
+
3
+ describe Transmogrifier::Engine do
4
+ subject(:engine) { described_class.from_rules_array(rules) }
5
+
6
+ describe "#run" do
7
+ let(:rules) do
8
+ [
9
+ {
10
+ "type" => "append",
11
+ "selector" => "top",
12
+ "object" => {"some" => "attributes"}
13
+ },
14
+
15
+ {
16
+ "type" => "move",
17
+ "selector" => "top",
18
+ "from" => "key1",
19
+ "to" => "key2",
20
+ },
21
+
22
+ {
23
+ "type" => "delete",
24
+ "selector" => "top",
25
+ "name" => "key3"
26
+ }
27
+ ]
28
+ end
29
+
30
+ it "runs all the rules" do
31
+ input_hash = {
32
+ "top" => {
33
+ "key1" => "value1",
34
+ "key3" => "value2"
35
+ }
36
+ }
37
+
38
+ expect(engine.run(input_hash)).to eq({
39
+ "top" => {
40
+ "some" => "attributes",
41
+ "key2" => "value1",
42
+ }
43
+ })
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,84 @@
1
+ require "transmogrifier"
2
+
3
+ module Transmogrifier
4
+ describe ArrayNode do
5
+ describe "#raw" do
6
+ it "returns the underlying array" do
7
+ array = [{"name" => "object1"}, {"name" => "object2"}]
8
+ node = ArrayNode.new(array)
9
+ expect(node.raw).to eq(array)
10
+ end
11
+ end
12
+
13
+ describe "#find_all" do
14
+ it "returns wildcard matches" do
15
+ array = [
16
+ {"name" => "object1", "nested" => {"key1" => "value1"}},
17
+ {"name" => "object2", "nested" => {"key2" => "value2"}}
18
+ ]
19
+ node = ArrayNode.new(array)
20
+
21
+ expect(node.find_all([[], "nested"]).map(&:raw)).to eq([
22
+ {"key1" => "value1"},
23
+ {"key2" => "value2"},
24
+ ])
25
+ end
26
+
27
+ it "filters by attributes" do
28
+ array = [
29
+ {"type" => "object", "key1" => "value1"},
30
+ {"type" => "object", "key2" => "value2"},
31
+ ]
32
+ node = ArrayNode.new(array)
33
+
34
+ expect(node.find_all([["type", "object"]]).map(&:raw)).to eq(array)
35
+ end
36
+ end
37
+
38
+ describe "#delete" do
39
+ context "when one node matches the criteria" do
40
+ it "deletes the node from the array" do
41
+ array = [
42
+ {"name" => "object1"},
43
+ {"name" => "object2"},
44
+ ]
45
+ node = ArrayNode.new(array)
46
+ expect(node.delete([["name", "object1"]])).to eq({"name" => "object1"})
47
+ end
48
+ end
49
+
50
+ context "when more than one node matches the criteria" do
51
+ it "raises an error" do
52
+ array = [
53
+ {"name" => "object1", "common_key" => "common_value"},
54
+ {"name" => "object2", "common_key" => "common_value"},
55
+ ]
56
+ node = ArrayNode.new(array)
57
+ expect {
58
+ node.delete([["common_key", "common_value"]])
59
+ }.to raise_error
60
+ end
61
+ end
62
+
63
+ context "when no nodes match the criteria" do
64
+ it "returns nil" do
65
+ array = [
66
+ {"name" => "object1"},
67
+ {"name" => "object2"},
68
+ ]
69
+ node = ArrayNode.new(array)
70
+ expect(node.delete([["name", "not_present"]])).to be_nil
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "#append" do
76
+ it "appends the node to the array" do
77
+ array = [{"name" => "object1"}]
78
+ node = ArrayNode.new(array)
79
+ node.append("name" => "object2")
80
+ expect(node.raw).to eq([{"name" => "object1"}, {"name" => "object2"}])
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,94 @@
1
+ require "transmogrifier"
2
+
3
+ module Transmogrifier
4
+ describe HashNode do
5
+ describe "#raw" do
6
+ it "returns the passed in hash" do
7
+ node = HashNode.new({"key" => "value"})
8
+ expect(node.raw).to eq({"key" => "value"})
9
+ end
10
+ end
11
+
12
+ describe "#find_all" do
13
+ context "given an empty set of selector keys" do
14
+ it "returns itself in an array" do
15
+ hash = {
16
+ "key" => "value",
17
+ "extra_key" => "other_value",
18
+ }
19
+ node = HashNode.new(hash)
20
+
21
+ expect(node.find_all([]).map(&:raw)).to eq([{
22
+ "key" => "value",
23
+ "extra_key" => "other_value",
24
+ }])
25
+ end
26
+ end
27
+
28
+ context "given multiple selector keys" do
29
+ let(:keys) { ["key", [["k", "v"]]] }
30
+
31
+ context "when the first key is a key in the hash" do
32
+ let(:hash) {{
33
+ "key" => [
34
+ {"k" => "v", "a" => "b"},
35
+ {"k" => "not_v", "c" => "d"},
36
+ {"k" => "v", "e" => "f"},
37
+ ]
38
+ }}
39
+
40
+ it "finds all children of the first key's value satisfying the remaining selector keys" do
41
+ node = HashNode.new(hash)
42
+
43
+ expect(node.find_all(keys).map(&:raw)).to eq([
44
+ {"k" => "v", "a" => "b"},
45
+ {"k" => "v", "e" => "f"}
46
+ ])
47
+ end
48
+ end
49
+
50
+ context "when the first key is not a key in the hash" do
51
+ let(:hash) {{
52
+ "not_key" => [
53
+ {"k" => "v", "a" => "b"},
54
+ {"k" => "not_v", "c" => "d"},
55
+ {"k" => "v", "e" => "f"}
56
+ ]
57
+ }}
58
+
59
+ it "returns an empty array" do
60
+ node = HashNode.new(hash)
61
+
62
+ expect(node.find_all(keys).map(&:raw)).to eq([])
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "#delete" do
69
+ it "deletes the given key" do
70
+ hash = {"key" => "value", "extra_key" => "other_value"}
71
+ node = HashNode.new(hash)
72
+ node.delete("extra_key")
73
+
74
+ expect(node.raw).to eq({"key" => "value"})
75
+ end
76
+
77
+ it "returns the node that was deleted" do
78
+ hash = {"key" => "value", "extra_key" => "other_value"}
79
+ node = HashNode.new(hash)
80
+
81
+ expect(node.delete("extra_key")).to eq("other_value")
82
+ end
83
+ end
84
+
85
+ describe "#append" do
86
+ it "appends the given node at the key" do
87
+ hash = {"key" => "value"}
88
+ node = HashNode.new(hash)
89
+ node.append({ "extra_key" => "extra_value"})
90
+ expect(node.raw).to eq({"key" => "value", "extra_key" => "extra_value"})
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,19 @@
1
+ require "transmogrifier"
2
+
3
+ module Transmogrifier
4
+ describe Node do
5
+ describe ".for" do
6
+ it "returns a node value" do
7
+ Node.for("value").should be_a(ValueNode)
8
+ end
9
+
10
+ it "returns a hash value" do
11
+ Node.for("key" => "value").should be_a(HashNode)
12
+ end
13
+
14
+ it "returns an array value" do
15
+ Node.for(["value"]).should be_a(ArrayNode)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ require "transmogrifier"
2
+
3
+ module Transmogrifier
4
+ describe ValueNode do
5
+ describe "#raw" do
6
+ it "returns the passed in value" do
7
+ node = ValueNode.new("hello")
8
+ expect(node.raw).to eq("hello")
9
+ end
10
+ end
11
+
12
+ describe "#find_all" do
13
+ context "when given empty keys" do
14
+ it "returns an array of itself" do
15
+ node = ValueNode.new("hello")
16
+ expect(node.find_all([])).to eq([node])
17
+ end
18
+ end
19
+
20
+ context "when given non-empty keys" do
21
+ it "raises an error" do
22
+ node = ValueNode.new("hello")
23
+ expect {
24
+ node.find_all(["foo.bar"])
25
+ }.to raise_error
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#delete" do
31
+ it "raises a NotImplementedError" do
32
+ expect {
33
+ ValueNode.new("hello").delete("key")
34
+ }.to raise_error(NotImplementedError)
35
+ end
36
+ end
37
+
38
+ describe "#append" do
39
+ it "raises a NotImplementedError" do
40
+ expect {
41
+ ValueNode.new("hello").append("value")
42
+ }.to raise_error(NotImplementedError)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ require "transmogrifier"
2
+
3
+ describe Transmogrifier::Rules::Append do
4
+ let(:input_hash) do
5
+ { "key" => "value", "array" => [] }
6
+ end
7
+
8
+ context "when the selector finds a HashNode" do
9
+ subject(:append) { described_class.new("", {"new_key" => "new_value"}) }
10
+ it "appends to the hash" do
11
+ expect(append.apply!(input_hash)).to eq({
12
+ "key" => "value",
13
+ "array" => [],
14
+ "new_key" => "new_value",
15
+ })
16
+ end
17
+ end
18
+
19
+ context "when the selector finds an ArrayNode" do
20
+ subject(:append) { described_class.new("array", {"new_key" => "new_value"}) }
21
+
22
+ it "appends to the array" do
23
+ expect(append.apply!(input_hash)).to eq({
24
+ "key" => "value",
25
+ "array" => [{"new_key" => "new_value"}],
26
+ })
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ require "transmogrifier"
2
+
3
+ describe Transmogrifier::Rules::Delete do
4
+ let(:input_hash) do
5
+ {
6
+ "key" => "value",
7
+ "array" => [{"inside" => "value"}],
8
+ "nested" => {
9
+ "key" => "value"
10
+ },
11
+ }
12
+ end
13
+
14
+ context "when the selector finds a HashNode" do
15
+ subject(:delete) { described_class.new("", "nested") }
16
+
17
+ it "deletes the hash from the parent" do
18
+ expect(delete.apply!(input_hash)).to eq({
19
+ "key" => "value",
20
+ "array" => [{"inside" => "value"}],
21
+ })
22
+ end
23
+ end
24
+
25
+ context "when the selector finds an ArrayNode" do
26
+ subject(:delete) { described_class.new("array", "[inside=value]") }
27
+
28
+ it "deletes the array from the parent" do
29
+ expect(delete.apply!(input_hash)).to eq({
30
+ "key" => "value",
31
+ "array" => [],
32
+ "nested" => {
33
+ "key" => "value"
34
+ },
35
+ })
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,110 @@
1
+ require "transmogrifier"
2
+
3
+ describe Transmogrifier::Rules::Move do
4
+ let(:input_hash) do
5
+ {
6
+ "key" => "value",
7
+ "array" => [{"inside" => "value"}],
8
+ "nested" => {
9
+ "key" => "value",
10
+ },
11
+ }
12
+ end
13
+
14
+ context "when the selector finds a HashNode" do
15
+ context "when the target key exists" do
16
+ subject(:move) { described_class.new("", "array.[inside=value]", "nested") }
17
+
18
+ it "moves the hash to the to selector" do
19
+ expect(move.apply!(input_hash)).to eq({
20
+ "key" => "value",
21
+ "array" => [],
22
+ "nested" => {
23
+ "key" => "value",
24
+ "inside" => "value",
25
+ },
26
+ })
27
+ end
28
+ end
29
+
30
+ context "when the target key doesn't exist" do
31
+ subject(:move) { described_class.new("", "array.[inside=value]", "nested.nested_again") }
32
+
33
+ it "moves the hash to a new child" do
34
+ expect(move.apply!(input_hash)).to eq({
35
+ "key" => "value",
36
+ "array" => [],
37
+ "nested" => {
38
+ "key" => "value",
39
+ "nested_again" => {
40
+ "inside" => "value",
41
+ }
42
+ },
43
+ })
44
+ end
45
+ end
46
+
47
+ context "when the from selector has a wildcard" do
48
+ let(:input_hash) do
49
+ {
50
+ "list_of_things" => [
51
+ {
52
+ "name" => "object1",
53
+ "property" => "property1",
54
+ "nested" => {}
55
+ },
56
+ {
57
+ "name" => "object2",
58
+ "property" => "property2",
59
+ "nested" => {}
60
+ },
61
+ ]
62
+ }
63
+ end
64
+ subject(:move) { described_class.new("list_of_things.[]", "property", "nested.property") }
65
+
66
+ it "moves the matched hash to the correct place" do
67
+ expect(move.apply!(input_hash)).to eq({
68
+ "list_of_things" => [
69
+ {
70
+ "name" => "object1",
71
+ "nested" => { "property" => "property1" }
72
+ },
73
+ {
74
+ "name" => "object2",
75
+ "nested" => { "property" => "property2" }
76
+ },
77
+ ]
78
+ })
79
+ end
80
+ end
81
+ end
82
+
83
+ context "when the selector finds an ArrayNode" do
84
+ subject(:move) { described_class.new("", "array", "nested.array") }
85
+
86
+ it "moves the array to the to selector" do
87
+ expect(move.apply!(input_hash)).to eq({
88
+ "key" => "value",
89
+ "nested" => {
90
+ "key" => "value",
91
+ "array" => [{"inside" => "value"}],
92
+ },
93
+ })
94
+ end
95
+ end
96
+
97
+ context "using move as a rename" do
98
+ subject(:rename) { described_class.new("", "array", "new_array") }
99
+
100
+ it "renames the node" do
101
+ expect(rename.apply!(input_hash)).to eq({
102
+ "key" => "value",
103
+ "new_array" => [{"inside" => "value"}],
104
+ "nested" => {
105
+ "key" => "value",
106
+ },
107
+ })
108
+ end
109
+ end
110
+ end