transmogrifier 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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: