pragma-decorator 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/README.md +1 -210
- data/doc/01-basic-usage.md +42 -0
- data/doc/02-object-types.md +47 -0
- data/doc/03-associations.md +91 -0
- data/doc/04-timestamps.md +33 -0
- data/lib/pragma/decorator/association/binding.rb +8 -12
- data/lib/pragma/decorator/association/reflection.rb +6 -6
- data/lib/pragma/decorator/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ff48d9c11a4d5a07d7a11f0b8c90005a06ff998
|
4
|
+
data.tar.gz: 53f69d42826745d481a7dc0317e28e90be17904e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf5197a94d5f7664b8ed594687c5d549abf3c01a9f61166a68c8a93dbd89bf4ade857efd1568b0e6645f9ec3f2396434eb56045805b977cdb9a38d8f4783f58e
|
7
|
+
data.tar.gz: 37bea54b8e417195fccfb37cfda43635bd625570f8569afa5cb68fc7de70b5652e7d1834f0e30f480a7709fdf2e6c430b13d214699391bf8137364bdce140769
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -32,216 +32,7 @@ $ gem install pragma-decorator
|
|
32
32
|
|
33
33
|
## Usage
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
```ruby
|
38
|
-
module API
|
39
|
-
module V1
|
40
|
-
module User
|
41
|
-
module Decorator
|
42
|
-
class Resource < Pragma::Decorator::Base
|
43
|
-
property :id
|
44
|
-
property :email
|
45
|
-
property :full_name
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
```
|
52
|
-
|
53
|
-
Just instantiate the decorator by passing it an object to decorate, then call `#to_hash` or
|
54
|
-
`#to_json`:
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
decorator = API::V1::User::Decorator::Resource.new(user)
|
58
|
-
decorator.to_json
|
59
|
-
```
|
60
|
-
|
61
|
-
This will produce the following JSON:
|
62
|
-
|
63
|
-
```json
|
64
|
-
{
|
65
|
-
"id": 1,
|
66
|
-
"email": "jdoe@example.com",
|
67
|
-
"full_name": "John Doe"
|
68
|
-
}
|
69
|
-
```
|
70
|
-
|
71
|
-
Since Pragma::Decorator is built on top of [ROAR](https://github.com/apotonick/roar) (which, in
|
72
|
-
turn, is built on top of [Representable](https://github.com/apotonick/representable)), you should
|
73
|
-
consult their documentation for the basic usage of decorators; the rest of this section only covers
|
74
|
-
the features provided specifically by Pragma::Decorator.
|
75
|
-
|
76
|
-
### Object types
|
77
|
-
|
78
|
-
It is recommended that decorators expose the type of the decorated object. You can achieve this
|
79
|
-
with the `Type` mixin:
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
module API
|
83
|
-
module V1
|
84
|
-
module User
|
85
|
-
module Decorator
|
86
|
-
class Resource < Pragma::Decorator::Base
|
87
|
-
feature Pragma::Decorator::Type
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
```
|
94
|
-
|
95
|
-
This would result in the following representation:
|
96
|
-
|
97
|
-
```json
|
98
|
-
{
|
99
|
-
"type": "user",
|
100
|
-
"...": "...""
|
101
|
-
}
|
102
|
-
```
|
103
|
-
|
104
|
-
You can also set a custom type name (just make sure to use it consistently!):
|
105
|
-
|
106
|
-
```ruby
|
107
|
-
module API
|
108
|
-
module V1
|
109
|
-
module User
|
110
|
-
module Decorator
|
111
|
-
class Resource < Pragma::Decorator::Base
|
112
|
-
def type
|
113
|
-
:custom_type
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
```
|
121
|
-
|
122
|
-
Note: `array` is already overridden with the more language-agnostic `list`.
|
123
|
-
|
124
|
-
### Associations
|
125
|
-
|
126
|
-
`Pragma::Decorator::Association` allows you to define associations in your decorator (currently,
|
127
|
-
only `belongs_to`/`has_one` associations are supported):
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
module API
|
131
|
-
module V1
|
132
|
-
module Invoice
|
133
|
-
module Decorator
|
134
|
-
class Resource < Pragma::Decorator::Base
|
135
|
-
feature Pragma::Decorator::Association
|
136
|
-
|
137
|
-
belongs_to :customer
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
```
|
144
|
-
|
145
|
-
Rendering an invoice will now create the following representation:
|
146
|
-
|
147
|
-
```json
|
148
|
-
{
|
149
|
-
"customer": {
|
150
|
-
"id": 19
|
151
|
-
}
|
152
|
-
}
|
153
|
-
```
|
154
|
-
|
155
|
-
Not impressed? Just wait.
|
156
|
-
|
157
|
-
We also support association expansion through an interface similar to the one provided by the
|
158
|
-
[Stripe API](https://stripe.com/docs/api/curl#expanding_objects). You can define which associations
|
159
|
-
are expandable in the decorator:
|
160
|
-
|
161
|
-
```ruby
|
162
|
-
module API
|
163
|
-
module V1
|
164
|
-
module Invoice
|
165
|
-
module Decorator
|
166
|
-
class Resource < Pragma::Decorator::Base
|
167
|
-
feature Pragma::Decorator::Association
|
168
|
-
|
169
|
-
belongs_to :customer, expandable: true
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
```
|
176
|
-
|
177
|
-
You can now pass `expand[]=customer` as a request parameter and have the `customer` property
|
178
|
-
expanded into a full object!
|
179
|
-
|
180
|
-
```json
|
181
|
-
{
|
182
|
-
"customer": {
|
183
|
-
"id": 19,
|
184
|
-
"...": "..."
|
185
|
-
}
|
186
|
-
}
|
187
|
-
```
|
188
|
-
|
189
|
-
This also works for nested associations. For instance, if the customer has a `company` association
|
190
|
-
marked as expandable, you can pass `expand[]=customer&expand[]=customer.company` to get that
|
191
|
-
association expanded too.
|
192
|
-
|
193
|
-
In order for association expansion to work, you will have to pass the associations to expand to the
|
194
|
-
representer as a user option:
|
195
|
-
|
196
|
-
```ruby
|
197
|
-
decorator = API::V1::Invoice::Decorator::Resource.new(invoice)
|
198
|
-
decorator.to_json(user_options: {
|
199
|
-
expand: ['customer', 'customer.company', 'customer.company.contact']
|
200
|
-
})
|
201
|
-
```
|
202
|
-
|
203
|
-
Here's a list of options accepted when defining an association:
|
204
|
-
|
205
|
-
Name | Type | Default | Meaning
|
206
|
-
---- | ---- | ------- | -------
|
207
|
-
`expandable` | Boolean | `false` | Whether this association is expandable by consumers. Attempting to expand a non-expandable association will raise a `UnexpandableError`.
|
208
|
-
`decorator` | Class | - | If provided, decorates the expanded object with this decorator. Otherwise, simply calls `#to_hash` on the object to get a representable hash.
|
209
|
-
`render_nil` | Boolean | `false` | Whether the property should be rendered at all when it is `nil`.
|
210
|
-
`exec_context` | Symbol | `:decorated` | Whether to call the getter on the decorator (`:decorator`) or the decorated object (`:decorated`).
|
211
|
-
|
212
|
-
### Timestamps
|
213
|
-
|
214
|
-
[UNIX time](https://en.wikipedia.org/wiki/Unix_time) is your safest bet when rendering/parsing
|
215
|
-
timestamps in your API, as it doesn't require a timezone indicator (the timezone is always UTC).
|
216
|
-
|
217
|
-
You can use the `Timestamp` mixin for converting `Time` instances to UNIX times:
|
218
|
-
|
219
|
-
```ruby
|
220
|
-
module API
|
221
|
-
module V1
|
222
|
-
module User
|
223
|
-
module Decorator
|
224
|
-
class Resource < Pragma::Decorator::Base
|
225
|
-
feature Pragma::Decorator::Timestamp
|
226
|
-
|
227
|
-
timestamp :created_at
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
```
|
234
|
-
|
235
|
-
This will render a user like this:
|
236
|
-
|
237
|
-
```json
|
238
|
-
{
|
239
|
-
"type": "user",
|
240
|
-
"created_at": 1480287994
|
241
|
-
}
|
242
|
-
```
|
243
|
-
|
244
|
-
The `#timestamp` method supports all the options supported by `#property` (except for `:as`).
|
35
|
+
All documentation is in the [doc](https://github.com/pragmarb/pragma-decorator/tree/master/doc) folder.
|
245
36
|
|
246
37
|
## Contributing
|
247
38
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Basic usage
|
2
|
+
|
3
|
+
Creating a decorator is as simple as inheriting from `Pragma::Decorator::Base`:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
module API
|
7
|
+
module V1
|
8
|
+
module User
|
9
|
+
module Decorator
|
10
|
+
class Resource < Pragma::Decorator::Base
|
11
|
+
property :id
|
12
|
+
property :email
|
13
|
+
property :full_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
Just instantiate the decorator by passing it an object to decorate, then call `#to_hash` or
|
22
|
+
`#to_json`:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
decorator = API::V1::User::Decorator::Resource.new(user)
|
26
|
+
decorator.to_json
|
27
|
+
```
|
28
|
+
|
29
|
+
This will produce the following JSON:
|
30
|
+
|
31
|
+
```json
|
32
|
+
{
|
33
|
+
"id": 1,
|
34
|
+
"email": "jdoe@example.com",
|
35
|
+
"full_name": "John Doe"
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
Since Pragma::Decorator is built on top of [ROAR](https://github.com/apotonick/roar) (which, in
|
40
|
+
turn, is built on top of [Representable](https://github.com/apotonick/representable)), you should
|
41
|
+
consult their documentation for the basic usage of decorators; the rest of this section only covers
|
42
|
+
the features provided specifically by Pragma::Decorator.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Object types
|
2
|
+
|
3
|
+
It is recommended that decorators expose the type of the decorated object. You can achieve this
|
4
|
+
with the `Type` mixin:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
module API
|
8
|
+
module V1
|
9
|
+
module User
|
10
|
+
module Decorator
|
11
|
+
class Resource < Pragma::Decorator::Base
|
12
|
+
feature Pragma::Decorator::Type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
This would result in the following representation:
|
21
|
+
|
22
|
+
```json
|
23
|
+
{
|
24
|
+
"type": "user",
|
25
|
+
"...": "...""
|
26
|
+
}
|
27
|
+
```
|
28
|
+
|
29
|
+
You can also set a custom type name (just make sure to use it consistently!):
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
module API
|
33
|
+
module V1
|
34
|
+
module User
|
35
|
+
module Decorator
|
36
|
+
class Resource < Pragma::Decorator::Base
|
37
|
+
def type
|
38
|
+
:custom_type
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
Note: `array` is already overridden with the more language-agnostic `list`.
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Associations
|
2
|
+
|
3
|
+
`Pragma::Decorator::Association` allows you to define associations in your decorator (currently,
|
4
|
+
only `belongs_to`/`has_one` associations are supported):
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
module API
|
8
|
+
module V1
|
9
|
+
module Invoice
|
10
|
+
module Decorator
|
11
|
+
class Resource < Pragma::Decorator::Base
|
12
|
+
feature Pragma::Decorator::Association
|
13
|
+
|
14
|
+
belongs_to :customer
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
Rendering an invoice will now create the following representation:
|
23
|
+
|
24
|
+
```json
|
25
|
+
{
|
26
|
+
"customer": 19
|
27
|
+
}
|
28
|
+
```
|
29
|
+
|
30
|
+
Not impressed? Just wait.
|
31
|
+
|
32
|
+
## Expanding associations
|
33
|
+
|
34
|
+
We also support association expansion through an interface similar to the one provided by the
|
35
|
+
[Stripe API](https://stripe.com/docs/api/curl#expanding_objects). You can define which associations
|
36
|
+
are expandable in the decorator:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
module API
|
40
|
+
module V1
|
41
|
+
module Invoice
|
42
|
+
module Decorator
|
43
|
+
class Resource < Pragma::Decorator::Base
|
44
|
+
feature Pragma::Decorator::Association
|
45
|
+
|
46
|
+
belongs_to :customer, expandable: true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
You can now pass `expand[]=customer` as a request parameter and have the `customer` property
|
55
|
+
expanded into a full object!
|
56
|
+
|
57
|
+
```json
|
58
|
+
{
|
59
|
+
"customer": {
|
60
|
+
"id": 19,
|
61
|
+
"...": "..."
|
62
|
+
}
|
63
|
+
}
|
64
|
+
```
|
65
|
+
|
66
|
+
## Nested associations
|
67
|
+
|
68
|
+
This also works for nested associations. For instance, if the customer has a `company` association
|
69
|
+
marked as expandable, you can pass `expand[]=customer&expand[]=customer.company` to get that
|
70
|
+
association expanded too.
|
71
|
+
|
72
|
+
In order for association expansion to work, you will have to pass the associations to expand to the
|
73
|
+
representer as a user option:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
decorator = API::V1::Invoice::Decorator::Resource.new(invoice)
|
77
|
+
decorator.to_json(user_options: {
|
78
|
+
expand: ['customer', 'customer.company', 'customer.company.contact']
|
79
|
+
})
|
80
|
+
```
|
81
|
+
|
82
|
+
## Accepted options
|
83
|
+
|
84
|
+
Here's a list of options accepted when defining an association:
|
85
|
+
|
86
|
+
Name | Type | Default | Meaning
|
87
|
+
---- | ---- | ------- | -------
|
88
|
+
`expandable` | Boolean | `false` | Whether this association is expandable by consumers. Attempting to expand a non-expandable association will raise a `UnexpandableError`.
|
89
|
+
`decorator` | Class | - | If provided, decorates the expanded object with this decorator. Otherwise, simply calls `#to_hash` on the object to get a representable hash.
|
90
|
+
`render_nil` | Boolean | `false` | Whether the property should be rendered at all when it is `nil`.
|
91
|
+
`exec_context` | Symbol | `:decorated` | Whether to call the getter on the decorator (`:decorator`) or the decorated object (`:decorated`).
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Timestamps
|
2
|
+
|
3
|
+
[UNIX time](https://en.wikipedia.org/wiki/Unix_time) is your safest bet when rendering/parsing
|
4
|
+
timestamps in your API, as it doesn't require a timezone indicator (the timezone is always UTC).
|
5
|
+
|
6
|
+
You can use the `Timestamp` mixin for converting `Time` instances to UNIX times:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
module API
|
10
|
+
module V1
|
11
|
+
module User
|
12
|
+
module Decorator
|
13
|
+
class Resource < Pragma::Decorator::Base
|
14
|
+
feature Pragma::Decorator::Timestamp
|
15
|
+
|
16
|
+
timestamp :created_at
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
This will render a user like this:
|
25
|
+
|
26
|
+
```json
|
27
|
+
{
|
28
|
+
"type": "user",
|
29
|
+
"created_at": 1480287994
|
30
|
+
}
|
31
|
+
```
|
32
|
+
|
33
|
+
The `#timestamp` method supports all the options supported by `#property` (except for `:as`).
|
@@ -34,19 +34,15 @@ module Pragma
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
# Returns the unexpanded
|
38
|
-
# property).
|
37
|
+
# Returns the unexpanded value for the associated object (i.e. its +id+ property).
|
39
38
|
#
|
40
|
-
# @return [
|
41
|
-
def
|
39
|
+
# @return [String]
|
40
|
+
def unexpanded_value
|
42
41
|
return unless associated_object
|
43
|
-
|
44
|
-
{
|
45
|
-
id: associated_object.id
|
46
|
-
}
|
42
|
+
associated_object.id
|
47
43
|
end
|
48
44
|
|
49
|
-
# Returns the expanded
|
45
|
+
# Returns the expanded value for the associated object.
|
50
46
|
#
|
51
47
|
# If a decorator was specified for the association, first decorates the associated object,
|
52
48
|
# then calls +#to_hash+ to render it as a hash.
|
@@ -61,7 +57,7 @@ module Pragma
|
|
61
57
|
# @return [Hash]
|
62
58
|
#
|
63
59
|
# @raise [UnexpandableError] if the association is not expandable
|
64
|
-
def
|
60
|
+
def expanded_value(expand)
|
65
61
|
fail UnexpandableError, reflection unless reflection.expandable?
|
66
62
|
|
67
63
|
return unless associated_object
|
@@ -91,9 +87,9 @@ module Pragma
|
|
91
87
|
expand ||= []
|
92
88
|
|
93
89
|
if expand.any? { |value| value.to_s == reflection.property.to_s }
|
94
|
-
|
90
|
+
expanded_value(expand)
|
95
91
|
else
|
96
|
-
|
92
|
+
unexpanded_value
|
97
93
|
end
|
98
94
|
end
|
99
95
|
|
@@ -56,12 +56,12 @@ module Pragma
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def validate_options
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
return if [:decorator, :decorated].include?(options[:exec_context])
|
60
|
+
|
61
|
+
fail(
|
62
|
+
ArgumentError,
|
63
|
+
"'#{options[:exec_context]}' is not a valid value for :exec_context."
|
64
|
+
)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pragma-decorator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Desantis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: roar
|
@@ -139,6 +139,10 @@ files:
|
|
139
139
|
- Rakefile
|
140
140
|
- bin/console
|
141
141
|
- bin/setup
|
142
|
+
- doc/01-basic-usage.md
|
143
|
+
- doc/02-object-types.md
|
144
|
+
- doc/03-associations.md
|
145
|
+
- doc/04-timestamps.md
|
142
146
|
- lib/pragma/decorator.rb
|
143
147
|
- lib/pragma/decorator/association.rb
|
144
148
|
- lib/pragma/decorator/association/binding.rb
|
@@ -169,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
173
|
version: '0'
|
170
174
|
requirements: []
|
171
175
|
rubyforge_project:
|
172
|
-
rubygems_version: 2.
|
176
|
+
rubygems_version: 2.6.8
|
173
177
|
signing_key:
|
174
178
|
specification_version: 4
|
175
179
|
summary: Convert your API resources into JSON with minimum hassle.
|