light_mapper 0.0.2 → 1.0.5

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,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 739e3317c888c4ead5db06449dee80bf264cff23
4
- data.tar.gz: 353f10793c1cd990659934465b064fe035771f55
2
+ SHA256:
3
+ metadata.gz: 90664595ffebf1ec50dc86f05c193a044f6deee8bafe3091d18f3aed23d3ec32
4
+ data.tar.gz: e4cbe5fa16216ca94cf9c77b0fb2c96fbfc02978acf805b06984dcaedebb9a26
5
5
  SHA512:
6
- metadata.gz: 6748d296cb4d18fa6c2f49e6777ec4e269e5eb19c961baa6e57677d8014958aa59c8464cbda208da8ff8b5b5a975974bdfffd86eaf9e6ae3ad00a68f619c8f68
7
- data.tar.gz: b52174375c53a98b10c09664096fa01ee0a6ec18e841c06a0f12571cd704580c48935c907faec8b32dd77aa6e60a05b518a969ff986f9e3d5e55a62db339131c
6
+ metadata.gz: 4c91780ca138072138e84d7fadc7b8d552009107f1a40c7c96cef076181120a141182894e6d907bd28be11eb85b4db52a60496fc5b162753de42733dd2646aef
7
+ data.tar.gz: 5c27ebcc9f91eda5680d9a5105a6bd54c43e90b229d447ca457adc49a4d0504b7b4aabdab4e7b8029f799d66b1b80de32d0ce232f59b1589812f3c0f34571b9c
data/.rubocop.yml CHANGED
@@ -15,10 +15,6 @@ Style/Documentation:
15
15
  Style/PercentLiteralDelimiters:
16
16
  Enabled: false
17
17
 
18
- # Offense count: 2
19
- Style/RegexpLiteral:
20
- MaxSlashes: 0
21
-
22
18
  # Offense count: 1
23
19
  # Cop supports --auto-correct.
24
20
  # Configuration parameters: EnforcedStyle, SupportedStyles.
@@ -45,4 +41,4 @@ Metrics/LineLength:
45
41
  AllCops:
46
42
  Exclude:
47
43
  - '**/Guardfile'
48
- - '**/light_parmas.gemspec'
44
+ - '**/light_mapper.gemspec'
data/.travis.yml CHANGED
@@ -1,6 +1,4 @@
1
1
  language: ruby
2
2
  script: bundle exec rake
3
3
  rvm:
4
- - 2.0.0
5
- - 2.1.0
6
- - 2.1.4
4
+ - 3.1.2
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in light_mapper.gemspec
4
4
  gemspec
5
+
6
+ ruby '2.7.4'
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # LightMapper
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/light_mapper.svg)](http://badge.fury.io/rb/light_mapper)
4
+ [![Build Status](https://travis-ci.org/pniemczyk/light_mapper.svg?branch=master)](https://travis-ci.org/pniemczyk/light_mapper)
5
+
3
6
  This is simple mapper for hash.
4
7
 
5
8
  ## Installation
@@ -31,6 +34,16 @@ Or install it yourself as:
31
34
  'LastName' => :last_name
32
35
  )
33
36
  ```
37
+
38
+ or
39
+
40
+ ```ruby
41
+ LightMapper.mapping(
42
+ { 'FirstName' => 'Pawel', 'LastName' => 'Niemczyk' },
43
+ { 'FirstName' => :first_name, 'LastName' => :last_name }
44
+ )
45
+ ```
46
+
34
47
  result is obvious:
35
48
 
36
49
  ```ruby
@@ -53,20 +66,16 @@ data = {
53
66
  }
54
67
 
55
68
  data.extend(LightMapper).mapping(PersonMapper)
69
+ # {first_name: 'Pawel', last_name: 'Niemczyk', 'age': 5}
56
70
  ```
57
71
 
58
72
  ### When you require all keys
59
73
 
60
74
  ```ruby
61
- {
62
- 'FirstName' => 'Pawel'
63
- }.extend(LightMapper, require_keys: true).mapping(
64
- 'FirstName' => :first_name,
65
- 'LastName' => :last_name
66
- )
75
+ { 'FirstName' => 'Pawel' }.extend(LightMapper).mapping({'FirstName' => :first_name, 'LastName' => :last_name}, strict: true)
67
76
  ```
68
77
 
69
- it will raise KeyError
78
+ it will raise LightMapper::KeyMissing: LastName key not found; Full path LastName
70
79
 
71
80
  ### When you want to pass string or symbol keys
72
81
 
@@ -74,10 +83,10 @@ it will raise KeyError
74
83
  {
75
84
  'FirstName' => 'Pawel',
76
85
  second_name: 'Niemczyk'
77
- }.extend(LightMapper, any_keys_kind: true).mapping(
78
- 'FirstName' => :first_name,
79
- 'second_name' => :last_name
80
- )
86
+ }.extend(LightMapper).mapping({
87
+ 'FirstName' => :first_name,
88
+ 'second_name' => :last_name
89
+ }, any_keys: true)
81
90
  ```
82
91
 
83
92
  result will be:
@@ -86,6 +95,124 @@ result will be:
86
95
  { first_name: 'Pawel', last_name: 'Niemczyk' }
87
96
  ```
88
97
 
98
+ ### Support for nested hashes, arrays and objects (now we talking what it is capable of)
99
+
100
+ ```ruby
101
+ {
102
+ 'source' => { 'google' => { 'search_word' => 'ruby' } },
103
+ 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
104
+ 'roles' => %w[admin manager user],
105
+ 'mixed' => { users: [User.new(email: 'max@example.com', name: 'Max', manager: true), User.new(email: 'pawel@example.com', name: 'Pawel', manager: false)] },
106
+ 'scores' => [ 10, 2, 5, 1000],
107
+ 'last_4_payments' => [
108
+ { 'amount' => 100, 'currency' => 'USD' },
109
+ { 'amount' => 200, 'currency' => 'USD' },
110
+ { 'amount' => 300, 'currency' => 'USD' },
111
+ { 'amount' => 400, 'currency' => 'USD' }
112
+ ],
113
+ 'array' => [
114
+ [1,2,3],
115
+ [4,5,6],
116
+ [
117
+ 7,
118
+ 8,
119
+ [':D']
120
+ ],
121
+ ]
122
+ }.extend(LightMapper).mapping(
123
+ 'source.google.search_word' => :word,
124
+ 'user.email' => :email,
125
+ 'user.as_json.name' => :name,
126
+ 'roles.0' => :first_role,
127
+ ['roles', 1] => :middle_role,
128
+ 'roles.last' => :last_role,
129
+ (->(source) { source[:mixed][:users].find { |user| user.manager }.email }) => :manager_email,
130
+ (->(source) { source[:mixed][:users].find { |user| user.manager }.name }) => :manager_name,
131
+ 'mixed.users.last.name' => :last_user_name,
132
+ (->(source) { source[:last_4_payments].map(&:values).map(&:first).max }) => :quarterly_payment_amount,
133
+ 'scores.sum' => :final_score,
134
+ 'array.2.2.first' => :smile
135
+ )
136
+ ```
137
+
138
+ result will be:
139
+
140
+ ```ruby
141
+ {
142
+ word: 'ruby',
143
+ email: 'pawel@example.com',
144
+ name: 'Pawel',
145
+ first_role: 'admin',
146
+ last_role: 'user',
147
+ manager_email: 'max@example.com',
148
+ manager_name: 'Max',
149
+ last_user_name: 'Pawel',
150
+ quarterly_payment_amount: 1000,
151
+ final_score: 1017
152
+ }
153
+ ```
154
+
155
+ ### Support for nested output structure and symbolize output keys
156
+
157
+ ```ruby
158
+ {
159
+ 'source' => { 'google' => { 'private_pool' => true } },
160
+ 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
161
+ 'roles' => %w[admin manager user],
162
+ 'scores' => [10, 2, 5, 1000],
163
+ 'last_4_payments' => [
164
+ { 'amount' => 100, 'currency' => 'USD' },
165
+ { 'amount' => 200, 'currency' => 'USD' },
166
+ { 'amount' => 300, 'currency' => 'USD' },
167
+ { 'amount' => 400, 'currency' => 'USD' }
168
+ ]
169
+ }.extend(LightMapper).mapping({
170
+ 'source.google.private_pool' => 'private',
171
+ 'user.email' => 'user.email',
172
+ 'user.name' => 'user.name',
173
+ 'roles' => 'user.roles',
174
+ 'scores.max' => 'user.best_score',
175
+ 'last_4_payments.last' => 'payment.last',
176
+ }, keys: :symbol)
177
+ ```
178
+
179
+ result will be:
180
+
181
+ ```ruby
182
+ {
183
+ private: true,
184
+ user: {
185
+ email: 'pawel@example.com',
186
+ name: 'Pawel',
187
+ roles: %w[admin manager user],
188
+ best_score: 1000,
189
+ },
190
+ payment: {
191
+ last: { 'amount' => 400, 'currency' => 'USD' }
192
+ }
193
+ }
194
+ ```
195
+
196
+
197
+
198
+ ### Mappers selection via pattern matching
199
+
200
+ ```ruby
201
+ GOOGLE_MAPPER = { 'result.user.name' => :name }
202
+ LINKEDIN_MAPPER = { 'result.client.display_name' => :word }
203
+
204
+ data = { source: 'google', user: { name: 'test'}, 'result' => { 'user' => { 'name' => 'Pawel'} } }
205
+ mapper = case data
206
+ in source: 'google', user: {name:} then GOOGLE_MAPPER
207
+ in source: 'linkedin', client: {display_name:} then LINKEDIN_MAPPER
208
+ else
209
+ raise 'Unknown mapper'
210
+ end
211
+
212
+ data.extend(LightMapper).mapping(mapper)
213
+ # result { name: 'Pawel' }
214
+ ```
215
+
89
216
  ## Contributing
90
217
 
91
218
  1. Fork it ( https://github.com/[my-github-username]/light_mapper/fork )
data/Rakefile CHANGED
@@ -1,2 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
2
3
 
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -1,3 +1,3 @@
1
1
  module LightMapper
2
- VERSION = '0.0.2'
2
+ VERSION = '1.0.5'
3
3
  end
data/lib/light_mapper.rb CHANGED
@@ -1,29 +1,124 @@
1
1
  require 'light_mapper/version'
2
2
 
3
3
  module LightMapper
4
- def mapping(mappings, opts = {})
5
- LightMapper.mapping(clone, mappings, opts)
6
- end
4
+ BaseError = Class.new(StandardError)
5
+ InvalidKey = Class.new(BaseError)
6
+ KeyMissing = Class.new(BaseError)
7
+ InvalidStructure = Class.new(BaseError)
8
+ AlreadyAssignedValue = Class.new(BaseError)
7
9
 
8
- def self.mapping(hash, mappings, opts = {})
9
- require_keys = opts[:require_keys] == true
10
- any_keys_kind = opts[:any_keys_kind] == true
11
- fetch_method = if any_keys_kind
12
- if require_keys
13
- -> (hash, key) { hash.fetch(key.to_s, hash.fetch(key.to_sym)) }
10
+ module Helper
11
+ def self.raise_key_missing(current, full_path, additional_message = nil)
12
+
13
+ raise KeyMissing, ["#{current} key not found; Full path #{full_path.map(&:to_s).join('.')}", additional_message].compact.join('; ')
14
+ end
15
+
16
+ def self.key_destructor(value)
17
+ case value
18
+ in String
19
+ value.split('.')
20
+ in Symbol
21
+ [value]
22
+ in Array
23
+ value
14
24
  else
15
- -> (hash, key) { hash[key.to_s] || hash[key.to_sym] }
25
+ raise InvalidKey, "Invalid key type: #{value.class}"
16
26
  end
17
- else
18
- if require_keys
19
- -> (hash, key) { hash.fetch(key) }
27
+ end
28
+
29
+ def self.value_extractor(object, current, path, full_path, strict = false, any_keys = false)
30
+ result = case object
31
+ in Hash
32
+ hash_key_extractor(object, current, full_path, strict, any_keys)
33
+ in Array
34
+ array_key_extractor(object, current, full_path, strict, any_keys)
35
+ in NilClass
36
+ nil
37
+ else
38
+ method_name = current.to_s.to_sym
39
+ object.respond_to?(method_name) ? object.send(method_name) : !strict ? nil : raise_key_missing(current, full_path)
40
+ end
41
+
42
+ path.compact.empty? ? result : value_extractor(result, path.first, path[1..-1], full_path, strict, any_keys)
43
+ end
44
+
45
+ def self.hash_key_extractor(object, current, full_path, strict, any_keys)
46
+ keys = any_keys ? [current, current.to_s, current.to_s.to_sym] : [current]
47
+ raise_key_missing(current, full_path) if strict && !keys.any? { |k| object.key?(k) }
48
+
49
+ object.values_at(*keys).compact.first
50
+ end
51
+
52
+ def self.array_key_extractor(object, current, full_path, strict, _any_keys)
53
+ index = current.to_s.match(/^(\d)+$/) ? current.to_i : nil
54
+
55
+ if index
56
+ raise_key_missing(current, full_path) if strict && index && object.size < index.next
57
+
58
+ object[index]
20
59
  else
21
- -> (hash, key) { hash[key] }
60
+ method_name = current.to_s.to_sym
61
+ raise_key_missing(current, full_path, "Array do not respond on #{method_name}") if strict && !object.respond_to?(method_name)
62
+
63
+ object.public_send(method_name)
22
64
  end
23
65
  end
24
66
 
25
- {}.tap do |h|
26
- mappings.each { |k, v| h[v] = fetch_method.call(hash, k) }
67
+ def self.build_structure(mapping)
68
+ mapping.values.each_with_object({}) do |value, result|
69
+ nesting = value.to_s.split('.')[0..-2]
70
+ next result if nesting.empty?
71
+
72
+ nesting.each do |key|
73
+ result[key] ||= {}
74
+ result = result[key]
75
+ end
76
+ end
77
+ end
78
+
79
+ def self.push(hash, key, value, keys: :string, build_structure: true)
80
+ return hash[key] = value if key.is_a?(Symbol)
81
+
82
+ path = key.to_s.split('.')
83
+ path = path.map(&:to_sym) if keys == :symbol
84
+
85
+ context = hash
86
+ context[path.first] if build_structure && path.size == 2
87
+
88
+ path.each_with_index do |k, idx|
89
+ last_idx = idx == path.size - 1
90
+ raise AlreadyAssignedValue, "Key #{k} already assigned in #{path} for #{hash.inspect} structure" if last_idx && context.key?(k) && !context[k].nil?
91
+ next context[k] = value if last_idx
92
+
93
+ context.send(:[]=,k, {}) if build_structure && !context.key?(k)
94
+ context = context.send(:[], k) if context.is_a?(Hash)
95
+ end
96
+ end
97
+
98
+ def self.compact(hash)
99
+ hash.each_with_object({}) do |(key, value), result|
100
+ next if value.empty?
101
+
102
+ result[key] = value.is_a?(Hash) ? compact(value) : value
103
+ end
104
+ end
105
+ rescue IndexError
106
+ raise InvalidStructure, "Invalid key: #{key} for #{hash.inspect} structure"
107
+ end
108
+
109
+ def mapping(mappings, opts = {})
110
+ LightMapper.mapping(clone, mappings, opts)
111
+ end
112
+
113
+ def self.mapping(hash, mappings, opts = {})
114
+ strict, any_keys, keys = opts.values_at(:strict, :any_keys, :keys)
115
+
116
+ mappings.each_with_object({}) do |(k, v), h|
117
+ next Helper.push(h, v, k.call(hash), keys: keys) if k.is_a?(Proc)
118
+
119
+ key_path = Helper.key_destructor(k)
120
+ value = Helper.value_extractor(hash, key_path.first, key_path[1..-1], key_path, strict, any_keys)
121
+ Helper.push(h, v, value, keys: keys)
27
122
  end
28
123
  end
29
124
  end
data/light_mapper.gemspec CHANGED
@@ -17,8 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = %w(lib)
20
+ spec.required_ruby_version = '>= 2.7'
20
21
 
21
- spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'bundler', '~> 2.3'
22
23
  spec.add_development_dependency 'rake', '~> 10.4'
23
24
  spec.add_development_dependency 'rspec', '~> 3.0'
24
25
  spec.add_development_dependency 'guard', '~> 2.12'
@@ -1,5 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class User
4
+ attr_accessor :name, :email, :manager
5
+
6
+ def initialize(name:, email:, manager: false)
7
+ @name = name
8
+ @email = email
9
+ @manager = manager
10
+ end
11
+
12
+ def as_json
13
+ { 'name' => name, 'email' => email, 'manager' => manager }
14
+ end
15
+ end
16
+
3
17
  describe LightMapper do
4
18
 
5
19
  subject { original_hash.extend(LightMapper) }
@@ -12,7 +26,7 @@ describe LightMapper do
12
26
  }
13
27
  end
14
28
 
15
- let(:maper) do
29
+ let(:mapper) do
16
30
  {
17
31
  'A' => :a,
18
32
  'b' => 'z'
@@ -20,7 +34,7 @@ describe LightMapper do
20
34
  end
21
35
 
22
36
  it 'return correct hash' do
23
- expect(subject.mapping(maper)).to eq(a: original_hash['A'], 'z' => original_hash['b'])
37
+ expect(subject.mapping(mapper)).to eq(a: original_hash['A'], 'z' => original_hash['b'])
24
38
  end
25
39
  end
26
40
 
@@ -32,7 +46,7 @@ describe LightMapper do
32
46
  }
33
47
  end
34
48
 
35
- let(:maper) do
49
+ let(:mapper) do
36
50
  {
37
51
  'A' => :a,
38
52
  'c' => 'z'
@@ -40,11 +54,11 @@ describe LightMapper do
40
54
  end
41
55
 
42
56
  it 'raise KeyError when required key missing' do
43
- expect { subject.mapping(maper, require_keys: true) }.to raise_error(KeyError)
57
+ expect { subject.mapping(mapper, strict: true) }.to raise_error(LightMapper::KeyMissing)
44
58
  end
45
59
  end
46
60
 
47
- describe 'basic data mapping when any keys kind is allowed' do
61
+ describe 'basic data mapping when any keys kind is allowed' do
48
62
  let(:original_hash) do
49
63
  {
50
64
  'A' => 'test',
@@ -54,7 +68,7 @@ describe LightMapper do
54
68
  }
55
69
  end
56
70
 
57
- let(:maper) do
71
+ let(:mapper) do
58
72
  {
59
73
  'A' => :a,
60
74
  'c' => :c,
@@ -63,15 +77,15 @@ describe LightMapper do
63
77
  end
64
78
 
65
79
  it 'return correct hash' do
66
- expect(subject.mapping(maper, any_keys_kind: true)).to eq(
67
- a: original_hash['A'],
68
- c: original_hash[:c],
69
- 'z' => original_hash['b']
70
- )
80
+ expect(subject.mapping(mapper, any_keys: true)).to eq(
81
+ a: original_hash['A'],
82
+ c: original_hash[:c],
83
+ 'z' => original_hash['b']
84
+ )
71
85
  end
72
86
 
73
87
  describe 'raise KeyError' do
74
- let(:maper) do
88
+ let(:mapper) do
75
89
  {
76
90
  'A' => :a,
77
91
  'c' => :c,
@@ -80,11 +94,173 @@ describe LightMapper do
80
94
  end
81
95
 
82
96
  it ' when required key missing' do
83
- expect { subject.mapping(maper, require_keys: true, any_keys_kind: true) }.to raise_error(KeyError)
97
+ expect { subject.mapping(mapper, strict: true, any_keys: true) }.to raise_error(LightMapper::KeyMissing)
84
98
  end
85
99
  end
86
100
  end
87
101
 
88
- it 'nested keys'
89
- it 'value conversion'
102
+ describe 'more advanced mapping' do
103
+ let(:source) do
104
+ {
105
+ 'source' => { 'google' => { 'search_word' => 'ruby' } },
106
+ 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
107
+ 'roles' => %w[admin manager user],
108
+ 'mixed' => { users: [User.new(email: 'max@example.com', name: 'Max', manager: true), User.new(email: 'pawel@example.com', name: 'Pawel', manager: false)] },
109
+ 'scores' => [10, 2, 5, 1000],
110
+ 'last_4_payments' => [
111
+ { 'amount' => 100, 'currency' => 'USD' },
112
+ { 'amount' => 200, 'currency' => 'USD' },
113
+ { 'amount' => 300, 'currency' => 'USD' },
114
+ { 'amount' => 400, 'currency' => 'USD' }
115
+ ],
116
+ 'array' => [
117
+ [1, 2, 3],
118
+ [4, 5, 6],
119
+ [
120
+ 7,
121
+ 8,
122
+ [':D']
123
+ ],
124
+ ]
125
+ }
126
+ end
127
+
128
+ context 'with nested hash mapping' do
129
+ let(:mapping) { { 'source.google.search_word' => :word } }
130
+
131
+ it 'return correct result' do
132
+ expect(source.extend(LightMapper).mapping(mapping)).to eq(word: 'ruby')
133
+ end
134
+ end
135
+
136
+ context 'with nested array mapping' do
137
+ let(:mapping) { { 'array.2.2.first' => :result } }
138
+
139
+ it 'return correct result' do
140
+ expect(source.extend(LightMapper).mapping(mapping)).to eq(result: ':D')
141
+ end
142
+ end
143
+
144
+ context 'with nested object mapping' do
145
+ let(:mapping) { { 'user.email' => :result } }
146
+
147
+ it 'return correct result' do
148
+ expect(source.extend(LightMapper).mapping(mapping)).to eq(result: 'pawel@example.com')
149
+ end
150
+ end
151
+
152
+ context 'with mapping proc' do
153
+ let(:mapping) { { (->(source) { source['last_4_payments'].last['amount'].to_s }) => :result } }
154
+
155
+ it 'return correct result' do
156
+ expect(source.extend(LightMapper).mapping(mapping)).to eq(result: '400')
157
+ end
158
+ end
159
+
160
+ context 'with mix of nested object types' do
161
+ let(:mapping) do
162
+ {
163
+ 'source.google.search_word' => :word,
164
+ 'user.email' => :email,
165
+ 'user.as_json.name' => :name,
166
+ 'roles.0' => :first_role,
167
+ ['roles', 1] => :middle_role,
168
+ 'roles.last' => :last_role,
169
+ (->(source) { source['mixed'][:users].find { |user| user.manager }.email }) => :manager_email,
170
+ (->(source) { source['mixed'][:users].find { |user| user.manager }.name }) => :manager_name,
171
+ 'mixed.users.last.name' => :last_user_name,
172
+ (->(source) { source['last_4_payments'].map(&:values).map(&:first).max }) => :quarterly_payment_amount,
173
+ 'scores.sum' => :final_score,
174
+ 'array.2.2.first' => :smile
175
+ }
176
+ end
177
+
178
+ it 'return correct result' do
179
+ expect(source.extend(LightMapper).mapping(mapping, any_keys: true)).to match({
180
+ word: 'ruby',
181
+ email: 'pawel@example.com',
182
+ name: 'Pawel',
183
+ final_score: 1017,
184
+ first_role: 'admin',
185
+ last_role: 'user',
186
+ manager_email: 'max@example.com',
187
+ manager_name: 'Max',
188
+ last_user_name: 'Pawel',
189
+ middle_role: 'manager',
190
+ quarterly_payment_amount: 400,
191
+ smile: ':D'
192
+ })
193
+ end
194
+
195
+ it 'return correct result base on proper key types' do
196
+ expect(source.extend(LightMapper).mapping(mapping)).to match({
197
+ word: 'ruby',
198
+ email: 'pawel@example.com',
199
+ name: 'Pawel',
200
+ final_score: 1017,
201
+ first_role: 'admin',
202
+ last_role: 'user',
203
+ manager_email: 'max@example.com',
204
+ manager_name: 'Max',
205
+ last_user_name: nil,
206
+ middle_role: 'manager',
207
+ quarterly_payment_amount: 400,
208
+ smile: ':D'
209
+ })
210
+ end
211
+
212
+ it 'raise KeyMissing error' do
213
+ expect { source.extend(LightMapper).mapping({ 'user.non_existence_method' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
214
+ expect { source.extend(LightMapper).mapping({ 'last_4_payments.4' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
215
+ expect { source.extend(LightMapper).mapping({ 'source.google.missing_key' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
216
+ end
217
+ end
218
+
219
+ context 'nested input to nested output' do
220
+ let(:source) do
221
+ {
222
+ 'source' => { 'google' => { 'private_pool' => true } },
223
+ 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
224
+ 'roles' => %w[admin manager user],
225
+ 'scores' => [10, 2, 5, 1000],
226
+ 'last_4_payments' => [
227
+ { 'amount' => 100, 'currency' => 'USD' },
228
+ { 'amount' => 200, 'currency' => 'USD' },
229
+ { 'amount' => 300, 'currency' => 'USD' },
230
+ { 'amount' => 400, 'currency' => 'USD' }
231
+ ]
232
+ }
233
+ end
234
+
235
+ let(:mapping) do
236
+ {
237
+ 'source.google.private_pool' => 'private',
238
+ 'user.email' => 'user.email',
239
+ 'user.name' => 'user.name',
240
+ 'roles' => 'user.roles',
241
+ 'scores.max' => 'user.best_score',
242
+ 'last_4_payments.last' => 'payment.last',
243
+ }
244
+ end
245
+
246
+ it 'return correct result' do
247
+ expect(source.extend(LightMapper).mapping(mapping, keys: :symbol)).to match({
248
+ private: true,
249
+ user: {
250
+ email: 'pawel@example.com',
251
+ name: 'Pawel',
252
+ roles: %w[admin manager user],
253
+ best_score: 1000,
254
+ },
255
+ payment: {
256
+ last: { 'amount' => 400, 'currency' => 'USD' }
257
+ }
258
+ })
259
+ end
260
+
261
+ it 'raise AlreadyAssignedValue error when key was allready assigned' do
262
+ expect { source.extend(LightMapper).mapping('source.google.private_pool' => 'private', 'source.google' => 'private') }.to raise_error(LightMapper::AlreadyAssignedValue)
263
+ end
264
+ end
265
+ end
90
266
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pawel Niemczyk
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-27 00:00:00.000000000 Z
11
+ date: 2022-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.7'
19
+ version: '2.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.7'
26
+ version: '2.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -147,7 +147,7 @@ homepage: https://github.com/pniemczyk/light_mapper
147
147
  licenses:
148
148
  - MIT
149
149
  metadata: {}
150
- post_install_message:
150
+ post_install_message:
151
151
  rdoc_options: []
152
152
  require_paths:
153
153
  - lib
@@ -155,16 +155,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
155
  requirements:
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
- version: '0'
158
+ version: '2.7'
159
159
  required_rubygems_version: !ruby/object:Gem::Requirement
160
160
  requirements:
161
161
  - - ">="
162
162
  - !ruby/object:Gem::Version
163
163
  version: '0'
164
164
  requirements: []
165
- rubyforge_project:
166
- rubygems_version: 2.4.5
167
- signing_key:
165
+ rubygems_version: 3.2.3
166
+ signing_key:
168
167
  specification_version: 4
169
168
  summary: Very light hash mapper
170
169
  test_files: