yahm 0.1.0 → 0.2.0
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 +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: []
|