blueprinter-rb 1.0.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/blueprinter/configuration.rb +10 -1
- data/lib/blueprinter/helpers/type_helpers.rb +3 -5
- data/lib/blueprinter/version.rb +1 -1
- data/lib/blueprinter/view_collection.rb +6 -8
- metadata +15 -20
- data/CHANGELOG.md +0 -167
- data/README.md +0 -1050
- data/Rakefile +0 -30
data/README.md
DELETED
@@ -1,1050 +0,0 @@
|
|
1
|
-
<img src="blueprinter_logo.svg" width="25%">
|
2
|
-
|
3
|
-
# Blueprinter
|
4
|
-
Blueprinter is a JSON Object Presenter for Ruby that takes business objects and breaks them down into simple hashes and serializes them to JSON. It can be used in Rails in place of other serializers (like JBuilder or ActiveModelSerializers). It is designed to be simple, direct, and performant.
|
5
|
-
|
6
|
-
It heavily relies on the idea of `views` which, similar to Rails views, are ways of predefining output for data in different contexts.
|
7
|
-
|
8
|
-
## Documentation
|
9
|
-
Docs can be found [here](http://www.rubydoc.info/gems/blueprinter).
|
10
|
-
|
11
|
-
## Usage
|
12
|
-
<details open>
|
13
|
-
<summary>Basic</summary>
|
14
|
-
|
15
|
-
---
|
16
|
-
|
17
|
-
If you have an object you would like serialized, simply create a blueprint. Say, for example, you have a User record with the following attributes `[:uuid, :email, :first_name, :last_name, :password, :address]`.
|
18
|
-
|
19
|
-
You may define a simple blueprint like so:
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
class UserBlueprint < Blueprinter::Base
|
23
|
-
identifier :uuid
|
24
|
-
|
25
|
-
fields :first_name, :last_name, :email
|
26
|
-
end
|
27
|
-
```
|
28
|
-
|
29
|
-
and then, in your code:
|
30
|
-
```ruby
|
31
|
-
puts UserBlueprint.render(user) # Output is a JSON string
|
32
|
-
```
|
33
|
-
|
34
|
-
And the output would look like:
|
35
|
-
|
36
|
-
```json
|
37
|
-
{
|
38
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
39
|
-
"email": "john.doe@some.fake.email.domain",
|
40
|
-
"first_name": "John",
|
41
|
-
"last_name": "Doe"
|
42
|
-
}
|
43
|
-
```
|
44
|
-
|
45
|
-
---
|
46
|
-
</details>
|
47
|
-
|
48
|
-
|
49
|
-
<details>
|
50
|
-
<summary>Collections</summary>
|
51
|
-
|
52
|
-
---
|
53
|
-
|
54
|
-
You can also pass a collection object or an array to the render method.
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
puts UserBlueprint.render(User.all)
|
58
|
-
```
|
59
|
-
|
60
|
-
This will result in JSON that looks something like this:
|
61
|
-
|
62
|
-
```json
|
63
|
-
[
|
64
|
-
{
|
65
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
66
|
-
"email": "john.doe@some.fake.email.domain",
|
67
|
-
"first_name": "John",
|
68
|
-
"last_name": "Doe"
|
69
|
-
},
|
70
|
-
{
|
71
|
-
"uuid": "733f0758-8f21-4719-875f-743af262c3ec",
|
72
|
-
"email": "john.doe.2@some.fake.email.domain",
|
73
|
-
"first_name": "John",
|
74
|
-
"last_name": "Doe 2"
|
75
|
-
}
|
76
|
-
]
|
77
|
-
```
|
78
|
-
|
79
|
-
---
|
80
|
-
</details>
|
81
|
-
|
82
|
-
|
83
|
-
<details>
|
84
|
-
<summary>Renaming</summary>
|
85
|
-
|
86
|
-
---
|
87
|
-
|
88
|
-
You can rename the resulting JSON keys in both fields and associations by using the `name` option.
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
class UserBlueprint < Blueprinter::Base
|
92
|
-
identifier :uuid
|
93
|
-
|
94
|
-
field :email, name: :login
|
95
|
-
|
96
|
-
association :user_projects, name: :projects
|
97
|
-
end
|
98
|
-
```
|
99
|
-
|
100
|
-
This will result in JSON that looks something like this:
|
101
|
-
|
102
|
-
```json
|
103
|
-
{
|
104
|
-
"uuid": "92a5c732-2874-41e4-98fc-4123cd6cfa86",
|
105
|
-
"login": "my@email.com",
|
106
|
-
"projects": []
|
107
|
-
}
|
108
|
-
```
|
109
|
-
|
110
|
-
---
|
111
|
-
</details>
|
112
|
-
|
113
|
-
|
114
|
-
<details>
|
115
|
-
<summary>Views</summary>
|
116
|
-
|
117
|
-
---
|
118
|
-
|
119
|
-
You may define different outputs by utilizing views:
|
120
|
-
```ruby
|
121
|
-
class UserBlueprint < Blueprinter::Base
|
122
|
-
identifier :uuid
|
123
|
-
field :email, name: :login
|
124
|
-
|
125
|
-
view :normal do
|
126
|
-
fields :first_name, :last_name
|
127
|
-
end
|
128
|
-
|
129
|
-
view :extended do
|
130
|
-
include_view :normal
|
131
|
-
field :address
|
132
|
-
association :projects
|
133
|
-
end
|
134
|
-
end
|
135
|
-
```
|
136
|
-
A view can include fields from another view by utilizing `include_view` and `include_views`.
|
137
|
-
|
138
|
-
Usage:
|
139
|
-
```ruby
|
140
|
-
puts UserBlueprint.render(user, view: :extended)
|
141
|
-
```
|
142
|
-
|
143
|
-
Output:
|
144
|
-
```json
|
145
|
-
{
|
146
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
147
|
-
"address": "123 Fake St.",
|
148
|
-
"first_name": "John",
|
149
|
-
"last_name": "Doe",
|
150
|
-
"login": "john.doe@some.fake.email.domain"
|
151
|
-
}
|
152
|
-
```
|
153
|
-
|
154
|
-
---
|
155
|
-
</details>
|
156
|
-
|
157
|
-
|
158
|
-
<details>
|
159
|
-
<summary>Identifiers</summary>
|
160
|
-
|
161
|
-
---
|
162
|
-
|
163
|
-
`identifier`s are used to specify a field or method name used as an identifier. Usually, this is something like `:id`.
|
164
|
-
|
165
|
-
Example:
|
166
|
-
```rb
|
167
|
-
class UserBlueprint < Blueprinter::Base
|
168
|
-
identifier :uuid
|
169
|
-
end
|
170
|
-
```
|
171
|
-
|
172
|
-
Blueprinter `identifier`s have a few properties that set them apart from `field`s.
|
173
|
-
|
174
|
-
1. Identifiers are **always** rendered and considered their own view (the `:identifier` view).
|
175
|
-
2. When rendering, identifier fields are always sorted first, before other fields.
|
176
|
-
|
177
|
-
If either of the above two developer conveniences are not desired, you can simply create your identifier fields as regular `field`s.
|
178
|
-
|
179
|
-
---
|
180
|
-
|
181
|
-
</details>
|
182
|
-
|
183
|
-
|
184
|
-
<details>
|
185
|
-
<summary>Root</summary>
|
186
|
-
|
187
|
-
---
|
188
|
-
|
189
|
-
You can also optionally pass in a root key to wrap your resulting json in:
|
190
|
-
```ruby
|
191
|
-
class UserBlueprint < Blueprinter::Base
|
192
|
-
identifier :uuid
|
193
|
-
field :email, name: :login
|
194
|
-
|
195
|
-
view :normal do
|
196
|
-
fields :first_name, :last_name
|
197
|
-
end
|
198
|
-
end
|
199
|
-
```
|
200
|
-
|
201
|
-
Usage:
|
202
|
-
```ruby
|
203
|
-
puts UserBlueprint.render(user, view: :normal, root: :user)
|
204
|
-
```
|
205
|
-
|
206
|
-
Output:
|
207
|
-
```json
|
208
|
-
{
|
209
|
-
"user": {
|
210
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
211
|
-
"first_name": "John",
|
212
|
-
"last_name": "Doe",
|
213
|
-
"login": "john.doe@some.fake.email.domain"
|
214
|
-
}
|
215
|
-
}
|
216
|
-
```
|
217
|
-
|
218
|
-
---
|
219
|
-
</details>
|
220
|
-
|
221
|
-
|
222
|
-
<details>
|
223
|
-
<summary>Meta Attributes</summary>
|
224
|
-
|
225
|
-
---
|
226
|
-
|
227
|
-
You can additionally add meta-data to the json as well:
|
228
|
-
```ruby
|
229
|
-
class UserBlueprint < Blueprinter::Base
|
230
|
-
identifier :uuid
|
231
|
-
field :email, name: :login
|
232
|
-
|
233
|
-
view :normal do
|
234
|
-
fields :first_name, :last_name
|
235
|
-
end
|
236
|
-
end
|
237
|
-
```
|
238
|
-
|
239
|
-
Usage:
|
240
|
-
```ruby
|
241
|
-
json = UserBlueprint.render(user, view: :normal, root: :user, meta: {links: [
|
242
|
-
'https://app.mydomain.com',
|
243
|
-
'https://alternate.mydomain.com'
|
244
|
-
]})
|
245
|
-
puts json
|
246
|
-
```
|
247
|
-
|
248
|
-
Output:
|
249
|
-
```json
|
250
|
-
{
|
251
|
-
"user": {
|
252
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
253
|
-
"first_name": "John",
|
254
|
-
"last_name": "Doe",
|
255
|
-
"login": "john.doe@some.fake.email.domain"
|
256
|
-
},
|
257
|
-
"meta": {
|
258
|
-
"links": [
|
259
|
-
"https://app.mydomain.com",
|
260
|
-
"https://alternate.mydomain.com"
|
261
|
-
]
|
262
|
-
}
|
263
|
-
}
|
264
|
-
```
|
265
|
-
_NOTE:_ For meta attributes, a [root](#root) is mandatory.
|
266
|
-
|
267
|
-
---
|
268
|
-
</details>
|
269
|
-
|
270
|
-
|
271
|
-
<details>
|
272
|
-
<summary>Exclude Fields</summary>
|
273
|
-
|
274
|
-
---
|
275
|
-
|
276
|
-
You can specifically choose to exclude certain fields for specific views
|
277
|
-
```ruby
|
278
|
-
class UserBlueprint < Blueprinter::Base
|
279
|
-
identifier :uuid
|
280
|
-
field :email, name: :login
|
281
|
-
|
282
|
-
view :normal do
|
283
|
-
fields :first_name, :last_name
|
284
|
-
end
|
285
|
-
|
286
|
-
view :extended do
|
287
|
-
include_view :normal
|
288
|
-
field :address
|
289
|
-
exclude :last_name
|
290
|
-
end
|
291
|
-
end
|
292
|
-
```
|
293
|
-
|
294
|
-
Usage:
|
295
|
-
```ruby
|
296
|
-
puts UserBlueprint.render(user, view: :extended)
|
297
|
-
```
|
298
|
-
|
299
|
-
Output:
|
300
|
-
```json
|
301
|
-
{
|
302
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
303
|
-
"address": "123 Fake St.",
|
304
|
-
"first_name": "John",
|
305
|
-
"login": "john.doe@some.fake.email.domain"
|
306
|
-
}
|
307
|
-
```
|
308
|
-
|
309
|
-
Use `excludes` to exclude multiple fields at once inline.
|
310
|
-
|
311
|
-
```ruby
|
312
|
-
class UserBlueprint < Blueprinter::Base
|
313
|
-
identifier :uuid
|
314
|
-
field :email, name: :login
|
315
|
-
|
316
|
-
view :normal do
|
317
|
-
fields :age, :first_name, :last_name,
|
318
|
-
end
|
319
|
-
|
320
|
-
view :extended do
|
321
|
-
include_view :normal
|
322
|
-
field :address
|
323
|
-
excludes :age, :last_name
|
324
|
-
end
|
325
|
-
end
|
326
|
-
```
|
327
|
-
|
328
|
-
---
|
329
|
-
</details>
|
330
|
-
|
331
|
-
|
332
|
-
<details>
|
333
|
-
<summary>Associations</summary>
|
334
|
-
|
335
|
-
---
|
336
|
-
|
337
|
-
You may include associated objects. Say for example, a user has projects:
|
338
|
-
```ruby
|
339
|
-
class ProjectBlueprint < Blueprinter::Base
|
340
|
-
identifier :uuid
|
341
|
-
field :name
|
342
|
-
end
|
343
|
-
|
344
|
-
class UserBlueprint < Blueprinter::Base
|
345
|
-
identifier :uuid
|
346
|
-
field :email, name: :login
|
347
|
-
|
348
|
-
view :normal do
|
349
|
-
fields :first_name, :last_name
|
350
|
-
association :projects, blueprint: ProjectBlueprint
|
351
|
-
end
|
352
|
-
end
|
353
|
-
```
|
354
|
-
|
355
|
-
Usage:
|
356
|
-
```ruby
|
357
|
-
puts UserBlueprint.render(user, view: :normal)
|
358
|
-
```
|
359
|
-
|
360
|
-
Output:
|
361
|
-
```json
|
362
|
-
{
|
363
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
364
|
-
"first_name": "John",
|
365
|
-
"last_name": "Doe",
|
366
|
-
"login": "john.doe@some.fake.email.domain",
|
367
|
-
"projects": [
|
368
|
-
{
|
369
|
-
"uuid": "dca94051-4195-42bc-a9aa-eb99f7723c82",
|
370
|
-
"name": "Beach Cleanup"
|
371
|
-
},
|
372
|
-
{
|
373
|
-
"uuid": "eb881bb5-9a51-4d27-8a29-b264c30e6160",
|
374
|
-
"name": "Storefront Revamp"
|
375
|
-
}
|
376
|
-
]
|
377
|
-
}
|
378
|
-
```
|
379
|
-
|
380
|
-
It is also possible to pass options from one Blueprint to another via an association.
|
381
|
-
For example:
|
382
|
-
```ruby
|
383
|
-
class VehicleBlueprint < Blueprinter::Base
|
384
|
-
identifier :uuid
|
385
|
-
field :full_name do |vehicle, options|
|
386
|
-
"#{vehicle.model} #{options[:trim]}"
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
class DriverBlueprint < Blueprinter::Base
|
391
|
-
identifier :uuid
|
392
|
-
|
393
|
-
view :normal do
|
394
|
-
fields :first_name, :last_name
|
395
|
-
association :vehicles, blueprint: VehicleBlueprint, options: { trim: 'LX' }
|
396
|
-
end
|
397
|
-
end
|
398
|
-
```
|
399
|
-
|
400
|
-
---
|
401
|
-
</details>
|
402
|
-
|
403
|
-
|
404
|
-
<details>
|
405
|
-
<summary>Default Association/Field Option</summary>
|
406
|
-
|
407
|
-
---
|
408
|
-
|
409
|
-
By default, an association or field that evaluates to `nil` is serialized as `nil`. A default serialized value can be specified as an option on the association or field for cases when the association/field could potentially evaluate to `nil`. You can also specify a global `field_default` or `association_default` in the Blueprinter config which will be used for all fields/associations that evaluate to nil.
|
410
|
-
|
411
|
-
#### Global Config Setting
|
412
|
-
```ruby
|
413
|
-
Blueprinter.configure do |config|
|
414
|
-
config.field_default = "N/A"
|
415
|
-
config.association_default = {}
|
416
|
-
end
|
417
|
-
```
|
418
|
-
|
419
|
-
#### Field-level/Association-level Setting
|
420
|
-
```ruby
|
421
|
-
class UserBlueprint < Blueprinter::Base
|
422
|
-
identifier :uuid
|
423
|
-
|
424
|
-
view :normal do
|
425
|
-
field :first_name, default: "N/A"
|
426
|
-
association :company, blueprint: CompanyBlueprint, default: {}
|
427
|
-
end
|
428
|
-
end
|
429
|
-
```
|
430
|
-
|
431
|
-
---
|
432
|
-
</details>
|
433
|
-
|
434
|
-
|
435
|
-
<details>
|
436
|
-
<summary>default_if</summary>
|
437
|
-
|
438
|
-
---
|
439
|
-
|
440
|
-
Sometimes, you may want certain "empty" values to pass through to the default value.
|
441
|
-
Blueprinter provides the ability to treat the following empty types as the default value (or `nil` if no default provided).
|
442
|
-
|
443
|
-
#### Blueprinter::EMPTY_COLLECTION
|
444
|
-
An empty array or empty active record collection.
|
445
|
-
|
446
|
-
#### Blueprinter::EMPTY_HASH
|
447
|
-
An empty hash.
|
448
|
-
|
449
|
-
#### Blueprinter::EMPTY_STRING
|
450
|
-
An empty string or symbol.
|
451
|
-
|
452
|
-
#### Field-level/Association-level Setting
|
453
|
-
```ruby
|
454
|
-
class UserBlueprint < Blueprinter::Base
|
455
|
-
identifier :uuid
|
456
|
-
|
457
|
-
view :normal do
|
458
|
-
# If first_name is an empty string, it will become "N/A"
|
459
|
-
field :first_name, default_if: Blueprinter::EmptyString, default: "N/A"
|
460
|
-
# If the projects association collection is empty, it will become nil
|
461
|
-
association :projects, blueprint: ProjectBlueprint, default_if: Blueprinter::EmptyCollection
|
462
|
-
end
|
463
|
-
end
|
464
|
-
```
|
465
|
-
|
466
|
-
---
|
467
|
-
</details>
|
468
|
-
|
469
|
-
|
470
|
-
<details>
|
471
|
-
<summary>Supporting Dynamic Blueprints For Associations</summary>
|
472
|
-
|
473
|
-
---
|
474
|
-
|
475
|
-
When defining an association, we can dynamically evaluate the blueprint. This comes in handy when adding polymorphic associations, by allowing reuse of existing blueprints.
|
476
|
-
```ruby
|
477
|
-
class Task < ActiveRecord::Base
|
478
|
-
belongs_to :taskable, polymorphic: true
|
479
|
-
end
|
480
|
-
|
481
|
-
class Project < ActiveRecord::Base
|
482
|
-
has_many :tasks, as: :taskable
|
483
|
-
|
484
|
-
def blueprint
|
485
|
-
ProjectBlueprint
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
class TaskBlueprint < Blueprinter::Base
|
490
|
-
identifier :uuid
|
491
|
-
|
492
|
-
view :normal do
|
493
|
-
field :title, default: "N/A"
|
494
|
-
association :taskable, blueprint: ->(taskable) {taskable.blueprint}, default: {}
|
495
|
-
end
|
496
|
-
end
|
497
|
-
```
|
498
|
-
_NOTE:_ `taskable.blueprint` should return a valid Blueprint class. Currently, `has_many` is not supported because of the very nature of polymorphic associations.
|
499
|
-
|
500
|
-
---
|
501
|
-
</details>
|
502
|
-
|
503
|
-
|
504
|
-
<details>
|
505
|
-
<summary>Defining A Field Directly In The Blueprint</summary>
|
506
|
-
|
507
|
-
---
|
508
|
-
|
509
|
-
You can define a field directly in the Blueprint by passing it a block. This is especially useful if the object does not already have such an attribute or method defined, and you want to define it specifically for use with the Blueprint. This is done by passing `field` a block. The block also yields the object and any options that were passed from `render`. For example:
|
510
|
-
|
511
|
-
```ruby
|
512
|
-
class UserBlueprint < Blueprinter::Base
|
513
|
-
identifier :uuid
|
514
|
-
field :full_name do |user, options|
|
515
|
-
"#{options[:title_prefix]} #{user.first_name} #{user.last_name}"
|
516
|
-
end
|
517
|
-
end
|
518
|
-
```
|
519
|
-
|
520
|
-
Usage:
|
521
|
-
|
522
|
-
```ruby
|
523
|
-
puts UserBlueprint.render(user, title_prefix: "Mr")
|
524
|
-
```
|
525
|
-
|
526
|
-
Output:
|
527
|
-
|
528
|
-
```json
|
529
|
-
{
|
530
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
531
|
-
"full_name": "Mr John Doe"
|
532
|
-
}
|
533
|
-
```
|
534
|
-
|
535
|
-
---
|
536
|
-
</details>
|
537
|
-
|
538
|
-
|
539
|
-
<details>
|
540
|
-
<summary>Defining An Identifier Directly In The Blueprint</summary>
|
541
|
-
|
542
|
-
---
|
543
|
-
|
544
|
-
You can also pass a block to an identifier:
|
545
|
-
|
546
|
-
```ruby
|
547
|
-
class UserBlueprint < Blueprinter::Base
|
548
|
-
identifier :uuid do |user, options|
|
549
|
-
options[:current_user].anonymize(user.uuid)
|
550
|
-
end
|
551
|
-
end
|
552
|
-
```
|
553
|
-
|
554
|
-
Usage:
|
555
|
-
|
556
|
-
```ruby
|
557
|
-
puts UserBlueprint.render(user, current_user: current_user)
|
558
|
-
```
|
559
|
-
|
560
|
-
Output:
|
561
|
-
|
562
|
-
```json
|
563
|
-
{
|
564
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
565
|
-
}
|
566
|
-
```
|
567
|
-
|
568
|
-
---
|
569
|
-
</details>
|
570
|
-
|
571
|
-
|
572
|
-
<details>
|
573
|
-
<summary>Defining An Association Directly In The Blueprint</summary>
|
574
|
-
|
575
|
-
---
|
576
|
-
|
577
|
-
You can also pass a block to an association:
|
578
|
-
|
579
|
-
```ruby
|
580
|
-
class ProjectBlueprint < Blueprinter::Base
|
581
|
-
identifier :uuid
|
582
|
-
field :name
|
583
|
-
end
|
584
|
-
|
585
|
-
class UserBlueprint < Blueprinter::Base
|
586
|
-
identifier :uuid
|
587
|
-
|
588
|
-
association :projects, blueprint: ProjectBlueprint do |user, options|
|
589
|
-
user.projects + options[:draft_projects]
|
590
|
-
end
|
591
|
-
end
|
592
|
-
```
|
593
|
-
|
594
|
-
Usage:
|
595
|
-
|
596
|
-
```ruby
|
597
|
-
puts UserBlueprint.render(user, draft_projects: Project.where(draft: true))
|
598
|
-
```
|
599
|
-
|
600
|
-
Output:
|
601
|
-
|
602
|
-
```json
|
603
|
-
{
|
604
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
605
|
-
"projects": [
|
606
|
-
{"uuid": "b426a1e6-ac41-45ab-bfef-970b9a0b4289", "name": "query-console"},
|
607
|
-
{"uuid": "5bd84d6c-4fd2-4e36-ae31-c137e39be542", "name": "blueprinter"},
|
608
|
-
{"uuid": "785f5cd4-7d8d-4779-a6dd-ec5eab440eff", "name": "uncontrollable"}
|
609
|
-
]
|
610
|
-
}
|
611
|
-
```
|
612
|
-
|
613
|
-
---
|
614
|
-
</details>
|
615
|
-
|
616
|
-
|
617
|
-
<details>
|
618
|
-
<summary>Passing Additional Properties To #render</summary>
|
619
|
-
|
620
|
-
---
|
621
|
-
|
622
|
-
`render` takes an options hash which you can pass additional properties, allowing you to utilize those additional properties in the `field` block. For example:
|
623
|
-
|
624
|
-
```ruby
|
625
|
-
class UserBlueprint < Blueprinter::Base
|
626
|
-
identifier :uuid
|
627
|
-
field(:company_name) do |_user, options|
|
628
|
-
options[:company].name
|
629
|
-
end
|
630
|
-
end
|
631
|
-
```
|
632
|
-
|
633
|
-
Usage:
|
634
|
-
|
635
|
-
```ruby
|
636
|
-
puts UserBlueprint.render(user, company: company)
|
637
|
-
```
|
638
|
-
|
639
|
-
Output:
|
640
|
-
|
641
|
-
```json
|
642
|
-
{
|
643
|
-
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
644
|
-
"company_name": "My Company LLC"
|
645
|
-
}
|
646
|
-
```
|
647
|
-
|
648
|
-
---
|
649
|
-
</details>
|
650
|
-
|
651
|
-
|
652
|
-
<details>
|
653
|
-
<summary>Conditional Fields</summary>
|
654
|
-
|
655
|
-
---
|
656
|
-
|
657
|
-
Both the `field` and the global Blueprinter Configuration supports `:if` and `:unless` options that can be used to serialize fields conditionally.
|
658
|
-
|
659
|
-
#### Global Config Setting
|
660
|
-
```ruby
|
661
|
-
Blueprinter.configure do |config|
|
662
|
-
config.if = ->(field_name, obj, _options) { !obj[field_name].nil? }
|
663
|
-
config.unless = ->(field_name, obj, _options) { obj[field_name].nil? }
|
664
|
-
end
|
665
|
-
```
|
666
|
-
|
667
|
-
#### Field-level Setting
|
668
|
-
```ruby
|
669
|
-
class UserBlueprint < Blueprinter::Base
|
670
|
-
identifier :uuid
|
671
|
-
field :last_name, if: ->(_field_name, user, options) { user.first_name != options[:first_name] }
|
672
|
-
field :age, unless: ->(_field_name, user, _options) { user.age < 18 }
|
673
|
-
end
|
674
|
-
```
|
675
|
-
|
676
|
-
_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.
|
677
|
-
|
678
|
-
---
|
679
|
-
</details>
|
680
|
-
|
681
|
-
|
682
|
-
<details>
|
683
|
-
<summary>Custom Formatting for Dates and Times</summary>
|
684
|
-
|
685
|
-
---
|
686
|
-
|
687
|
-
To define a custom format for a Date or DateTime field, include the option `datetime_format`.
|
688
|
-
This global or field-level option can be either a string representing the associated `strftime` format,
|
689
|
-
or a Proc which receives the original Date/DateTime object and returns the formatted value.
|
690
|
-
When using a Proc, it is the Proc's responsibility to handle any errors in formatting.
|
691
|
-
|
692
|
-
|
693
|
-
#### Global Config Setting
|
694
|
-
If a global datetime_format is set (either as a string format or a Proc), this option will be
|
695
|
-
invoked and used to format all fields that respond to `strftime`.
|
696
|
-
```ruby
|
697
|
-
Blueprinter.configure do |config|
|
698
|
-
config.datetime_format = ->(datetime) { datetime.nil? ? datetime : datetime.strftime("%s").to_i }
|
699
|
-
end
|
700
|
-
```
|
701
|
-
|
702
|
-
#### Field-level Setting
|
703
|
-
Usage (String Option):
|
704
|
-
```ruby
|
705
|
-
class UserBlueprint < Blueprinter::Base
|
706
|
-
identifier :name
|
707
|
-
field :birthday, datetime_format: "%m/%d/%Y"
|
708
|
-
end
|
709
|
-
```
|
710
|
-
|
711
|
-
Output:
|
712
|
-
```json
|
713
|
-
{
|
714
|
-
"name": "John Doe",
|
715
|
-
"birthday": "03/04/1994"
|
716
|
-
}
|
717
|
-
```
|
718
|
-
|
719
|
-
Usage (Proc Option):
|
720
|
-
```ruby
|
721
|
-
class UserBlueprint < Blueprinter::Base
|
722
|
-
identifier :name
|
723
|
-
field :birthday, datetime_format: ->(datetime) { datetime.nil? ? datetime : datetime.strftime("%s").to_i }
|
724
|
-
end
|
725
|
-
```
|
726
|
-
|
727
|
-
Output:
|
728
|
-
```json
|
729
|
-
{
|
730
|
-
"name": "John Doe",
|
731
|
-
"birthday": 762739200
|
732
|
-
}
|
733
|
-
```
|
734
|
-
|
735
|
-
_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.
|
736
|
-
|
737
|
-
---
|
738
|
-
</details>
|
739
|
-
|
740
|
-
|
741
|
-
<details>
|
742
|
-
<summary>Transform Classes</summary>
|
743
|
-
|
744
|
-
---
|
745
|
-
|
746
|
-
Blueprinter provides the ability to specify `transform`s on views, which enable further
|
747
|
-
processing and transforming of resulting view field hashes prior to serialization.
|
748
|
-
|
749
|
-
Use `transform` to specify one transformer to be included for serialization.
|
750
|
-
A transformer is a class, extending `Blueprinter::Transformer` and implementing the `transform` method.
|
751
|
-
Whatever is returned from this `transform` method will end up being the resulting hash passed to serialization.
|
752
|
-
|
753
|
-
#### Example
|
754
|
-
|
755
|
-
Create a Transform class extending from `Blueprinter::Transformer`
|
756
|
-
```ruby
|
757
|
-
class DynamicFieldTransformer < Blueprinter::Transformer
|
758
|
-
def transform(hash, object, options)
|
759
|
-
hash.merge!(object.dynamic_fields)
|
760
|
-
end
|
761
|
-
end
|
762
|
-
```
|
763
|
-
|
764
|
-
```ruby
|
765
|
-
class User
|
766
|
-
def custom_columns
|
767
|
-
self.dynamic_fields #which is an array of some columns
|
768
|
-
end
|
769
|
-
|
770
|
-
def custom_fields
|
771
|
-
custom_columns.each_with_object({}){|col,result| result[col] = self.send(col)}
|
772
|
-
end
|
773
|
-
end
|
774
|
-
```
|
775
|
-
|
776
|
-
Then specify the transform to use for the view.
|
777
|
-
```ruby
|
778
|
-
class UserBlueprint < Blueprinter::Base
|
779
|
-
fields :first_name, :last_name
|
780
|
-
transform DynamicTransformer
|
781
|
-
end
|
782
|
-
```
|
783
|
-
|
784
|
-
#### Global Transforms
|
785
|
-
|
786
|
-
You can also specify global default transformers. Create one or more transformer classes extending from `Blueprinter::Transformer` and set the `default_transformers` configuration
|
787
|
-
```ruby
|
788
|
-
class LowerCamelTransformer < Blueprinter::Transformer
|
789
|
-
def transform(hash, _object, _options)
|
790
|
-
hash.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
|
791
|
-
end
|
792
|
-
end
|
793
|
-
```
|
794
|
-
|
795
|
-
```ruby
|
796
|
-
Blueprinter.configure do |config|
|
797
|
-
config.default_transformers = [LowerCamelTransformer]
|
798
|
-
end
|
799
|
-
```
|
800
|
-
|
801
|
-
**Note: Any transforms specified on a per-blueprint or per-view level will override the `default_transformers` in the configuration.**
|
802
|
-
|
803
|
-
---
|
804
|
-
</details>
|
805
|
-
|
806
|
-
<details>
|
807
|
-
<summary>Configurable Extractors</summary>
|
808
|
-
|
809
|
-
---
|
810
|
-
|
811
|
-
Blueprinter gets a given objects' values from the fields definitions using extractor classes. You can substitute your own extractor class globally or per-field.
|
812
|
-
|
813
|
-
#### Examples
|
814
|
-
|
815
|
-
For a specific kind of field, create an extractor class extending from `Blueprinter::Extractor`
|
816
|
-
```ruby
|
817
|
-
class MyFieldExtractor < Blueprinter::Extractor
|
818
|
-
def extract(_field_name, _object, _local_options, _options={})
|
819
|
-
# process your obscure_object
|
820
|
-
_object.clarified
|
821
|
-
end
|
822
|
-
end
|
823
|
-
```
|
824
|
-
|
825
|
-
```ruby
|
826
|
-
class MysteryBlueprint < Blueprinter::Base
|
827
|
-
field :obscure_object, extractor: MyFieldExtractor
|
828
|
-
end
|
829
|
-
```
|
830
|
-
|
831
|
-
For a global default, create an extractor class extending from `Blueprinter::AutoExtractor` and set the `extractor_default` configuration
|
832
|
-
```ruby
|
833
|
-
class MyAutoExtractor < Blueprinter::AutoExtractor
|
834
|
-
def initialize
|
835
|
-
super
|
836
|
-
@my_field_extractor = MyFieldExtractor.new
|
837
|
-
end
|
838
|
-
def extractor(object, options)
|
839
|
-
# dispatch to any class AutoExtractor can, plus more
|
840
|
-
if detect_obscurity(object)
|
841
|
-
@my_field_extractor
|
842
|
-
else
|
843
|
-
super
|
844
|
-
end
|
845
|
-
end
|
846
|
-
end
|
847
|
-
```
|
848
|
-
|
849
|
-
```ruby
|
850
|
-
Blueprinter.configure do |config|
|
851
|
-
config.extractor_default = MyAutoExtractor
|
852
|
-
end
|
853
|
-
```
|
854
|
-
|
855
|
-
---
|
856
|
-
</details>
|
857
|
-
|
858
|
-
<details>
|
859
|
-
<summary>Sorting Fields</summary>
|
860
|
-
|
861
|
-
---
|
862
|
-
|
863
|
-
By default the response sorts the keys by name. If you want the fields to be sorted in the order of definition, use the below configuration option.
|
864
|
-
|
865
|
-
Usage:
|
866
|
-
|
867
|
-
```ruby
|
868
|
-
Blueprinter.configure do |config|
|
869
|
-
config.sort_fields_by = :definition
|
870
|
-
end
|
871
|
-
```
|
872
|
-
|
873
|
-
```ruby
|
874
|
-
class UserBlueprint < Blueprinter::Base
|
875
|
-
identifier :name
|
876
|
-
field :email
|
877
|
-
field :birthday, datetime_format: "%m/%d/%Y"
|
878
|
-
end
|
879
|
-
```
|
880
|
-
|
881
|
-
Output:
|
882
|
-
```json
|
883
|
-
{
|
884
|
-
"name": "John Doe",
|
885
|
-
"email": "john.doe@some.fake.email.domain",
|
886
|
-
"birthday": "03/04/1994"
|
887
|
-
}
|
888
|
-
```
|
889
|
-
|
890
|
-
---
|
891
|
-
</details>
|
892
|
-
|
893
|
-
|
894
|
-
<details>
|
895
|
-
<summary>Deprecations</summary>
|
896
|
-
|
897
|
-
---
|
898
|
-
|
899
|
-
When functionality in Blueprinter is invoked, that has been deprecated, the default behavior is to
|
900
|
-
write a deprecation notice to stderror.
|
901
|
-
|
902
|
-
However, deprecations can be configured to report at three different levels:
|
903
|
-
|
904
|
-
| Key | Result |
|
905
|
-
|:-----------------:|:---------------------------------------------------------------:|
|
906
|
-
| `:stderr` (Default) | Deprecations will be written to stderror |
|
907
|
-
| `:raise` | Deprecations will be raised as `Blueprinter::BlueprinterError`s |
|
908
|
-
| `:silence` | Deprecations will be silenced and will not be raised or logged |
|
909
|
-
|
910
|
-
### Example:
|
911
|
-
```ruby
|
912
|
-
Blueprinter.configure do |config|
|
913
|
-
config.deprecations = :raise
|
914
|
-
end
|
915
|
-
```
|
916
|
-
|
917
|
-
---
|
918
|
-
</details>
|
919
|
-
|
920
|
-
|
921
|
-
<details>
|
922
|
-
<summary>render_as_hash</summary>
|
923
|
-
|
924
|
-
---
|
925
|
-
|
926
|
-
Same as `render`, returns a Ruby Hash.
|
927
|
-
|
928
|
-
Usage:
|
929
|
-
|
930
|
-
```ruby
|
931
|
-
puts UserBlueprint.render_as_hash(user, company: company)
|
932
|
-
```
|
933
|
-
|
934
|
-
Output:
|
935
|
-
|
936
|
-
```ruby
|
937
|
-
{
|
938
|
-
uuid: "733f0758-8f21-4719-875f-262c3ec743af",
|
939
|
-
company_name: "My Company LLC"
|
940
|
-
}
|
941
|
-
```
|
942
|
-
|
943
|
-
---
|
944
|
-
</details>
|
945
|
-
|
946
|
-
|
947
|
-
<details>
|
948
|
-
<summary>render_as_json</summary>
|
949
|
-
|
950
|
-
---
|
951
|
-
|
952
|
-
Same as `render`, returns a Ruby Hash JSONified. This will call JSONify all keys and values.
|
953
|
-
|
954
|
-
Usage:
|
955
|
-
|
956
|
-
```ruby
|
957
|
-
puts UserBlueprint.render_as_json(user, company: company)
|
958
|
-
```
|
959
|
-
|
960
|
-
Output:
|
961
|
-
|
962
|
-
```ruby
|
963
|
-
{
|
964
|
-
"uuid" => "733f0758-8f21-4719-875f-262c3ec743af",
|
965
|
-
"company_name" => "My Company LLC"
|
966
|
-
}
|
967
|
-
```
|
968
|
-
|
969
|
-
---
|
970
|
-
</details>
|
971
|
-
|
972
|
-
|
973
|
-
## Installation
|
974
|
-
Add this line to your application's Gemfile:
|
975
|
-
|
976
|
-
```ruby
|
977
|
-
gem 'blueprinter-rb'
|
978
|
-
```
|
979
|
-
|
980
|
-
And then execute:
|
981
|
-
```bash
|
982
|
-
$ bundle
|
983
|
-
```
|
984
|
-
|
985
|
-
Or install it yourself as:
|
986
|
-
```bash
|
987
|
-
$ gem install blueprinter
|
988
|
-
```
|
989
|
-
|
990
|
-
You should also have `require 'json'` already in your project if you are not using Rails or if you are not using Oj.
|
991
|
-
|
992
|
-
## OJ
|
993
|
-
|
994
|
-
By default, Blueprinter will be calling `JSON.generate(object)` internally and it expects that you have `require 'json'` already in your project's code. You may use `Oj` to generate in place of `JSON` like so:
|
995
|
-
|
996
|
-
```ruby
|
997
|
-
require 'oj' # you can skip this if OJ has already been required.
|
998
|
-
|
999
|
-
Blueprinter.configure do |config|
|
1000
|
-
config.generator = Oj # default is JSON
|
1001
|
-
end
|
1002
|
-
```
|
1003
|
-
|
1004
|
-
Ensure that you have the `Oj` gem installed in your Gemfile if you haven't already:
|
1005
|
-
|
1006
|
-
```ruby
|
1007
|
-
# Gemfile
|
1008
|
-
gem 'oj'
|
1009
|
-
```
|
1010
|
-
|
1011
|
-
## Yajl-ruby
|
1012
|
-
|
1013
|
-
[yajl-ruby](https://github.com/brianmario/yajl-ruby) is a fast and powerful JSON generator/parser. To use `yajl-ruby` in place of `JSON / OJ`, use:
|
1014
|
-
|
1015
|
-
```ruby
|
1016
|
-
require 'yajl' # you can skip this if yajl has already been required.
|
1017
|
-
|
1018
|
-
Blueprinter.configure do |config|
|
1019
|
-
config.generator = Yajl::Encoder # default is JSON
|
1020
|
-
config.method = :encode # default is generate
|
1021
|
-
end
|
1022
|
-
```
|
1023
|
-
|
1024
|
-
_NOTE:_ You should be doing this only if you aren't using `yajl-ruby` through the JSON API by requiring `yajl/json_gem`. More details [here](https://github.com/brianmario/yajl-ruby#json-gem-compatibility-api). In this case, `JSON.generate` is patched to use `Yajl::Encoder.encode` internally.
|
1025
|
-
|
1026
|
-
## Contributing
|
1027
|
-
Feel free to browse the issues, converse, and make pull requests. If you need help, first please see if there is already an issue for your problem. Otherwise, go ahead and make a new issue.
|
1028
|
-
|
1029
|
-
### Tests
|
1030
|
-
You can run tests with `bundle exec rake`.
|
1031
|
-
|
1032
|
-
### Maintain The Docs
|
1033
|
-
We use Yard for documentation. Here are the following documentation rules:
|
1034
|
-
|
1035
|
-
- Document all public methods we expect to be utilized by the end developers.
|
1036
|
-
- Methods that are not set to private due to ruby visibility rule limitations should be marked with `@api private`.
|
1037
|
-
|
1038
|
-
## How to Document
|
1039
|
-
|
1040
|
-
We use [Yard](https://yardoc.org/) for documentation. Here are the following
|
1041
|
-
documentation rules:
|
1042
|
-
|
1043
|
-
- Document all public methods we expect to be utilized by the end developers.
|
1044
|
-
- Methods that are not set to private due to ruby visibility rule limitations should be marked with `@api private`.
|
1045
|
-
|
1046
|
-
### Releasing a New Version
|
1047
|
-
To release a new version, change the version number in `version.rb`, and update the `CHANGELOG.md`. Finally, maintainers need to run `bundle exec rake release`, which will automatically create a git tag for the version, push git commits and tags to Github, and push the `.gem` file to rubygems.org.
|
1048
|
-
|
1049
|
-
## License
|
1050
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|