yahm 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -9
- data/lib/yahm.rb +2 -133
- data/lib/yahm/mapping.rb +11 -2
- data/lib/yahm/version.rb +1 -1
- data/spec/yahm/mapping_spec.rb +13 -0
- data/spec/yahm_spec.rb +0 -85
- data/yahm.gemspec +1 -1
- metadata +17 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fe26e652bc9220089279697888c2caa5b3469b4
|
4
|
+
data.tar.gz: 49fcff2c3a44c6e2fcd3a870eb3db4521bdc47be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20b69251b456cb660f337f03eb9e18357a8f6d3f3d8a20db461b49021dd1566a0ddaa33828efbc9750448f58a4508459a8f25815d9c6387995b2554076882021
|
7
|
+
data.tar.gz: 0062463e2cf0ac9bbea45c04deb07cffe4302eb83cb059d2126a8e618b601aee08ddaf135594884a2b11230b3b91d7633be23fbef05cad819f2e443914f265b9
|
data/README.md
CHANGED
@@ -69,7 +69,21 @@ end
|
|
69
69
|
Record.new({ ... })
|
70
70
|
=> { :id => "...", :count => ..., :subjects => { :most_important_subject => "..."}, ... }
|
71
71
|
```
|
72
|
-
###
|
72
|
+
### Mapping options
|
73
|
+
|
74
|
+
#### `context:`
|
75
|
+
|
76
|
+
Something that responds to a method call, e.g. a class. Is used the resolve undefined methods of lambdas/procs.
|
77
|
+
|
78
|
+
#### `use_threads: [true|false]` [experimental]
|
79
|
+
|
80
|
+
Use a thread pool to execute to rules of a mapping. The current implementation is very naiv. No locking of shared data etc. Use this *only* if *you know* what you are doing and if your rules a complex enough to compensate the overhead introduced by threading.
|
81
|
+
|
82
|
+
#### `thread_pool_size: [number]` [experimental]
|
83
|
+
|
84
|
+
The number of threads used to execute rules if `use_threads: true`.
|
85
|
+
|
86
|
+
### Per rule options
|
73
87
|
|
74
88
|
#### `to: [string|lambda|proc]`
|
75
89
|
|
@@ -91,14 +105,6 @@ You can pass a lambda/proc as `processed_by` option. Inside of lambdas or procs,
|
|
91
105
|
|
92
106
|
If a value is a string and `split_by` is supplied, the string is splitted by the given character and an array would be returned.
|
93
107
|
|
94
|
-
#### `use_threads: [true|false]` [experimental]
|
95
|
-
|
96
|
-
Use a thread pool to execute to rules of a mapping. The current implementation is very naiv. No locking of shared data etc. Use this *only* if *you know* what you are doing and if your rules a complex enough to compensate the overhead introduced by threading.
|
97
|
-
|
98
|
-
#### `thread_pool_size: [number]` [experimental]
|
99
|
-
|
100
|
-
The number of threads used to execute rules if `use_threads: true`.
|
101
|
-
|
102
108
|
## Related work
|
103
109
|
|
104
110
|
* hash_mapper (https://github.com/ismasan/hash_mapper)
|
data/lib/yahm.rb
CHANGED
@@ -1,137 +1,6 @@
|
|
1
|
-
require "
|
1
|
+
require "hpath"
|
2
2
|
|
3
3
|
class Yahm
|
4
4
|
require "yahm/mapping"
|
5
|
-
|
6
|
-
def self.get(object, path)
|
7
|
-
current_path_element, remaining_path = split_path(path)
|
8
|
-
|
9
|
-
if hash_path?(current_path_element) && object.is_a?(Hash)
|
10
|
-
key = current_path_element[1..-1]
|
11
|
-
|
12
|
-
# handle cases where one forces symbol/string keys
|
13
|
-
value =
|
14
|
-
if key.start_with?(":")
|
15
|
-
object[key[1..-1].to_sym]
|
16
|
-
elsif key.start_with?('"') && key.end_with?('"')
|
17
|
-
object[key[1..-2]]
|
18
|
-
else
|
19
|
-
object[key.to_sym] || object[key.to_s]
|
20
|
-
end
|
21
|
-
|
22
|
-
unless value.nil?
|
23
|
-
remaining_path ? self.get(value, remaining_path) : value
|
24
|
-
else
|
25
|
-
value
|
26
|
-
end
|
27
|
-
elsif array_path?(current_path_element) && object.is_a?(Array)
|
28
|
-
index_parameter = current_path_element.slice(1..-2)
|
29
|
-
|
30
|
-
index =
|
31
|
-
if index_parameter == ""
|
32
|
-
0..object.length
|
33
|
-
else
|
34
|
-
index_parameter.to_i
|
35
|
-
end
|
36
|
-
|
37
|
-
if remaining_path
|
38
|
-
unless object[index].nil?
|
39
|
-
map_result = object[index.is_a?(Range) ? index : index..index].map do |array_element|
|
40
|
-
self.get(array_element, remaining_path)
|
41
|
-
end.compact
|
42
|
-
|
43
|
-
map_result == [] ? nil : map_result.flatten(array_depth(map_result) - 1)
|
44
|
-
else
|
45
|
-
nil
|
46
|
-
end
|
47
|
-
else
|
48
|
-
object[index]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.set(object, path, value)
|
54
|
-
current_path_element, remaining_path = split_path(path)
|
55
|
-
next_path_element = split_path(remaining_path)[0]
|
56
|
-
|
57
|
-
if hash_path?(current_path_element) && object.is_a?(Hash)
|
58
|
-
key = current_path_element[1..-1]
|
59
|
-
|
60
|
-
# handle cases where one forces symbol/string keys
|
61
|
-
key =
|
62
|
-
if key.start_with?(":")
|
63
|
-
key[1..-1].to_sym
|
64
|
-
elsif key.start_with?('"') && key.end_with?('"')
|
65
|
-
key[1..-2]
|
66
|
-
else
|
67
|
-
key.to_sym # keys should be symbols per default
|
68
|
-
end
|
69
|
-
|
70
|
-
if next_path_element.nil?
|
71
|
-
object[key] = value
|
72
|
-
elsif hash_path?(next_path_element)
|
73
|
-
self.set(object[key] || object[key] = {}, remaining_path, value)
|
74
|
-
elsif array_path?(next_path_element)
|
75
|
-
self.set(object[key] || object[key] = [], remaining_path, value)
|
76
|
-
end
|
77
|
-
elsif array_path?(current_path_element) && object.is_a?(Array)
|
78
|
-
if hash_path?(next_path_element)
|
79
|
-
object.push(_object = {})
|
80
|
-
self.set(_object, remaining_path, value)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
#
|
86
|
-
private
|
87
|
-
#
|
88
|
-
def self.array_depth(array)
|
89
|
-
depth = 1
|
90
|
-
until array == (flattend_array = array.flatten(1))
|
91
|
-
depth += 1
|
92
|
-
array = flattend_array
|
93
|
-
end
|
94
|
-
|
95
|
-
depth
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.array_path?(path)
|
99
|
-
path[0] == "["
|
100
|
-
end
|
101
|
-
|
102
|
-
def decode_string_or_symbol_from(string)
|
103
|
-
if string.start_with?(":")
|
104
|
-
string[1..-1].to_sym
|
105
|
-
elsif string.start_with?('"') && string.end_with?('"')
|
106
|
-
string[1..-2]
|
107
|
-
else
|
108
|
-
string.to_sym # defaults to symbols
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.hash_path?(path)
|
113
|
-
path[0] == "/"
|
114
|
-
end
|
115
|
-
|
116
|
-
def self.split_path(path)
|
117
|
-
unless path.nil?
|
118
|
-
path_elements = path
|
119
|
-
.gsub(/(\/)|(\[)/,
|
120
|
-
"/" => " /",
|
121
|
-
"[" => " [",
|
122
|
-
)
|
123
|
-
.split(" ")
|
124
|
-
|
125
|
-
remaining_path =
|
126
|
-
if path_elements.length > 1
|
127
|
-
path_elements.slice(1..-1).join
|
128
|
-
else
|
129
|
-
nil
|
130
|
-
end
|
131
|
-
|
132
|
-
[path_elements.first, remaining_path]
|
133
|
-
else
|
134
|
-
[nil, nil]
|
135
|
-
end
|
136
|
-
end
|
5
|
+
require "yahm/version"
|
137
6
|
end
|
data/lib/yahm/mapping.rb
CHANGED
@@ -60,7 +60,16 @@ class Yahm::Mapping
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def apply_rule(rule, input_hash, output_hash)
|
63
|
-
value =
|
63
|
+
value =
|
64
|
+
if rule[:path].is_a?(Array)
|
65
|
+
rule[:path].inject({}) do |memo, _path|
|
66
|
+
memo[_path] = Hpath.get(input_hash, _path)
|
67
|
+
memo
|
68
|
+
end
|
69
|
+
else
|
70
|
+
Hpath.get(input_hash, rule[:path])
|
71
|
+
end
|
72
|
+
|
64
73
|
value = rule[:default] && value.nil? ? rule[:default] : value
|
65
74
|
value = rule[:force_array] ? (value.nil? ? [] : [value].flatten(1)) : value
|
66
75
|
value = rule[:split_by] && value.is_a?(String) ? value.split(rule[:split_by]).map!(&:strip) : value
|
@@ -69,7 +78,7 @@ class Yahm::Mapping
|
|
69
78
|
if rule[:to].is_a?(Proc)
|
70
79
|
instance_exec(value, output_hash, &rule[:to])
|
71
80
|
else
|
72
|
-
|
81
|
+
Hpath.set(output_hash, rule[:to], value)
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
data/lib/yahm/version.rb
CHANGED
data/spec/yahm/mapping_spec.rb
CHANGED
@@ -131,5 +131,18 @@ describe Yahm::Mapping do
|
|
131
131
|
:array_length => 2
|
132
132
|
})
|
133
133
|
end
|
134
|
+
|
135
|
+
it "can map multiple input fields to one output field" do
|
136
|
+
mapping = Yahm::Mapping.new do
|
137
|
+
map ["/hello", "/world", "/foo"], to: "/combined", processed_by: lambda { |v| v.values.compact.join(" ") }
|
138
|
+
end
|
139
|
+
|
140
|
+
expect(mapping.apply_to({
|
141
|
+
hello: "hello",
|
142
|
+
world: "world"
|
143
|
+
})).to eq({
|
144
|
+
combined: "hello world"
|
145
|
+
})
|
146
|
+
end
|
134
147
|
end
|
135
148
|
end
|
data/spec/yahm_spec.rb
CHANGED
@@ -5,89 +5,4 @@ describe Yahm do
|
|
5
5
|
expect(Yahm.const_defined? :Mapping).to be_true
|
6
6
|
expect(Yahm::Mapping.class.is_a? Class)
|
7
7
|
end
|
8
|
-
|
9
|
-
describe "#get" do
|
10
|
-
let(:object) do
|
11
|
-
{
|
12
|
-
hello: "world",
|
13
|
-
empty_array: [],
|
14
|
-
empty_hash: [],
|
15
|
-
"string_key" => "foobar",
|
16
|
-
some: {
|
17
|
-
really: [
|
18
|
-
{
|
19
|
-
nested: [
|
20
|
-
{
|
21
|
-
structure: "hello"
|
22
|
-
},
|
23
|
-
{
|
24
|
-
structure: "world"
|
25
|
-
}
|
26
|
-
]
|
27
|
-
}
|
28
|
-
]
|
29
|
-
},
|
30
|
-
foo: [
|
31
|
-
{
|
32
|
-
bar: 1
|
33
|
-
},
|
34
|
-
{
|
35
|
-
bar: 2
|
36
|
-
}
|
37
|
-
],
|
38
|
-
arr_of_arrs: [
|
39
|
-
[ [1,2] , [3,4,5] ],
|
40
|
-
[ 6,7 ],
|
41
|
-
8
|
42
|
-
]
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
it "returns elements which match the given path" do
|
47
|
-
expect(Yahm.get(object, "/hello")).to eq("world")
|
48
|
-
expect(Yahm.get(object, "/empty_array")).to eq([])
|
49
|
-
expect(Yahm.get(object, "/empty_hash")).to eq([])
|
50
|
-
expect(Yahm.get(object, "/foo")).to eq([{:bar=>1}, {:bar=>2}])
|
51
|
-
expect(Yahm.get(object, "/foo[0]")).to eq({:bar=>1})
|
52
|
-
expect(Yahm.get(object, "/foo[1]")).to eq({:bar=>2})
|
53
|
-
expect(Yahm.get(object, "/foo[]/bar")).to eq([1,2])
|
54
|
-
expect(Yahm.get(object, "/arr_of_arrs[0][1][2]")).to eq([5])
|
55
|
-
expect(Yahm.get(object, "/some/really[]/nested[]/structure")).to eq(["hello", "world"])
|
56
|
-
expect(Yahm.get(object, "/arr_of_arrs[0]")).to eq([[1, 2], [3, 4, 5]])
|
57
|
-
end
|
58
|
-
|
59
|
-
it "returns nil if the path is invalid" do
|
60
|
-
expect(Yahm.get(object, "/hello_world")).to eq(nil)
|
61
|
-
expect(Yahm.get(object, "/foo[3]")).to eq(nil)
|
62
|
-
expect(Yahm.get(object, "/foo[]/bar/muff")).to eq(nil)
|
63
|
-
expect(Yahm.get(object, "/foo[]/bar[0]")).to eq(nil)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "allows specification of the key type using : or \"\"" do
|
67
|
-
expect(Yahm.get(object, '/"string_key"')).to eq("foobar")
|
68
|
-
expect(Yahm.get(object, '/:string_key"')).to eq(nil)
|
69
|
-
expect(Yahm.get(object, "/string_key")).to eq("foobar")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe "#set" do
|
74
|
-
it "sets the objects value according to path and value" do
|
75
|
-
Yahm.set(object = {}, "/foo[]/bar", [1,2,3])
|
76
|
-
expect(object).to eq({:foo=>[{:bar=>[1, 2, 3]}]})
|
77
|
-
end
|
78
|
-
|
79
|
-
it "allows specification of the key type using : or \"\"" do
|
80
|
-
Yahm.set(object = {}, "/:foo/\"bar\"", "foobar")
|
81
|
-
expect(object).to eq({:foo=>{"bar"=>"foobar"}})
|
82
|
-
end
|
83
|
-
|
84
|
-
it "extends existing arrays/hashes if present" do
|
85
|
-
Yahm.set(object = { foo: [{bar: [1,2,3]}] }, "/foo[]/bar", [4,5,6])
|
86
|
-
expect(object).to eq({:foo=>[{:bar=>[1, 2, 3]}, {:bar=>[4, 5, 6]}]})
|
87
|
-
|
88
|
-
Yahm.set(object = { foo: "bar" }, "/muff", "muffmuff")
|
89
|
-
expect(object).to eq({:foo=>"bar", :muff=>"muffmuff"})
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
8
|
end
|
data/yahm.gemspec
CHANGED
@@ -7,7 +7,6 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "yahm"
|
8
8
|
spec.version = Yahm::VERSION
|
9
9
|
spec.authors = ["Michael Sievers"]
|
10
|
-
spec.email = ["m.sievers@ub.uni-paderborn.de"]
|
11
10
|
spec.summary = %q{Yet another ruby hash mapper}
|
12
11
|
spec.homepage = "https://github.com/ubpb/yahm"
|
13
12
|
spec.license = "MIT"
|
@@ -17,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
17
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
17
|
spec.require_paths = ["lib"]
|
19
18
|
|
19
|
+
spec.add_dependency "hpath", ">= 0.0.5"
|
20
20
|
spec.add_dependency "thread", ">= 0.1.4"
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.5"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yahm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Sievers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hpath
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.5
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: thread
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,8 +81,7 @@ dependencies:
|
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
69
83
|
description:
|
70
|
-
email:
|
71
|
-
- m.sievers@ub.uni-paderborn.de
|
84
|
+
email:
|
72
85
|
executables: []
|
73
86
|
extensions: []
|
74
87
|
extra_rdoc_files: []
|