cartograph 1.0.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 +23 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/cartograph.gemspec +24 -0
- data/examples/collection_representation.rb +41 -0
- data/examples/domains.rb +54 -0
- data/examples/representation_for.rb +24 -0
- data/lib/cartograph.rb +24 -0
- data/lib/cartograph/artist.rb +34 -0
- data/lib/cartograph/dsl.rb +104 -0
- data/lib/cartograph/map.rb +91 -0
- data/lib/cartograph/property.rb +69 -0
- data/lib/cartograph/property_collection.rb +27 -0
- data/lib/cartograph/root_key.rb +19 -0
- data/lib/cartograph/sculptor.rb +40 -0
- data/lib/cartograph/version.rb +3 -0
- data/spec/lib/cartograph/artist_spec.rb +115 -0
- data/spec/lib/cartograph/dsl_spec.rb +257 -0
- data/spec/lib/cartograph/map_spec.rb +160 -0
- data/spec/lib/cartograph/property_collection_spec.rb +15 -0
- data/spec/lib/cartograph/property_spec.rb +235 -0
- data/spec/lib/cartograph/root_key_spec.rb +38 -0
- data/spec/lib/cartograph/sculptor_spec.rb +107 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/support/dsl_contexts.rb +22 -0
- data/spec/support/dummy_comment.rb +2 -0
- data/spec/support/dummy_user.rb +2 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 75a2908b56243cc37194669f69b38f4b98b98674
|
4
|
+
data.tar.gz: d6ef9fc6b4ada540498ba187594fdff5234767aa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 46424d9ba2555a11ea4acac0ad660259b5b46d47b9a1da40226c9dd8695cb7df22a6f82d4c02c2c9a5339b15290c5ffacaec91e6329c1d210b2d0939dd71a8ea
|
7
|
+
data.tar.gz: 4bc57fb275ea7f6f3f349af9e08f34ca20c9d302b7612ae1575c1d942b86d05dc6a624170df72bed91b4d0d334d4b933be59df0dffb023d8a6fe6058928db307
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.pairs
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Cartograph Changelog
|
2
|
+
====================
|
3
|
+
|
4
|
+
### master
|
5
|
+
|
6
|
+
### [v1.0.0][v1.0.0] (September 7, 2017)
|
7
|
+
|
8
|
+
* Added support for dry-struct
|
9
|
+
([#3](https://github.com/kyrylo/cartograph/pull/3))
|
10
|
+
|
11
|
+
[v1.0.0]: https://github.com/kyrylo/cartograph/releases/tag/v1.0.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Robert Ross
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Cartograph
|
2
|
+
|
3
|
+
A Serialization / Deserialization library.
|
4
|
+
|
5
|
+
[](https://travis-ci.org/kyrylo/cartograph)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'cartograph'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Cartograph makes it easy to generate and convert JSON. It's intention is to be used for API clients.
|
20
|
+
|
21
|
+
For example, if you have an object that you would like to convert to JSON for a create request to an API. You would have something similar to this:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class UserMapping
|
25
|
+
include Cartograph::DSL
|
26
|
+
|
27
|
+
cartograph do
|
28
|
+
mapping User # The object we're mapping
|
29
|
+
|
30
|
+
property :name, :email, scopes: [:create, :update]
|
31
|
+
property :id, scopes: :read
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
user = User.new(name: 'Bobby Tables')
|
36
|
+
json_for_create = UserMapping.representation_for(:create, user)
|
37
|
+
```
|
38
|
+
|
39
|
+
### Rendering Objects or Collections as Hashes
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
user = User.new(name: 'PB Jelly')
|
43
|
+
users = [user]
|
44
|
+
|
45
|
+
hash = UserMapping.hash_for(:read, user)
|
46
|
+
hash_collection = UserMapping.hash_collection_for(:read, user)
|
47
|
+
```
|
48
|
+
|
49
|
+
### Rendering Collections as JSON
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
user = User.new(name: 'Bobby Tables')
|
53
|
+
users = Array.new(10, user)
|
54
|
+
|
55
|
+
json = UserMapping.represent_collection_for(:read, users)
|
56
|
+
```
|
57
|
+
|
58
|
+
---
|
59
|
+
|
60
|
+
Some API's will give you the created resource back as JSON as well on a successful create. For that, you may do something like this:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
response = HTTPClient.post("http://something.com/api/users", body: json_for_create)
|
64
|
+
created_user = UserMapping.extract_single(response.body, :read)
|
65
|
+
```
|
66
|
+
|
67
|
+
Most API's will have a way of retrieving an entire resource collection. For this you can instruct Cartograph to convert a collection.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
response = HTTPClient.get("http://something.com/api/users")
|
71
|
+
users = UserMapping.extract_collection(response.body, :read)
|
72
|
+
# => [ User, User, User ]
|
73
|
+
```
|
74
|
+
|
75
|
+
### Getting Harder
|
76
|
+
|
77
|
+
Sometimes resources will nest other properties under a key. Cartograph can handle this as well.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class UserMapping
|
81
|
+
include Cartograph::DSL
|
82
|
+
|
83
|
+
cartograph do
|
84
|
+
mapping User # The object we're mapping
|
85
|
+
|
86
|
+
property :name, scopes: [:read]
|
87
|
+
|
88
|
+
property :comments do
|
89
|
+
mapping Comment # The nested object we're mapping
|
90
|
+
|
91
|
+
property :text, scopes: [:read]
|
92
|
+
property :author, scopes: [:read]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Just like the previous examples, when you serialize this. It will include the comment block for the scope defined.
|
99
|
+
|
100
|
+
### Root Keys
|
101
|
+
|
102
|
+
Cartograph can also handle the event of root keys in response bodies. For example, if you receive a response with:
|
103
|
+
|
104
|
+
```json
|
105
|
+
{ "user": { "id": 123 } }
|
106
|
+
```
|
107
|
+
|
108
|
+
You could define a mapping like this:
|
109
|
+
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class UserMapping
|
113
|
+
include Cartograph::DSL
|
114
|
+
|
115
|
+
cartograph do
|
116
|
+
mapping User
|
117
|
+
root_key singular: 'user', plural: 'users', scopes: [:read]
|
118
|
+
property :id, scopes: [:read]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
This means that when you call the same thing:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
response = HTTPClient.get("http://something.com/api/users")
|
127
|
+
users = UserMapping.extract_collection(response.body, :read)
|
128
|
+
```
|
129
|
+
|
130
|
+
It will look for the root key before trying to deserialize the JSON response.
|
131
|
+
The advantage of this is it will only use the root key if there is a scope defined for it.
|
132
|
+
|
133
|
+
|
134
|
+
### Including other definitions within eachother
|
135
|
+
|
136
|
+
Sometimes you might have models that are nested within eachother on responses. Or you simply want to cleanup definitions by separating concerns. Cartograph lets you do this with includes.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
class UserMapping
|
140
|
+
include Cartograph::DSL
|
141
|
+
|
142
|
+
cartograph do
|
143
|
+
mapping User
|
144
|
+
property :id, scopes: [:read]
|
145
|
+
property :comments, plural: true, include: CommentMapping
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class CommentMapping
|
150
|
+
include Cartograph::DSL
|
151
|
+
|
152
|
+
cartograph do
|
153
|
+
mapping Comment
|
154
|
+
property :id, scopes: [:read]
|
155
|
+
property :text, scopes: [:read]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
|
161
|
+
### Scope blocks
|
162
|
+
|
163
|
+
Sometimes adding scopes to all properties can be tedious, to avoid that, you can define properties within a scope block.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class UserMapping
|
167
|
+
include Cartograph::DSL
|
168
|
+
|
169
|
+
cartograph do
|
170
|
+
scoped :read do
|
171
|
+
property :name
|
172
|
+
property :id
|
173
|
+
property :email, key: 'email_address' # The JSON returned has the key of email_address, our property is called email however.
|
174
|
+
end
|
175
|
+
|
176
|
+
scoped :update, :create do
|
177
|
+
property :name
|
178
|
+
property :email, key: 'email_address'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
Now when JSON includes comments for a user, it will know how to map the comments using the provided Cartograph definition.
|
185
|
+
|
186
|
+
---
|
187
|
+
|
188
|
+
### Caching
|
189
|
+
|
190
|
+
Cartograph has the option to cache certain serializations, determined by the way you setup the key.
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class UserMapping
|
194
|
+
include Cartograph::DSL
|
195
|
+
|
196
|
+
cartograph do
|
197
|
+
cache { Rails.cache } # As long as this respond to #fetch(key_name, options = {}, &block) it will work
|
198
|
+
cache_key { |object| object.cache_key }
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
## Contributing
|
206
|
+
|
207
|
+
1. Fork it ( https://github.com/kyrylo/cartograph/fork )
|
208
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
209
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
210
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
211
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/cartograph.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cartograph/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cartograph"
|
8
|
+
spec.version = Cartograph::VERSION
|
9
|
+
spec.authors = ["Robert Ross", "Kyrylo Silin"]
|
10
|
+
spec.email = ["silin@kyrylo.org"]
|
11
|
+
spec.summary = %q{Cartograph makes it easy to generate and convert JSON. It's intention is to be used for API clients.}
|
12
|
+
spec.description = %q{Cartograph makes it easy to generate and convert JSON. It's intention is to be used for API clients.}
|
13
|
+
spec.homepage = "https://github.com/kyrylo/cartograph"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.6'
|
23
|
+
spec.add_development_dependency 'pry', '~> 0'
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'cartograph'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class User < Struct.new(:id, :name, :comments)
|
5
|
+
end
|
6
|
+
|
7
|
+
class Comment < Struct.new(:id, :text)
|
8
|
+
end
|
9
|
+
|
10
|
+
class UserMapping
|
11
|
+
include Cartograph::DSL
|
12
|
+
|
13
|
+
cartograph do
|
14
|
+
mapping User
|
15
|
+
|
16
|
+
property :id, scopes: [:read]
|
17
|
+
property :name, scopes: [:read, :create]
|
18
|
+
|
19
|
+
property :comments, plural: true, scopes: [:read] do
|
20
|
+
mapping Comment
|
21
|
+
|
22
|
+
property :id, scopes: [:read]
|
23
|
+
property :text, scopes: [:read, :create]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
user = User.new(1, 'he@he.com')
|
29
|
+
comment = Comment.new(12, 'aksjdfhasjkdfh')
|
30
|
+
|
31
|
+
user.comments = Array.new(3, comment)
|
32
|
+
users = Array.new(4, user)
|
33
|
+
|
34
|
+
json = UserMapping.represent_collection_for(:read, users)
|
35
|
+
puts "The JSON generated from that collection:"
|
36
|
+
puts json
|
37
|
+
|
38
|
+
users_again = UserMapping.extract_collection(json, :read)
|
39
|
+
puts "\n"
|
40
|
+
puts "And the JSON slurped back into an array:"
|
41
|
+
pp users_again
|
data/examples/domains.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'cartograph'
|
2
|
+
|
3
|
+
json = '{
|
4
|
+
"domains": [
|
5
|
+
{
|
6
|
+
"name": "example.com",
|
7
|
+
"ttl": 1800,
|
8
|
+
"zone_file": "Example zone file text..."
|
9
|
+
}
|
10
|
+
],
|
11
|
+
"meta": {
|
12
|
+
"total": 1
|
13
|
+
}
|
14
|
+
}'
|
15
|
+
|
16
|
+
class Domain
|
17
|
+
attr_accessor :name, :ttl, :zone_file
|
18
|
+
end
|
19
|
+
|
20
|
+
class MetaInformation
|
21
|
+
attr_accessor :total
|
22
|
+
end
|
23
|
+
|
24
|
+
class DomainMapper
|
25
|
+
include Cartograph::DSL
|
26
|
+
|
27
|
+
cartograph do
|
28
|
+
mapping Domain
|
29
|
+
root_key singular: 'domain', plural: 'domains', scopes: [:read]
|
30
|
+
|
31
|
+
property :name, scopes: [:read]
|
32
|
+
property :ttl, scopes: [:read]
|
33
|
+
property :zone_file, scopes: [:read]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class MetaInformationMapper
|
38
|
+
include Cartograph::DSL
|
39
|
+
|
40
|
+
cartograph do
|
41
|
+
mapping MetaInformation
|
42
|
+
|
43
|
+
root_key singular: 'meta', scopes: [:read]
|
44
|
+
property :total, scopes: [:read]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
domains = DomainMapper.extract_collection(json, :read)
|
49
|
+
meta = MetaInformationMapper.extract_single(json, :read)
|
50
|
+
|
51
|
+
puts "Total Domains: #{domains.size}"
|
52
|
+
puts domains.map(&:name)
|
53
|
+
puts
|
54
|
+
puts "Total pages: #{meta.total}"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Domain
|
2
|
+
attr_accessor :name, :ttl, :zone_file
|
3
|
+
end
|
4
|
+
|
5
|
+
class DomainMapper
|
6
|
+
include Cartograph::DSL
|
7
|
+
|
8
|
+
cartograph do
|
9
|
+
mapping Domain
|
10
|
+
root_key singular: 'domain', plural: 'domains', scopes: [:read]
|
11
|
+
|
12
|
+
property :name, scopes: [:read, :create]
|
13
|
+
property :ttl, scopes: [:read, :create]
|
14
|
+
property :zone_file, scopes: [:read]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
domain = Domain.new
|
19
|
+
domain.name = 'example.com'
|
20
|
+
domain.ttl = 3600
|
21
|
+
domain.zone_file = "this wont be represented for create"
|
22
|
+
|
23
|
+
puts DomainMapper.representation_for(:create, domain)
|
24
|
+
#=> {"name":"example.com","ttl":3600}
|