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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDE3MWU3Yjc2NTU5ZjQ5NTNmMzhmZDU0YWU0N2NjZWExMjQzNmQwMA==
4
+ MmRlZjJhNjI1YTc0NWUyMGU0ZDM1NGYwNmRlNGJkYjg1MTI5ZDdhMg==
5
5
  data.tar.gz: !binary |-
6
- ZTk1NTkyN2JlZTRkMzI2MTgxZTM3ZjY2OWFmZTY4YjRjYmUxZTQwOA==
6
+ NjQxOGFhOGYwNzhhNGVjNTJkNTllNDYxZjc2ZTkyNDZiZWE0MTFhMg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZDQ3MDFkYzkyZTE2NDk1M2RlYmM1MjFlOTRhNzIxN2JmZWE4MWFlNDk5MDcw
10
- NjhkMWVlZTBjZDllZTNhZjg2MWU1OWU2N2QxNDNmYWQyYzk3MWE2YjVjNDIy
11
- YzJjNTM3NWVjNmU1YTQ3YjgxNGRhNTBlZTIxMzY4NTM4NGQzYTk=
9
+ YmQ3YmI3MTFjOGM1Y2I3ZGQ0NTc1NGE4MmEzNTgzMmUyMzllZWE1NGUyZmQ0
10
+ NzYzYWI0NGUwOTU4Y2Y3MjBkNDYzZTZjNDU5Njg2Mzk3M2RkNzRhNzRmMzEy
11
+ MDU5ODk1MWJkZmNkYTAyNjIxNmNmOWYzMzBiOGJhZmU4NzVjYjA=
12
12
  data.tar.gz: !binary |-
13
- MDdhNWI1ODUwNDcxNDQ0YTFmZDBmMjI4MzMwNTgzMjFiNDhlMGYxZDQ2MTVl
14
- NjJhMDhkZTMzOTE3NDBhMjk5YzgwNjY0OTMyYWI1MWIxYTFkZmE0NjllOTgy
15
- ZjdjZWM5YThjY2ZiMWYzNmNkNmJiZWE0MzA4ZDRjNzFhZDg5MTQ=
13
+ YzI4MzZlODAyNDZjNTA4MDEyNGVkNjFlYWI5NWJlNzk2YWQ3MjU1YTdhMWVj
14
+ ZTc2ZWFkY2JjNDFlYTcwYmVhYWM4NTVmYTUyZDc1MjU1OTk4YTg1ZTQ5ZmYz
15
+ ZTEzNGZiZjE1OGU5OGNlNGJhY2MwNTNkMDliYjdlZGE4MDczODk=
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Transmogrifier
2
2
 
3
+ [![Build Status](https://travis-ci.org/jfoley/transmogrifier.png)](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 key
9
+ #### Appending a node
8
10
  ```ruby
9
11
  engine = Transmogrifier::Engine.new
10
- append = Transmogrifier::Rules::Append.new("", "new_key", "new_value")
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
- #### Copying a key
22
+ ### Updating a node
21
23
  ```ruby
22
24
  engine = Transmogrifier::Engine.new
23
- move = Transmogrifier::Rules::Copy.new("", "key", "nested")
25
+ update = Transmogrifier::Rules::Update.new("nested.nested_key", "updated-value")
24
26
 
25
- engine.add_rule(:copy, "", "key", "nested")
27
+ engine.add_rule(update)
26
28
 
27
29
  input_hash = {"key" => "value", "nested" => {"nested_key" => "nested_value"}}
28
- output_hash = transmogrifier.run(input_hash)
30
+ output_hash = engine.run(input_hash)
29
31
 
30
- # output_hash => {"key" => "value", "nested" => {"nested_key" => "nested_value", "key" => "value"}}
32
+ # output_hash => {"key" => "value", "nested" => {"nested_key" => "updated-value"}}
31
33
  ```
32
34
 
33
- #### Deleting a key
35
+ #### Copying a node
34
36
  ```ruby
35
37
  engine = Transmogrifier::Engine.new
36
- delete = Transmogrifier::Rules::Delete("", "extra_key")
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("", "key", "nested")
65
+ move = Transmogrifier::Rules::Modify.new("key", "al", "og")
50
66
 
51
- engine.add_rule(:modify, "key", "al", "og")
67
+ engine.add_rule(move)
52
68
 
53
- input_hash = {"key" => "value", "nested" => {"nested_key" => "nested_value"}}
54
- output_hash = transmogrifier.run(input_hash)
69
+ input_hash = {"key" => "value"}
70
+ output_hash = engine.run(input_hash)
55
71
 
56
- # output_hash => {"key" => "vogue", "nested" => {"nested_key" => "nested_value"}}
72
+ # output_hash => {"key" => "vogue"}
57
73
  ```
58
74
 
59
- #### Moving a key
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 = transmogrifier.run(input_hash)
83
+ output_hash = engine.run(input_hash)
68
84
 
69
85
  # output_hash => {"nested" => {"nested_key" => "nested_value", "key" => "value"}}
70
86
  ```
@@ -22,8 +22,6 @@ module Transmogrifier
22
22
 
23
23
  def clone(key)
24
24
  matching_node = @hash[key]
25
- raise "Multiple nodes match #{key}, clone criteria ambiguous" if matching_node.nil?
26
-
27
25
  Marshal.load(Marshal.dump(matching_node))
28
26
  end
29
27
 
@@ -1,8 +1,8 @@
1
1
  module Transmogrifier
2
2
  module Rules
3
3
  class Append
4
- def initialize(parent_selector, hash)
5
- @parent_selector, @hash = parent_selector, hash
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)
@@ -14,6 +14,7 @@ module Transmogrifier
14
14
  parents.each do |parent|
15
15
  to_parent = parent.find_all(to_keys).first
16
16
  object = parent.find_all(from_keys).first.clone(from_key)
17
+ next if object.nil?
17
18
 
18
19
  if to_child = to_parent.find_all([to_key]).first
19
20
  to_child.append(object)
@@ -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
@@ -1,4 +1,6 @@
1
1
  module Transmogrifier
2
+ class SelectorNotFoundError < RuntimeError; end
3
+
2
4
  class Selector
3
5
  FILTER_REGEX = /\[(.*)\]/
4
6
  OPERATORS = ["!=", "="]
@@ -1,3 +1,3 @@
1
1
  module Transmogrifier
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -66,14 +66,25 @@ module Transmogrifier
66
66
  end
67
67
 
68
68
  describe "#clone" do
69
- it "returns a copy of value" do
70
- value = "other_value"
71
- hash = {"key" => "value", "extra_key" => value}
72
- node = HashNode.new(hash)
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
- expect(node.clone("extra_key")).to eql(value)
75
- expect(node.clone("extra_key")).to_not be(value)
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
 
@@ -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
@@ -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 "appends to the hash" do
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 "appends to the array" do
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
@@ -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.3
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: