cartograph 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/kyrylo/cartograph.svg?branch=master)](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}
|