http_api_tools 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +479 -0
- data/Rakefile +4 -0
- data/http_api_tools.gemspec +29 -0
- data/lib/http_api_tools/base_json_serializer.rb +103 -0
- data/lib/http_api_tools/expanded_relation_includes.rb +77 -0
- data/lib/http_api_tools/identity_map.rb +42 -0
- data/lib/http_api_tools/json_serializer_dsl.rb +62 -0
- data/lib/http_api_tools/model/acts_like_active_model.rb +16 -0
- data/lib/http_api_tools/model/attributes.rb +159 -0
- data/lib/http_api_tools/model/has_many_array.rb +47 -0
- data/lib/http_api_tools/model/transformers/date_time_transformer.rb +31 -0
- data/lib/http_api_tools/model/transformers/registry.rb +55 -0
- data/lib/http_api_tools/model.rb +2 -0
- data/lib/http_api_tools/nesting/json_serializer.rb +45 -0
- data/lib/http_api_tools/nesting/relation_loader.rb +89 -0
- data/lib/http_api_tools/relation_includes.rb +146 -0
- data/lib/http_api_tools/serializer_registry.rb +27 -0
- data/lib/http_api_tools/sideloading/json_deserializer.rb +121 -0
- data/lib/http_api_tools/sideloading/json_deserializer_mapping.rb +27 -0
- data/lib/http_api_tools/sideloading/json_serializer.rb +125 -0
- data/lib/http_api_tools/sideloading/relation_sideloader.rb +79 -0
- data/lib/http_api_tools/sideloading/sideload_map.rb +54 -0
- data/lib/http_api_tools/type_key_resolver.rb +27 -0
- data/lib/http_api_tools/version.rb +3 -0
- data/lib/http_api_tools.rb +10 -0
- data/reports/empty.png +0 -0
- data/reports/minus.png +0 -0
- data/reports/plus.png +0 -0
- data/spec/http_api_tools/expanded_relation_includes_spec.rb +31 -0
- data/spec/http_api_tools/identity_map_spec.rb +31 -0
- data/spec/http_api_tools/model/attributes_spec.rb +170 -0
- data/spec/http_api_tools/model/has_many_array_spec.rb +48 -0
- data/spec/http_api_tools/model/transformers/date_time_transformer_spec.rb +36 -0
- data/spec/http_api_tools/model/transformers/registry_spec.rb +53 -0
- data/spec/http_api_tools/nesting/json_serializer_spec.rb +173 -0
- data/spec/http_api_tools/relation_includes_spec.rb +196 -0
- data/spec/http_api_tools/sideloading/json_deserializer_spec.rb +93 -0
- data/spec/http_api_tools/sideloading/json_serializer_performance_spec.rb +51 -0
- data/spec/http_api_tools/sideloading/json_serializer_spec.rb +174 -0
- data/spec/http_api_tools/sideloading/sideload_map_spec.rb +59 -0
- data/spec/http_api_tools/support/company_deserializer_mapping.rb +11 -0
- data/spec/http_api_tools/support/person_deserializer_mapping.rb +9 -0
- data/spec/http_api_tools/support/spec_models.rb +89 -0
- data/spec/http_api_tools/support/spec_nesting_serializers.rb +41 -0
- data/spec/http_api_tools/support/spec_sideloading_serializers.rb +41 -0
- data/spec/http_api_tools/type_key_resolver_spec.rb +19 -0
- data/spec/spec_helper.rb +8 -0
- metadata +214 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTc5ODNiODdjNjE4ZWQyZDFmNDAwZDVkZjU2MDkxZDQyOWJkYmQxMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YTdmODFmOTQ1ZjM5YWNmNDE5ODg0ZWNjOGI2NjY3NmFlNDhmYjlhNA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDliOWFhOTViZmYwNzQ0OGM3Mzk4NTgzOGFiOWU5NjBmYTRiMmJmY2UwNTcz
|
10
|
+
YWNlM2EwOWM0YTMwZmRlZjZmNjZlZDlkMmI4ZmI0ODdkZTZmYzYwNjE0MDBh
|
11
|
+
NzAzNTM1ZGY0ZWY5MDVjODU2YTJiZDY5YWFhZjc5NjZiN2FhMjI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YzNkNWRlOGNiZGVjMGQ1MDEwOTcwMWQ0NTI0Y2Y5MTY5MzJlMWQxN2UxZDg2
|
14
|
+
YjgxMzVkNzM3MmMwZjdhZWMzNjdjZjZlOGQ4MWI0ZGQ5ZTkzNGRjMzMzNTUw
|
15
|
+
Nzg1NmYyNzBjZjhmZGE4NjcxMWNmZDhlZDg1NmE5MWVhNDQzODI=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Hooroo
|
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,479 @@
|
|
1
|
+
|
2
|
+
# Http API Tools
|
3
|
+
|
4
|
+
Provides fast serialization/deserialization of models with simple model attribute definition in client apps.
|
5
|
+
|
6
|
+
Adheres to the ID Based Json API Spec - http://jsonapi.org/format/#id-based-json-api for serialization
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'http_api_tools'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install http_api_tools
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
At a high level this gem provides serialization of models (active model or otherwise), deserialization of the serialized json and a way to declaritively create basic models in clients with basic type coercion.
|
24
|
+
|
25
|
+
It has been written to work as a whole where the producer and client of the api are both maintained by the same development team. Conventions are used throughout to keep things simple. At this stage, breaking these conventions isn't supported in many cases but the gem can be extended towards this goal as the needs arise. Please see the note on performance in the section on contributing at the end of this document.
|
26
|
+
|
27
|
+
### Serialization
|
28
|
+
There are two supported serialization formats - sideloading and nesting. Both formats maintain an identical api and
|
29
|
+
usage pattern while serializing in different ways. While it is possible to provide both formats in an application, it's likely you'd stick to one as the general philosophy is thttp_api_tools a resource should always be represented in the same way.
|
30
|
+
|
31
|
+
To use a serializer in a controller you should instantiate an instance of the serializer for the top level type you're serializing and pass it to render.
|
32
|
+
|
33
|
+
`render json: UserSerializer.new(user)`
|
34
|
+
|
35
|
+
|
36
|
+
#### Nesting vs Sideloading
|
37
|
+
The big difference between these formats is thttp_api_tools nesting represents the relationships between resources implicitly in it's structure whereas sideloading is a flattened structure with relationships represented via linked identifiers. The details of these formats will be described in more detail below.
|
38
|
+
|
39
|
+
|
40
|
+
#### Serializer Definition
|
41
|
+
|
42
|
+
This serializer will either be defined as a nesting or sideloading serializer depening on the serializer it is based on.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class UserSerializer
|
46
|
+
|
47
|
+
include HttpApiTools::Sideloading::JsonSerializer
|
48
|
+
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class UserSerializer
|
54
|
+
|
55
|
+
include HttpApiTools::Nesting::JsonSerializer
|
56
|
+
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
Serializers can define attributes and relationships to be serialized.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
class UserSerializer
|
65
|
+
|
66
|
+
include HttpApiTools::Sideloading::JsonSerializer
|
67
|
+
|
68
|
+
serializes(User)
|
69
|
+
attributes :id, :first_name, :last_name
|
70
|
+
has_many :posts
|
71
|
+
has_one :profile
|
72
|
+
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
If you want to serialize any composite attributes they can be defined as a method on the serializer and defined as an attribute. The object being serialized can be accessed via the `serializable` method on the serializer.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class UserSerializer
|
80
|
+
|
81
|
+
include HttpApiTools::Sideloading::JsonSerializer
|
82
|
+
|
83
|
+
serializes(User)
|
84
|
+
attributes :id, :first_name, :last_name, :full_name
|
85
|
+
|
86
|
+
def full_name
|
87
|
+
"#{serializable.first_name} #{serializable.last_name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
#### JSON Structure
|
94
|
+
|
95
|
+
##### Sideloading
|
96
|
+
|
97
|
+
By default, only the ids of related objects will be serialized. For serializers using a 'sideloading' approach, these relationships and their ids will be added to the `links` hash.
|
98
|
+
|
99
|
+
|
100
|
+
```javascript
|
101
|
+
{
|
102
|
+
"users": [{
|
103
|
+
"id": 1,
|
104
|
+
"first_name": "John",
|
105
|
+
"last_name": "Smith",
|
106
|
+
"links" {
|
107
|
+
"profile": 2,
|
108
|
+
"posts": [3, 4]
|
109
|
+
}
|
110
|
+
}]
|
111
|
+
}
|
112
|
+
```
|
113
|
+
|
114
|
+
##### Nesting
|
115
|
+
As with sideloading serializers, by default, only the ids of related objects will be serialized. For serializers using a 'nesting' approach, these relationships and their ids will be inlined using their _id / _ids attribute name suffix.
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
```javascript
|
120
|
+
{
|
121
|
+
"users": [{
|
122
|
+
"id": 1,
|
123
|
+
"first_name": "John",
|
124
|
+
"last_name": "Smith",
|
125
|
+
"profile_id": 2,
|
126
|
+
"post_ids": [3, 4]
|
127
|
+
}]
|
128
|
+
}
|
129
|
+
```
|
130
|
+
|
131
|
+
One advantage to this approach is thttp_api_tools it's always clear whttp_api_tools relationships exist for a resource, even if you don't
|
132
|
+
include the resources themselves in the response.
|
133
|
+
|
134
|
+
##### Serializing related resources via includes
|
135
|
+
Often it will be desirable to load related data to save on requests. This can be done when creating the top level serializer using the same approach ActiveRecord uses for including relationships in queries.
|
136
|
+
|
137
|
+
`UserSerializer.new(user).includes(:profile, { posts: [:comments] })`
|
138
|
+
|
139
|
+
Which produces the following json when sideloaded:
|
140
|
+
|
141
|
+
```javascript
|
142
|
+
{
|
143
|
+
"users": [{
|
144
|
+
"id": 1,
|
145
|
+
"first_name": "John",
|
146
|
+
"last_name": "Smith",
|
147
|
+
"links": {
|
148
|
+
"profile": 2,
|
149
|
+
"posts": [3, 4]
|
150
|
+
}
|
151
|
+
}],
|
152
|
+
"linked": {
|
153
|
+
"profiles": [
|
154
|
+
{
|
155
|
+
"id": 2,
|
156
|
+
//...
|
157
|
+
}
|
158
|
+
],
|
159
|
+
|
160
|
+
posts: [
|
161
|
+
{
|
162
|
+
"id": 3,
|
163
|
+
"links": {
|
164
|
+
"user": 1,
|
165
|
+
"comments": [5]
|
166
|
+
}
|
167
|
+
//...
|
168
|
+
},
|
169
|
+
{
|
170
|
+
"id": 4,
|
171
|
+
"links": {
|
172
|
+
"user": 1,
|
173
|
+
"comments": []
|
174
|
+
}
|
175
|
+
//...
|
176
|
+
}
|
177
|
+
],
|
178
|
+
"comments": [
|
179
|
+
"id": 5,
|
180
|
+
"links": {
|
181
|
+
"post": 3
|
182
|
+
}
|
183
|
+
//...
|
184
|
+
]
|
185
|
+
}
|
186
|
+
|
187
|
+
}
|
188
|
+
```
|
189
|
+
|
190
|
+
and the following when nested:
|
191
|
+
|
192
|
+
```javascript
|
193
|
+
{
|
194
|
+
"users": [{
|
195
|
+
"id": 1,
|
196
|
+
"first_name": "John",
|
197
|
+
"last_name": "Smith",
|
198
|
+
"profile": {
|
199
|
+
"id": 2,
|
200
|
+
},
|
201
|
+
posts: [
|
202
|
+
{
|
203
|
+
"id": 3,
|
204
|
+
"user_id": 1
|
205
|
+
"comments": [
|
206
|
+
{
|
207
|
+
"id": 5,
|
208
|
+
"post_id": 3
|
209
|
+
}
|
210
|
+
]
|
211
|
+
},
|
212
|
+
{
|
213
|
+
"id": 4,
|
214
|
+
"user_id": 1
|
215
|
+
"comments": []
|
216
|
+
}
|
217
|
+
]
|
218
|
+
}]
|
219
|
+
}
|
220
|
+
```
|
221
|
+
|
222
|
+
One benefit to sideloading over nesting resources is thttp_api_tools if the same resource is referenced multiple times, it only needs to be serialized once. Depending on your data, this may or may not be significant.
|
223
|
+
|
224
|
+
##### Including related resources via the url
|
225
|
+
It's possible to determine whttp_api_tools resources to include by providing a query string parameter:
|
226
|
+
|
227
|
+
`http://example.com/users/1?include?comments,posts.comments`
|
228
|
+
|
229
|
+
This can be parsed using:
|
230
|
+
|
231
|
+
`relation_includes = HttpApiTools::RelationIncludes.from_params(params)`
|
232
|
+
|
233
|
+
and splat into the serializer includes:
|
234
|
+
|
235
|
+
`UserSerializer.new(user).includes(*relation_includes)`
|
236
|
+
|
237
|
+
and/or active record queries:
|
238
|
+
|
239
|
+
`User.find(params[:id]).includes(*relation_includes.for_query(UserSerializer))`
|
240
|
+
|
241
|
+
When providing the includes for an active record query, we actually want a deeper set of includes in order to account for the ids fetched for has_many relationships. If we passed the same set of includes to the query as we pass to the serializer, we'd end up with n+1 queries when fetching the ids for the has_many relationships.
|
242
|
+
|
243
|
+
Calling `relation_includes.for_query(UserSerializer)` will figure out the minimum set of includes thttp_api_tools are required based on the following:
|
244
|
+
|
245
|
+
* The models and their relationships
|
246
|
+
* The relationships actually being serialized
|
247
|
+
|
248
|
+
**** Note thttp_api_tools this particular API is pretty rough at the moment and likely to change once we find a nicer way of describing this feature.
|
249
|
+
|
250
|
+
##### Restricting whttp_api_tools is included
|
251
|
+
Once you expose whttp_api_tools can be included as a query string parameter you risk exposing too much information or poorly considered api calls thttp_api_tools fetch too much. This can be countered by defining whttp_api_tools is `includable` for each serializer when it's being used as the root serializer for a json response.
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
class UserSerializer
|
255
|
+
|
256
|
+
include HttpApiTools::Nesting::JsonSerializer
|
257
|
+
|
258
|
+
serializes(User)
|
259
|
+
|
260
|
+
attributes :id, :first_name, :last_name, :full_name
|
261
|
+
|
262
|
+
has_many :posts
|
263
|
+
has_many :comments
|
264
|
+
|
265
|
+
includable(:profile, {:posts, [:comments]})
|
266
|
+
|
267
|
+
end
|
268
|
+
```
|
269
|
+
|
270
|
+
This will ensure thttp_api_tools regardless of whttp_api_tools is declared in the `include` param, no more than the allowable includes are ever returned.
|
271
|
+
|
272
|
+
To help in documenting whttp_api_tools is includable, both the includable and included relations are returned in the meta data of the response.
|
273
|
+
|
274
|
+
```javascript
|
275
|
+
"meta": {
|
276
|
+
"type": "user",
|
277
|
+
"root_key": "users",
|
278
|
+
"includable": "profile,posts,posts.comments"
|
279
|
+
"included": "posts"
|
280
|
+
}
|
281
|
+
```
|
282
|
+
|
283
|
+
#### Meta data
|
284
|
+
Every request will also contain a special meta attribute which could be augmented with various additional pieces
|
285
|
+
of meta-data. At this point, it will always return the `type` and `root_key` for the current request. Eg:
|
286
|
+
|
287
|
+
```javascript
|
288
|
+
{
|
289
|
+
"meta": {
|
290
|
+
"type": "user",
|
291
|
+
"root_key": "users"
|
292
|
+
},
|
293
|
+
"users": [{
|
294
|
+
"id": 1,
|
295
|
+
"first_name": "John",
|
296
|
+
"last_name": "Smith",
|
297
|
+
"profile_id": 2,
|
298
|
+
"post_ids": [3, 4]
|
299
|
+
}]
|
300
|
+
}
|
301
|
+
```
|
302
|
+
|
303
|
+
Notice thttp_api_tools the root is an array and the root_key a plural. This is the case regardless of whether a single resource
|
304
|
+
is being represented or a collection of resources. This is in line with the json-api spec and generally simplifies both serialization and deserialization.
|
305
|
+
|
306
|
+
##### Adding Metadata
|
307
|
+
It might be desirable to add extra metadata to the serialized response. For example, adding information such as limit, offset, whttp_api_tools includes are valid etc can be helpful to a client.
|
308
|
+
|
309
|
+
`UserSerializer.new(user).meta(limit: 10, offset: 0)`
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
### Deserialization
|
314
|
+
The `HttpApiTools::JsonDeserializer` expects json in the format thttp_api_tools the serializer has created making it easy to create matching rest apis and clients with little work needing to be done at each end. Currently only sideloaded json can be deserialized. Nested deserializers are coming.
|
315
|
+
|
316
|
+
`HttpApiTools::JsonDeserializer.new(json).deserialize`
|
317
|
+
|
318
|
+
This will iterate over the json, using the attribute names to match types to models in the client app. As long as models exist with names thttp_api_tools match the keys in the json, a complete graph of objects will be created upon deserialization, complete with two way relationships when they exist.
|
319
|
+
|
320
|
+
In the previous example, the following model classes would be expected:
|
321
|
+
|
322
|
+
* User
|
323
|
+
* Post
|
324
|
+
* Comment
|
325
|
+
|
326
|
+
#### Deserializer Mappings
|
327
|
+
|
328
|
+
At times, the name of an object's key may deviate from it's type and can't be deserialized by convention alone.
|
329
|
+
|
330
|
+
```javascript
|
331
|
+
{
|
332
|
+
"users": [{
|
333
|
+
"id": 1,
|
334
|
+
"first_name": "John",
|
335
|
+
"last_name": "Smith",
|
336
|
+
"links": {
|
337
|
+
"posts": [3]
|
338
|
+
}
|
339
|
+
}],
|
340
|
+
"linked": {
|
341
|
+
posts: [
|
342
|
+
{
|
343
|
+
"id": 3,
|
344
|
+
"links": {
|
345
|
+
"author": 1
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
}
|
350
|
+
```
|
351
|
+
|
352
|
+
In this example, the `user` is the `author` of the `post`. It is impossible to infer from the data thttp_api_tools an `author` attribute key should map to a `User` type so we need to give it a helping hand. This can be done once per type by creating a `JsonDeserializerMapping` class. Like with serializers, deserializer mappings are convention based, using the model class name as a prefix.
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
class PostDeserializerMapping
|
356
|
+
|
357
|
+
include HttpApiTools::JsonDeserializerMapping
|
358
|
+
|
359
|
+
map :author, User
|
360
|
+
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
Whenever we're deserializing a `post`, the `author` attribute will always be deserialized to an instance of a `User`.
|
365
|
+
|
366
|
+
This can also be applied against collections:
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
class CompanyDeserializerMapping
|
370
|
+
|
371
|
+
include HttpApiTools::JsonDeserializerMapping
|
372
|
+
|
373
|
+
map :employees, Person
|
374
|
+
|
375
|
+
end
|
376
|
+
```
|
377
|
+
|
378
|
+
### Models
|
379
|
+
Client models have some basic requirements thttp_api_tools are catered to such as attribute definition, default values and type tranforms.
|
380
|
+
|
381
|
+
For example:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
class User
|
385
|
+
|
386
|
+
include HttpApiTools::Model::Attributes
|
387
|
+
include HttpApiTools::Model::ActsLikeActiveModel
|
388
|
+
|
389
|
+
attribute :id
|
390
|
+
attribute :first_name
|
391
|
+
attribute :last_name
|
392
|
+
attribute :created_at: type: :date_time
|
393
|
+
attribute :posts, default: []
|
394
|
+
attribute :profile
|
395
|
+
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
This will define a User class with attr_accessors for all attributes defined. The initialize method will accept a hash of values which will be passed through type transformers when configured and have defaults applied when no value is passed in for a key.
|
400
|
+
|
401
|
+
Currently there is a single registered type transform for date_time transforms. This expects an iso8601 date format as a string which will be transformed into a ruby DateTime.
|
402
|
+
|
403
|
+
#### Registering custom type transformers.
|
404
|
+
|
405
|
+
Type transformers expect the following two-way interface:
|
406
|
+
|
407
|
+
```ruby
|
408
|
+
class MoneyTranformer
|
409
|
+
|
410
|
+
def self.from_raw(value)
|
411
|
+
Money.new(value)
|
412
|
+
end
|
413
|
+
|
414
|
+
def self.to_raw(money)
|
415
|
+
money.to_s
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
419
|
+
```
|
420
|
+
|
421
|
+
Transformers should then be registered against a type key:
|
422
|
+
|
423
|
+
|
424
|
+
```ruby
|
425
|
+
HttpApiTools::Transformers::Registry.instance.register(:money, MoneyTransformer)
|
426
|
+
```
|
427
|
+
|
428
|
+
Now you can define an attribute as a `money` type:
|
429
|
+
|
430
|
+
```ruby
|
431
|
+
class Account
|
432
|
+
|
433
|
+
include HttpApiTools::Model::Attributes
|
434
|
+
|
435
|
+
attribute :balance: type: :money
|
436
|
+
|
437
|
+
end
|
438
|
+
```
|
439
|
+
|
440
|
+
#### Read only attributes
|
441
|
+
Sometimes it's useful to define a field as readonly. The intent being thttp_api_tools we prevent changing an attribute value thttp_api_tools shouldn't be changed or prevent a value from being serialized and sent in the payload thttp_api_tools the server won't accept.
|
442
|
+
|
443
|
+
In the previous example, it might be better to set the `created_at` field as readonly:
|
444
|
+
|
445
|
+
```ruby
|
446
|
+
class User
|
447
|
+
|
448
|
+
include HttpApiTools::Model::Attributes
|
449
|
+
include HttpApiTools::Model::ActsLikeActiveModel
|
450
|
+
|
451
|
+
attribute :id
|
452
|
+
attribute :first_name
|
453
|
+
attribute :last_name
|
454
|
+
attribute :created_at: type: :date_time, read_only: true
|
455
|
+
attribute :posts, default: []
|
456
|
+
attribute :profile
|
457
|
+
|
458
|
+
end
|
459
|
+
```
|
460
|
+
|
461
|
+
### Polymorphism
|
462
|
+
At this point, polymorphic relationships are not catered for but they can be when the need arises.
|
463
|
+
|
464
|
+
|
465
|
+
## Contributing
|
466
|
+
|
467
|
+
### A note on performance
|
468
|
+
Performance is critial for this gem so any changes must be made with this in mind. There is a basic performance
|
469
|
+
spec for serialization thttp_api_tools dumps some timings and creates a profile report in `reports/profile_report.html`.
|
470
|
+
|
471
|
+
Until we have a more robust way of tracking performance over time, please do some before and after tests against this when you make changes. Even small things have been found to introduce big performance issues.
|
472
|
+
|
473
|
+
|
474
|
+
## To Do
|
475
|
+
* Deserializer for nested json
|
476
|
+
|
477
|
+
|
478
|
+
|
479
|
+
|