response_mapper 0.1.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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/Rakefile +18 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/response_mapper.rb +75 -0
- data/response_mapper.gemspec +33 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c76a14c4bd55e3571d20c84f80bb571fb611443
|
4
|
+
data.tar.gz: 59b49f26798a52e2ce508c0d6bfb92de67015a1f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 53f9bb135997d413a86f147749afe9aa61e2a35ba6a9723d5e4c5e59ff45b5b5045580d5d2d88a6621ed5543b1aab6e95414627e88e0ba115daa9a10517c7702
|
7
|
+
data.tar.gz: cb674456b6d0428fdb0ac2a0911382bbbe236f15079719200046bcbb2ec7c01554761f2470a6eeca605ed3d1e34dd95afe638a3f655454bc5b2f91553d84de52
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Sergii Makagon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
[](https://travis-ci.org/smakagon/response_mapper)
|
2
|
+
|
3
|
+
# ResponseMapper
|
4
|
+
|
5
|
+
These days we all deal with many different APIs.
|
6
|
+
It can be either third-party services, or our own microservices.
|
7
|
+
Not all of them are well-designed and sometimes their attributes named in a really weird way.
|
8
|
+
|
9
|
+
`ResponseMapper` allows to map attributes from API response to your domain language.
|
10
|
+
|
11
|
+
For example:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
response = JSON.parse(response_from_api)
|
15
|
+
# { "order_number" => 10, "order_items" => [{ order_item_id: 1, item_title: "Book" }] }
|
16
|
+
```
|
17
|
+
|
18
|
+
Once we parsed response, all keys are strings. Usually we want to do two things:
|
19
|
+
|
20
|
+
1. Map response, so we can easily instantiate entity from response.
|
21
|
+
2. Symbolize keys to keep things consistent.
|
22
|
+
|
23
|
+
With `ResponseMapper` we can do this:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
mapping = { order_number: :id, order_item_id: :id, order_items: :items, item_title: :title }
|
27
|
+
|
28
|
+
order_attributes = ResponseMapper.map(data: response, mapping: mapping)
|
29
|
+
# { id: 10, items: [{ id: 1, title: "Book" }] }
|
30
|
+
```
|
31
|
+
|
32
|
+
Now we have nice Hash with symbolized keys that correspond to attributes of `Order` in our system.
|
33
|
+
For example further step could be just wrap this hash into `Order` entity:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
Entity::Order.new(order_attributes)
|
37
|
+
```
|
38
|
+
|
39
|
+
`ResponseMapper` maps and symbolizes keys even for nested arrays and hashes.
|
40
|
+
It will work for more complex responses.
|
41
|
+
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
Add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'response_mapper'
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
$ bundle
|
53
|
+
|
54
|
+
Or install it yourself as:
|
55
|
+
|
56
|
+
$ gem install response_mapper
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
`ResponseMapper` provides one class method `.map` which has two required params: `data` and `mapping`.
|
61
|
+
There is one optional parameter: `symbolize_keys` which is set to `true` by default.
|
62
|
+
|
63
|
+
### data
|
64
|
+
`data` can be anything, but `ResponseMapper` will try to map it only if it's a `Hash` or `Array`.
|
65
|
+
If it's not a `Hash` or `Array` - `ResponseMapper` will return `data` as is.
|
66
|
+
|
67
|
+
### mapping
|
68
|
+
`mapping` should be a Hash with attributes you want to map:
|
69
|
+
|
70
|
+
```
|
71
|
+
{ order_number: :id }
|
72
|
+
```
|
73
|
+
|
74
|
+
It will map any occurence of `:order_number` in data to `:id`.
|
75
|
+
If `mapping` is not a Hash (or empty Hash) - ResponseMapper will raise `ResponseMapper::Error`.
|
76
|
+
|
77
|
+
### symbolize_keys
|
78
|
+
`sybmolize_keys` is set to `true` by default.
|
79
|
+
If your data contains hashes with strings as keys, they will be symbolized and then mapped.
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/smakagon/response_mapper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
RuboCop::RakeTask.new(:rubocop)
|
9
|
+
|
10
|
+
task default: %i[rubocop spec]
|
11
|
+
|
12
|
+
task :console do
|
13
|
+
exec 'irb -r response_mapper -I ./lib'
|
14
|
+
end
|
15
|
+
|
16
|
+
task :build do
|
17
|
+
system 'gem build response_mapper.gemspec'
|
18
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "response_mapper"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Allows to map API response to domain language of your application
|
4
|
+
#
|
5
|
+
# ResponseMapper.map(
|
6
|
+
# data: { order_number: 1, order_items: [1,2,3] }
|
7
|
+
# mapping: { order_number: :id, order_items: :items }
|
8
|
+
# )
|
9
|
+
#
|
10
|
+
# Returns nice Hash with proper naming
|
11
|
+
# which could be used to instantiate order entity:
|
12
|
+
# { id: 1, items: [1,2,3] }
|
13
|
+
#
|
14
|
+
class ResponseMapper
|
15
|
+
VERSION = '0.1.0'
|
16
|
+
|
17
|
+
Error = Class.new(StandardError)
|
18
|
+
|
19
|
+
def self.map(data:, mapping:, symbolize_keys: true)
|
20
|
+
new(data, mapping, symbolize_keys).map
|
21
|
+
end
|
22
|
+
|
23
|
+
def map
|
24
|
+
map_data(data)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :data, :mapping, :symbolize_keys
|
30
|
+
|
31
|
+
def initialize(data, mapping, symbolize_keys)
|
32
|
+
@data = data
|
33
|
+
@mapping = mapping
|
34
|
+
@symbolize_keys = symbolize_keys
|
35
|
+
|
36
|
+
validate_mapping
|
37
|
+
end
|
38
|
+
|
39
|
+
def map_data(data)
|
40
|
+
case data
|
41
|
+
when Hash then map_hash(data)
|
42
|
+
when Array then map_array(data)
|
43
|
+
else
|
44
|
+
data
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def map_hash(hash)
|
49
|
+
hash.each_with_object({}) do |(k, v), result|
|
50
|
+
k = k.to_sym if symbolize_keys
|
51
|
+
|
52
|
+
mapped_value = [Hash, Array].include?(v.class) ? map_data(v) : v
|
53
|
+
mapped_key = mapping.fetch(k, k)
|
54
|
+
|
55
|
+
result[mapped_key] = mapped_value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def map_array(array)
|
60
|
+
array.each_with_object([]) do |entity, arr|
|
61
|
+
if [Hash, Array].include?(entity.class)
|
62
|
+
arr.push(map_data(entity))
|
63
|
+
else
|
64
|
+
arr.push(mapping.fetch(entity, entity))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_mapping
|
70
|
+
# rubocop:disable Style/GuardClause
|
71
|
+
if !mapping.is_a?(Hash) || mapping&.empty?
|
72
|
+
raise Error, 'Please, provide Hash with mapping, for example: { order_number: :id }' # rubocop:disable LineLength
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
lib = File.expand_path('../lib', __FILE__)
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'response_mapper'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'response_mapper'
|
10
|
+
spec.version = ResponseMapper::VERSION
|
11
|
+
spec.authors = ['Sergii Makagon']
|
12
|
+
spec.email = ['makagon87@gmail.com']
|
13
|
+
|
14
|
+
# rubocop:disable LineLength
|
15
|
+
spec.summary = 'Allows to map API response to domain language of your application'
|
16
|
+
spec.description = 'Usually API returns data with naming that might not be the best fit for your application. You can map customer_id to user_id with ResponseMapper easily.'
|
17
|
+
spec.homepage = 'https://github.com/smakagon/response_mapper'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
# rubocop:enable LineLength
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
22
|
+
f.match(%r{^(test|spec|features)/})
|
23
|
+
end
|
24
|
+
spec.bindir = 'exe'
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ['lib']
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.49'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: response_mapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergii Makagon
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.49'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.49'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.10'
|
83
|
+
description: Usually API returns data with naming that might not be the best fit for
|
84
|
+
your application. You can map customer_id to user_id with ResponseMapper easily.
|
85
|
+
email:
|
86
|
+
- makagon87@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/setup
|
101
|
+
- lib/response_mapper.rb
|
102
|
+
- response_mapper.gemspec
|
103
|
+
homepage: https://github.com/smakagon/response_mapper
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.6.13
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Allows to map API response to domain language of your application
|
127
|
+
test_files: []
|