light_mapper 0.0.2 → 1.0.5

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