transmogrifier 0.0.3 → 0.0.4
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 +8 -8
- data/README.md +33 -17
- data/lib/transmogrifier/nodes/hash_node.rb +0 -2
- data/lib/transmogrifier/rules/append.rb +2 -2
- data/lib/transmogrifier/rules/copy.rb +1 -0
- data/lib/transmogrifier/rules/update.rb +30 -0
- data/lib/transmogrifier/selector.rb +2 -0
- data/lib/transmogrifier/version.rb +1 -1
- data/spec/nodes/hash_node_spec.rb +18 -7
- data/spec/rules/copy_spec.rb +18 -0
- data/spec/rules/modify_spec.rb +2 -2
- data/spec/rules/update_spec.rb +101 -0
- data/spec/transmogrifier_spec.rb +18 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmRlZjJhNjI1YTc0NWUyMGU0ZDM1NGYwNmRlNGJkYjg1MTI5ZDdhMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NjQxOGFhOGYwNzhhNGVjNTJkNTllNDYxZjc2ZTkyNDZiZWE0MTFhMg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YmQ3YmI3MTFjOGM1Y2I3ZGQ0NTc1NGE4MmEzNTgzMmUyMzllZWE1NGUyZmQ0
|
10
|
+
NzYzYWI0NGUwOTU4Y2Y3MjBkNDYzZTZjNDU5Njg2Mzk3M2RkNzRhNzRmMzEy
|
11
|
+
MDU5ODk1MWJkZmNkYTAyNjIxNmNmOWYzMzBiOGJhZmU4NzVjYjA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YzI4MzZlODAyNDZjNTA4MDEyNGVkNjFlYWI5NWJlNzk2YWQ3MjU1YTdhMWVj
|
14
|
+
ZTc2ZWFkY2JjNDFlYTcwYmVhYWM4NTVmYTUyZDc1MjU1OTk4YTg1ZTQ5ZmYz
|
15
|
+
ZTEzNGZiZjE1OGU5OGNlNGJhY2MwNTNkMDliYjdlZGE4MDczODk=
|
data/README.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# Transmogrifier
|
2
2
|
|
3
|
+
[](https://travis-ci.org/jfoley/transmogrifier)
|
4
|
+
|
3
5
|
Transmogrifier is a tool that allows you to decalaritively migrate a hash from one schema to another. It works by specifying a set of rules to apply to the hash and then running them in order.
|
4
6
|
|
5
7
|
## Usage
|
6
8
|
### Available Rules
|
7
|
-
#### Appending a
|
9
|
+
#### Appending a node
|
8
10
|
```ruby
|
9
11
|
engine = Transmogrifier::Engine.new
|
10
|
-
append = Transmogrifier::Rules::Append.new("", "new_key"
|
12
|
+
append = Transmogrifier::Rules::Append.new("", { "new_key" => "new_value" })
|
11
13
|
|
12
14
|
engine.add_rule(append)
|
13
15
|
|
@@ -17,23 +19,37 @@ output_hash = engine.run(input_hash)
|
|
17
19
|
# output_hash => {"key" => "value", "new_key" => "new_value"}
|
18
20
|
```
|
19
21
|
|
20
|
-
|
22
|
+
### Updating a node
|
21
23
|
```ruby
|
22
24
|
engine = Transmogrifier::Engine.new
|
23
|
-
|
25
|
+
update = Transmogrifier::Rules::Update.new("nested.nested_key", "updated-value")
|
24
26
|
|
25
|
-
engine.add_rule(
|
27
|
+
engine.add_rule(update)
|
26
28
|
|
27
29
|
input_hash = {"key" => "value", "nested" => {"nested_key" => "nested_value"}}
|
28
|
-
output_hash =
|
30
|
+
output_hash = engine.run(input_hash)
|
29
31
|
|
30
|
-
# output_hash => {"key" => "value", "nested" => {"nested_key" => "
|
32
|
+
# output_hash => {"key" => "value", "nested" => {"nested_key" => "updated-value"}}
|
31
33
|
```
|
32
34
|
|
33
|
-
####
|
35
|
+
#### Copying a node
|
34
36
|
```ruby
|
35
37
|
engine = Transmogrifier::Engine.new
|
36
|
-
|
38
|
+
copy = Transmogrifier::Rules::Copy.new("", "key", "nested.nested_key2")
|
39
|
+
|
40
|
+
engine.add_rule(copy)
|
41
|
+
|
42
|
+
input_hash = {"key" => "value", "nested" => {"nested_key" => "nested_value"}}
|
43
|
+
output_hash = engine.run(input_hash)
|
44
|
+
|
45
|
+
# output_hash => {"key" => "value", "nested" => {"nested_key" => "nested_value", "nested_key2" => "value"}}
|
46
|
+
```
|
47
|
+
If the node to be copied does not exist, then this operation will no-op leaving the hash unchanged.
|
48
|
+
|
49
|
+
#### Deleting a node
|
50
|
+
```ruby
|
51
|
+
engine = Transmogrifier::Engine.new
|
52
|
+
delete = Transmogrifier::Rules::Delete.new("", "extra_key")
|
37
53
|
|
38
54
|
engine.add_rule(delete)
|
39
55
|
|
@@ -43,20 +59,20 @@ output_hash = engine.run(input_hash)
|
|
43
59
|
# output_hash => {"key" => "value"}
|
44
60
|
```
|
45
61
|
|
46
|
-
#### Modifying a value
|
62
|
+
#### Modifying a node's value
|
47
63
|
```ruby
|
48
64
|
engine = Transmogrifier::Engine.new
|
49
|
-
move = Transmogrifier::Rules::Modify.new("", "
|
65
|
+
move = Transmogrifier::Rules::Modify.new("key", "al", "og")
|
50
66
|
|
51
|
-
engine.add_rule(
|
67
|
+
engine.add_rule(move)
|
52
68
|
|
53
|
-
input_hash = {"key" => "value"
|
54
|
-
output_hash =
|
69
|
+
input_hash = {"key" => "value"}
|
70
|
+
output_hash = engine.run(input_hash)
|
55
71
|
|
56
|
-
# output_hash => {"key" => "vogue"
|
72
|
+
# output_hash => {"key" => "vogue"}
|
57
73
|
```
|
58
74
|
|
59
|
-
#### Moving a
|
75
|
+
#### Moving a node
|
60
76
|
```ruby
|
61
77
|
engine = Transmogrifier::Engine.new
|
62
78
|
move = Transmogrifier::Rules::Move.new("", "key", "nested")
|
@@ -64,7 +80,7 @@ move = Transmogrifier::Rules::Move.new("", "key", "nested")
|
|
64
80
|
engine.add_rule(:move, "", "key", "nested")
|
65
81
|
|
66
82
|
input_hash = {"key" => "value", "nested" => {"nested_key" => "nested_value"}}
|
67
|
-
output_hash =
|
83
|
+
output_hash = engine.run(input_hash)
|
68
84
|
|
69
85
|
# output_hash => {"nested" => {"nested_key" => "nested_value", "key" => "value"}}
|
70
86
|
```
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Transmogrifier
|
2
2
|
module Rules
|
3
3
|
class Append
|
4
|
-
def initialize(parent_selector,
|
5
|
-
@parent_selector, @hash = parent_selector,
|
4
|
+
def initialize(parent_selector, new_hash)
|
5
|
+
@parent_selector, @hash = parent_selector, new_hash
|
6
6
|
end
|
7
7
|
|
8
8
|
def apply!(input_hash)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Transmogrifier
|
2
|
+
module Rules
|
3
|
+
class Update
|
4
|
+
def initialize(selector, new_value)
|
5
|
+
@selector, @new_value = selector, new_value
|
6
|
+
end
|
7
|
+
|
8
|
+
def apply!(input_hash)
|
9
|
+
top = Node.for(input_hash)
|
10
|
+
*parent_keys, child_key = Selector.from_string(@selector).keys
|
11
|
+
|
12
|
+
parents = top.find_all(parent_keys)
|
13
|
+
raise SelectorNotFoundError, "#{parent_keys} does not select a node" unless parents.length > 0
|
14
|
+
|
15
|
+
parents.each do |parent|
|
16
|
+
if parent.class == HashNode
|
17
|
+
raise SelectorNotFoundError, "no mapping for #{child_key} in parent node" unless parent.raw.has_key?(child_key)
|
18
|
+
parent.append({child_key => @new_value})
|
19
|
+
elsif parent.class == ArrayNode
|
20
|
+
deleted_node = parent.delete(child_key)
|
21
|
+
raise SelectorNotFoundError, "no node found with specified attributes" if deleted_node.nil?
|
22
|
+
parent.append(@new_value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
top.raw
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -66,14 +66,25 @@ module Transmogrifier
|
|
66
66
|
end
|
67
67
|
|
68
68
|
describe "#clone" do
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
context "when key exists" do
|
70
|
+
it "returns a copy of value" do
|
71
|
+
value = "other_value"
|
72
|
+
hash = {"key" => "value", "extra_key" => value}
|
73
|
+
node = HashNode.new(hash)
|
74
|
+
|
75
|
+
expect(node.clone("extra_key")).to eql(value)
|
76
|
+
expect(node.clone("extra_key")).to_not be(value)
|
77
|
+
expect(node.raw).to eq("key" => "value", "extra_key" => "other_value")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when key does not exist" do
|
82
|
+
it "returns nil" do
|
83
|
+
hash = {"key" => "value"}
|
84
|
+
node = HashNode.new(hash)
|
73
85
|
|
74
|
-
|
75
|
-
|
76
|
-
expect(node.raw).to eq("key" => "value", "extra_key" => "other_value")
|
86
|
+
expect(node.clone("extra_key")).to be_nil
|
87
|
+
end
|
77
88
|
end
|
78
89
|
end
|
79
90
|
|
data/spec/rules/copy_spec.rb
CHANGED
@@ -96,4 +96,22 @@ describe Transmogrifier::Rules::Copy do
|
|
96
96
|
})
|
97
97
|
end
|
98
98
|
end
|
99
|
+
|
100
|
+
context "when the selector does not find a node" do
|
101
|
+
context "when the parent node is a HashNode" do
|
102
|
+
subject(:copy) { described_class.new("", "missing_key", "nested") }
|
103
|
+
|
104
|
+
it "does not modify the input hash" do
|
105
|
+
expect(copy.apply!(input_hash)).to eq(input_hash)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when the parent node is an ArrayNode" do
|
110
|
+
subject(:copy) { described_class.new("", "array.[inside=missing_value]", "nested") }
|
111
|
+
|
112
|
+
it "does not modify the input hash" do
|
113
|
+
expect(copy.apply!(input_hash)).to eq(input_hash)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
99
117
|
end
|
data/spec/rules/modify_spec.rb
CHANGED
@@ -19,7 +19,7 @@ describe Transmogrifier::Rules::Modify do
|
|
19
19
|
context "when the selector finds a HashNode" do
|
20
20
|
subject(:modify) { described_class.new("", "al", "og") }
|
21
21
|
|
22
|
-
it "
|
22
|
+
it "raises an error" do
|
23
23
|
expect{modify.apply!(input_hash)}.to raise_error(NotImplementedError)
|
24
24
|
end
|
25
25
|
end
|
@@ -27,7 +27,7 @@ describe Transmogrifier::Rules::Modify do
|
|
27
27
|
context "when the selector finds an ArrayNode" do
|
28
28
|
subject(:modify) { described_class.new("array", "al", "og") }
|
29
29
|
|
30
|
-
it "
|
30
|
+
it "raises an error" do
|
31
31
|
expect{modify.apply!(input_hash)}.to raise_error(NotImplementedError)
|
32
32
|
end
|
33
33
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "transmogrifier"
|
2
|
+
|
3
|
+
describe Transmogrifier::Rules::Update do
|
4
|
+
let(:input_hash) do
|
5
|
+
{
|
6
|
+
"key" => "value",
|
7
|
+
"hash" => { "nested" => "hash-value" },
|
8
|
+
"array" => [{"name" => "array-value"}],
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when the key specifies a ValueNode" do
|
13
|
+
subject(:update) { described_class.new("key", "new-value") }
|
14
|
+
|
15
|
+
it "replaces the value with the new value" do
|
16
|
+
expect(update.apply!(input_hash)).to eq({
|
17
|
+
"key" => "new-value",
|
18
|
+
"hash" => { "nested" => "hash-value" },
|
19
|
+
"array" => [{"name" => "array-value"}],
|
20
|
+
})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when the key specifies a HashNode" do
|
25
|
+
subject(:update) { described_class.new("hash", "new-value") }
|
26
|
+
|
27
|
+
it "replaces the value of the hash with the new value" do
|
28
|
+
expect(update.apply!(input_hash)).to eq({
|
29
|
+
"key" => "value",
|
30
|
+
"hash" => "new-value",
|
31
|
+
"array" => [{"name" => "array-value"}],
|
32
|
+
})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when the key specifies an ArrayNode" do
|
37
|
+
subject(:update) { described_class.new("array", "new-value") }
|
38
|
+
|
39
|
+
it "replaces the value of the array with the new value" do
|
40
|
+
expect(update.apply!(input_hash)).to eq({
|
41
|
+
"key" => "value",
|
42
|
+
"hash" => { "nested" => "hash-value" },
|
43
|
+
"array" => "new-value",
|
44
|
+
})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the selector specifies a node inside of an array" do
|
49
|
+
let(:input_hash) do
|
50
|
+
{
|
51
|
+
"array" => [
|
52
|
+
{ "name" => "value-to-modify", "other" => "properties", "get" => "overwritten" },
|
53
|
+
{ "name" => "leave-me-alone" },
|
54
|
+
],
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
subject(:update) { described_class.new("array.[name=value-to-modify]", { "name" => "new-value"}) }
|
59
|
+
|
60
|
+
it "replaces the value of the array's node with the new value" do
|
61
|
+
expect(update.apply!(input_hash)).to eq({
|
62
|
+
"array" => [
|
63
|
+
{ "name" => "leave-me-alone" },
|
64
|
+
{ "name" => "new-value" },
|
65
|
+
],
|
66
|
+
})
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when the selector does not find a node" do
|
71
|
+
context "when the selector finds a hash as the parent" do
|
72
|
+
subject(:update) { described_class.new("non-existant-key", "new-value") }
|
73
|
+
|
74
|
+
it "raises a SelectorNotFoundError" do
|
75
|
+
expect {
|
76
|
+
update.apply!(input_hash)
|
77
|
+
}.to raise_error(Transmogrifier::SelectorNotFoundError)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when the selector finds an array as the parent" do
|
82
|
+
subject(:update) { described_class.new("array.[name=non-existant-key]", "new-value") }
|
83
|
+
|
84
|
+
it "raises a SelectorNotFoundError" do
|
85
|
+
expect {
|
86
|
+
update.apply!(input_hash)
|
87
|
+
}.to raise_error(Transmogrifier::SelectorNotFoundError)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when the selector does not find a parent" do
|
92
|
+
subject(:update) { described_class.new("wrong.key", "new-value") }
|
93
|
+
|
94
|
+
it "raises a SelectorNotFoundError" do
|
95
|
+
expect {
|
96
|
+
update.apply!(input_hash)
|
97
|
+
}.to raise_error(Transmogrifier::SelectorNotFoundError)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/spec/transmogrifier_spec.rb
CHANGED
@@ -180,6 +180,24 @@ describe Transmogrifier::Engine do
|
|
180
180
|
})
|
181
181
|
end
|
182
182
|
end
|
183
|
+
|
184
|
+
context "when the selector does not find a node" do
|
185
|
+
context "when the parent node is a HashNode" do
|
186
|
+
subject(:rules) { [ {"type" => "copy", "selector" => "", "from" => "missing_key", "to" => "nested"} ] }
|
187
|
+
|
188
|
+
it "does not modify the input hash" do
|
189
|
+
expect(engine.run(input_hash)).to eq(input_hash)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "when the parent node is an ArrayNode" do
|
194
|
+
subject(:rules) { [ {"type" => "copy", "selector" => "", "from" => "array.[inside=missing_value]", "to" => "nested"} ] }
|
195
|
+
|
196
|
+
it "does not modify the input hash" do
|
197
|
+
expect(engine.run(input_hash)).to eq(input_hash)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
183
201
|
end
|
184
202
|
|
185
203
|
describe "deleting keys" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transmogrifier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Foley
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/transmogrifier/rules/delete.rb
|
73
73
|
- lib/transmogrifier/rules/modify.rb
|
74
74
|
- lib/transmogrifier/rules/move.rb
|
75
|
+
- lib/transmogrifier/rules/update.rb
|
75
76
|
- lib/transmogrifier/selector.rb
|
76
77
|
- lib/transmogrifier/version.rb
|
77
78
|
- README.md
|
@@ -87,6 +88,7 @@ files:
|
|
87
88
|
- spec/rules/delete_spec.rb
|
88
89
|
- spec/rules/modify_spec.rb
|
89
90
|
- spec/rules/move_spec.rb
|
91
|
+
- spec/rules/update_spec.rb
|
90
92
|
- spec/selector_spec.rb
|
91
93
|
- spec/transmogrifier_spec.rb
|
92
94
|
homepage: http://github.com/jfoley/transmogrifier
|
@@ -124,6 +126,7 @@ test_files:
|
|
124
126
|
- spec/rules/delete_spec.rb
|
125
127
|
- spec/rules/modify_spec.rb
|
126
128
|
- spec/rules/move_spec.rb
|
129
|
+
- spec/rules/update_spec.rb
|
127
130
|
- spec/selector_spec.rb
|
128
131
|
- spec/transmogrifier_spec.rb
|
129
132
|
has_rdoc:
|