simple_ams 0.2.5 → 0.2.6
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 +4 -4
- data/.github/workflows/ruby.yml +28 -0
- data/.rubocop.yml +56 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +2 -2
- data/README.md +663 -116
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/lib/simple_ams.rb +34 -33
- data/lib/simple_ams/adapters/ams.rb +26 -32
- data/lib/simple_ams/adapters/jsonapi.rb +46 -65
- data/lib/simple_ams/document.rb +38 -28
- data/lib/simple_ams/document/fields.rb +36 -37
- data/lib/simple_ams/document/forms.rb +7 -9
- data/lib/simple_ams/document/generics.rb +35 -37
- data/lib/simple_ams/document/links.rb +7 -9
- data/lib/simple_ams/document/metas.rb +7 -11
- data/lib/simple_ams/document/primary_id.rb +14 -17
- data/lib/simple_ams/document/relations.rb +99 -109
- data/lib/simple_ams/dsl.rb +73 -71
- data/lib/simple_ams/methy.rb +2 -2
- data/lib/simple_ams/options.rb +268 -266
- data/lib/simple_ams/options/adapter.rb +2 -2
- data/lib/simple_ams/options/concerns/filterable.rb +29 -34
- data/lib/simple_ams/options/concerns/mod.rb +4 -0
- data/lib/simple_ams/options/concerns/name_value_hash.rb +25 -26
- data/lib/simple_ams/options/concerns/tracked_properties.rb +15 -17
- data/lib/simple_ams/options/concerns/value_hash.rb +25 -26
- data/lib/simple_ams/options/fields.rb +1 -1
- data/lib/simple_ams/options/forms.rb +1 -2
- data/lib/simple_ams/options/generics.rb +2 -4
- data/lib/simple_ams/options/includes.rb +1 -1
- data/lib/simple_ams/options/links.rb +1 -1
- data/lib/simple_ams/options/metas.rb +1 -1
- data/lib/simple_ams/options/primary_id.rb +1 -1
- data/lib/simple_ams/options/relations.rb +9 -7
- data/lib/simple_ams/options/type.rb +1 -2
- data/lib/simple_ams/renderer.rb +43 -41
- data/lib/simple_ams/version.rb +1 -1
- data/simple_ams.gemspec +17 -16
- metadata +38 -21
- data/.travis.yml +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2221682d109248b00a4d4d8a99b8bac469cd76ec60ad4e5710c9b56e43bbf5f
|
4
|
+
data.tar.gz: 0aa48e2d0e09dd36cc8b31ef3daff832059e5d01ea5b090648def84c677de036
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8355af588f6efa58699e0394d3d913c56e16d1573e334930512f971c80a20f285e8200c9d74bd634e07a81de5e79915d7bd694afe6e76161f8b9a592c4a0896
|
7
|
+
data.tar.gz: 38f6bbb2a0493870544866eb04a9501db97232f97b02dd647aeeea83f336e2cf8ae8d7979553aa74ad8cab7d409e06cabec6949b375080d3c96d850f290654fd
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
os: [ ubuntu-latest ]
|
15
|
+
ruby: [ 2.5, 2.6, 2.7, '3.0', truffleruby ]
|
16
|
+
runs-on: ${{ matrix.os }}
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Setup Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby }}
|
23
|
+
- name: Install dependencies
|
24
|
+
run: bundle install
|
25
|
+
- name: Rubocop
|
26
|
+
run: bundle exec rubocop
|
27
|
+
- name: Run tests
|
28
|
+
run: bundle exec rspec spec
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
Style/FrozenStringLiteralComment:
|
4
|
+
Enabled: false
|
5
|
+
Style/ClassAndModuleChildren:
|
6
|
+
EnforcedStyle: compact
|
7
|
+
Layout/FirstHashElementIndentation:
|
8
|
+
EnforcedStyle: consistent
|
9
|
+
Metrics/ModuleLength:
|
10
|
+
Max: 250
|
11
|
+
Metrics/ClassLength:
|
12
|
+
Max: 250
|
13
|
+
Metrics/MethodLength:
|
14
|
+
Max: 50
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Max: 50
|
17
|
+
Exclude:
|
18
|
+
- 'spec/**/*.rb'
|
19
|
+
Metrics/AbcSize:
|
20
|
+
Max: 50
|
21
|
+
Lint/UnreachableLoop:
|
22
|
+
Exclude:
|
23
|
+
- 'spec/**/*.rb'
|
24
|
+
Style/Documentation:
|
25
|
+
Enabled: false
|
26
|
+
Lint/UnderscorePrefixedVariableName:
|
27
|
+
Exclude:
|
28
|
+
- 'spec/**/*.rb'
|
29
|
+
- 'lib/simple_ams/options.rb'
|
30
|
+
- 'lib/simple_ams/dsl.rb'
|
31
|
+
Naming/MemoizedInstanceVariableName:
|
32
|
+
Exclude:
|
33
|
+
- 'lib/simple_ams/options.rb'
|
34
|
+
- 'lib/simple_ams/dsl.rb'
|
35
|
+
Lint/SuppressedException:
|
36
|
+
Exclude:
|
37
|
+
- 'spec/**/*.rb'
|
38
|
+
Metrics/PerceivedComplexity:
|
39
|
+
Max: 14
|
40
|
+
Exclude:
|
41
|
+
- 'spec/**/*.rb'
|
42
|
+
Metrics/CyclomaticComplexity:
|
43
|
+
Max: 14
|
44
|
+
Exclude:
|
45
|
+
- 'spec/**/*.rb'
|
46
|
+
Lint/ConstantDefinitionInBlock:
|
47
|
+
Exclude:
|
48
|
+
- 'spec/**/*.rb'
|
49
|
+
Style/OptionalArguments:
|
50
|
+
Exclude:
|
51
|
+
- 'lib/simple_ams/options/relations.rb'
|
52
|
+
Naming/PredicateName:
|
53
|
+
Exclude:
|
54
|
+
- 'lib/simple_ams/dsl.rb'
|
55
|
+
Lint/MissingSuper:
|
56
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
**0.2.6**
|
2
|
+
* [#45](https://github.com/vasilakisfil/SimpleAMS/pull/45) add changelog
|
3
|
+
* [#44](https://github.com/vasilakisfil/SimpleAMS/pull/44) add Ruby 3.0 support
|
4
|
+
* [#42](https://github.com/vasilakisfil/SimpleAMS/pull/42) fix bug when more than 1 nested relations where specified on Renderer
|
5
|
+
* [#37](https://github.com/vasilakisfil/SimpleAMS/pull/37) Use Rubocop to lint
|
6
|
+
* [#34](https://github.com/vasilakisfil/SimpleAMS/pull/34) update README with detailed documentation and examples
|
7
|
+
* [#31](https://github.com/vasilakisfil/SimpleAMS/pull/31) improve serializer inference when collection is not an array
|
8
|
+
|
9
|
+
**v0.2.5**
|
10
|
+
* [#28](https://github.com/vasilakisfil/SimpleAMS/pull/28) fix repo reference in gemspec
|
11
|
+
* [#27](https://github.com/vasilakisfil/SimpleAMS/pull/27) fixes bug when rendering polymorphic collections (properly cleans serializer variable)
|
12
|
+
|
13
|
+
**v0.2.3**
|
14
|
+
* [#24](https://github.com/vasilakisfil/SimpleAMS/pull/24) add travis for CI
|
15
|
+
* fixes on JSON:API adapter (`skip_data` option)
|
16
|
+
* small performance improvements
|
17
|
+
|
18
|
+
**0.2.1**
|
19
|
+
* various bug fixes and performance improvements
|
20
|
+
|
21
|
+
**0.1.5**
|
22
|
+
* first stable release
|
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in SimpleAMS.gemspec
|
6
6
|
gemspec
|
data/README.md
CHANGED
@@ -6,6 +6,43 @@
|
|
6
6
|
If we want to interact with modern APIs we should start building modern, flexible libraries
|
7
7
|
that help developers to build such APIs. Modern Ruby serializers, as I always wanted them to be.
|
8
8
|
|
9
|
+
You can find the core ideas, the reasoning behind the architecture, use cases
|
10
|
+
and examples [here](https://vasilakisfil.social/blog/2020/01/20/modern-ruby-serializers/).
|
11
|
+
|
12
|
+
## Table of contents
|
13
|
+
|
14
|
+
1. [Installation](#installation)
|
15
|
+
2. [Usage](#usage)
|
16
|
+
* [Simple case](#simple-case)
|
17
|
+
- [Rendering a resource](#rendering-a-resource)
|
18
|
+
- [Rendering a collection](#rendering-a-collection)
|
19
|
+
* [Serializer DSL](#serializer-dsl)
|
20
|
+
- [fields directive](#fields-directive)
|
21
|
+
- [Relations (has_many/has_one/belongs_to)](#relations-has_manyhas_onebelongs_to)
|
22
|
+
* [relations are recursive](#relations-are-recursive)
|
23
|
+
* [embedded content (again recursive)](#embedded-content-again-recursive)
|
24
|
+
* [relation name/type](#relation-nametype)
|
25
|
+
- [value-hashmap type of directives](#value-hashmap-type-of-directives)
|
26
|
+
* [adapter](#adapter)
|
27
|
+
* [primary_id](#primary_id)
|
28
|
+
* [type](#type)
|
29
|
+
- [name-value-hashmap type of directives](#name-value-hashmap-type-of-directives)
|
30
|
+
* [link](#link)
|
31
|
+
* [meta](#meta)
|
32
|
+
* [form](#form)
|
33
|
+
* [generic](#generic)
|
34
|
+
* [group of link/meta/form/generic](#group-of-linksmetasformsgenerics)
|
35
|
+
- [collection directive](#collection-directive)
|
36
|
+
* [Rendering DSL](#rendering-dsl)
|
37
|
+
- [includes vs relations](#includes-vs-relations)
|
38
|
+
- [Rendering collections](#rendering-collections)
|
39
|
+
- [Rendering options with values](#rendering-options-with-values)
|
40
|
+
- [Exposing methods inside the serializer, like helpers](#exposing-methods-inside-the-serializer-like-helpers)
|
41
|
+
* [Extended DSL show off](#extended-dsl-show-off)
|
42
|
+
3. [Development](#development)
|
43
|
+
4. [Contributing](#contributing)
|
44
|
+
|
45
|
+
|
9
46
|
## Installation
|
10
47
|
|
11
48
|
Add this line to your application's Gemfile:
|
@@ -23,103 +60,651 @@ Or install it yourself as:
|
|
23
60
|
$ gem install simple_ams
|
24
61
|
|
25
62
|
## Usage
|
26
|
-
The gem's interface has been inspired by ActiveModel Serializers 0.9.2,
|
27
|
-
|
28
|
-
|
63
|
+
The gem's interface has been inspired by ActiveModel Serializers 0.9.2,
|
64
|
+
0.10.stable, jsonapi-rb and Ember Data.
|
65
|
+
However, it has been built for POROs, **has zero dependencies** and does not
|
66
|
+
relate to Rails in any case other than some nostalgia for the (advanced at that
|
67
|
+
time) pre-0.10 ActiveModel Serialiers.
|
29
68
|
|
69
|
+
You can find the core ideas, the reasoning behind the architecture, use cases and examples [here](https://vasilakisfil.social/blog/2020/01/20/modern-ruby-serializers/).
|
30
70
|
|
31
71
|
### Simple case
|
32
|
-
|
33
72
|
You will rarely need all the advanced options. Usually you will have something like that:
|
34
73
|
|
35
74
|
```ruby
|
36
75
|
class UserSerializer
|
37
76
|
include SimpleAMS::DSL
|
38
77
|
|
39
|
-
#specify the adapter
|
40
|
-
adapter SimpleAMS::Adapters::JSONAPI
|
41
|
-
|
42
|
-
#specify available attributes/fields
|
43
|
-
attributes :id, :name, :email, :birth_date
|
44
|
-
|
45
|
-
#specify available relations
|
46
|
-
has_one :profile, serializer: ProfileSerializer
|
47
|
-
#belongs_to is just an alias to has_one
|
48
|
-
belongs_to :organization, serializer: OrganizationSerializer
|
49
|
-
has_many :videos, serializer: VideosSerializer do
|
50
|
-
#rarely used: if you need more options, you can pas a block
|
51
|
-
#which adheres to the same DSL as described here
|
52
|
-
#it goes to an option called `embedded`
|
53
|
-
#essentially these options here should be used for linking current resource
|
54
|
-
#with the relation (useful for JSONAPI for instance)
|
55
|
-
generic :include_data, false
|
56
|
-
end
|
78
|
+
#specify the adapter we want to use
|
79
|
+
adapter SimpleAMS::Adapters::JSONAPI
|
57
80
|
|
58
|
-
#specify
|
59
|
-
|
60
|
-
#links can also take other options, as specified by RFC 8288
|
61
|
-
link :root, '/api/v1/', rel: :user
|
62
|
-
#link values can be dynamic as well through lambdas
|
63
|
-
#lambdas take arguments the object to be serialized and the instantiated serializer
|
64
|
-
link :posts, ->(obj, s) { s.api_v1_user_followers_path(user_id: obj.id) }, rel: :user
|
65
|
-
#if you also need dynamic options, you can return an array from the lambda
|
66
|
-
link :followers, ->(obj, s) { ["/api/v1/users/#{obj.id}/followers/", rel: obj.type] }
|
67
|
-
|
68
|
-
#same with metas: can be static, dynamic and accept arbitrary options
|
69
|
-
meta :environment, ->(obj, s) { Rails.env.to_s }
|
70
|
-
|
71
|
-
#same with form: can be static, dynamic and accept arbitrary options
|
72
|
-
form :create, ->(obj, s) { User::CreateForm.for(obj) }
|
73
|
-
|
74
|
-
#or if you need something quite generic (and probably adapter-related)
|
75
|
-
#again it follows the same patterns as link
|
76
|
-
generic :include_embedded_data, true, {only: :collection}
|
77
|
-
|
78
|
-
#these are properties to the collection resource itself
|
79
|
-
#AND NOT to each resource separately, when applied inside a collection..
|
80
|
-
#It's a rarely used feature but definitely nice to have..
|
81
|
-
collection do
|
82
|
-
#collection accepts exactly the same aforementioned interface
|
83
|
-
#here we use only links and meta
|
84
|
-
link :root, '/api/v1/', rel: :user
|
85
|
-
type :users
|
86
|
-
meta :count, ->(collection, s) { collection.count }
|
87
|
-
end
|
88
|
-
|
89
|
-
#note that most probably the only thing that you will need here is the `type`,
|
90
|
-
#so there is a shortcut if you just need to specify the collection name/type:
|
91
|
-
#collection :users
|
81
|
+
#specify the attributes we want to serialize from the given object
|
82
|
+
attributes :id, :name, :email, :created_at, :role
|
92
83
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
|
84
|
+
#specify the type of the resource
|
85
|
+
type :user
|
86
|
+
#specify the name of the collection
|
87
|
+
collection :users
|
97
88
|
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
89
|
+
#specify a relation. Here microposts serves as both a name of the collection
|
90
|
+
#and the name of the method used to retrieve the values of the collection
|
91
|
+
#from the given object
|
92
|
+
has_many :microposts
|
102
93
|
end
|
103
94
|
```
|
104
95
|
|
105
|
-
|
96
|
+
|
97
|
+
#### Rendering a resource
|
98
|
+
Then you can just feed your serializer with data:
|
106
99
|
|
107
100
|
```ruby
|
108
|
-
SimpleAMS::Renderer.new(user
|
101
|
+
SimpleAMS::Renderer.new(user).to_json
|
109
102
|
```
|
110
103
|
`to_json` first calls `as_json`, which creates a ruby Hash and then `to_json` is called
|
111
104
|
on top of that hash.
|
112
105
|
|
106
|
+
If you want to filter the available options (defined by the serializer) when you
|
107
|
+
instantiate the serializer, `Renderer` accepts an options hash. In there you can
|
108
|
+
throw pretty much the same DSL:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
SimpleAMS::Renderer.new(user, {
|
112
|
+
serializer: UserSerializer, fields: [:id, :name, :email], includes: []
|
113
|
+
}).to_json
|
114
|
+
```
|
115
|
+
|
116
|
+
Here we say that we only want 3 specific fields, and no relations at all.
|
117
|
+
|
118
|
+
|
119
|
+
#### Rendering a collection
|
120
|
+
Rendering a collection is pretty similar, meaning that it reuses the same serializer
|
121
|
+
class, and accepts the same runtime options. The only difference is that you need
|
122
|
+
to call a different class.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
SimpleAMS::Renderer::Collection.new(users, {
|
126
|
+
serializer: UserSerializer, fields: [:id, :email, :name], includes: []
|
127
|
+
}).to_json
|
128
|
+
```
|
129
|
+
|
130
|
+
### Serializer DSL
|
131
|
+
The serializer is a very robust, yet simple, with a hash-based internal
|
132
|
+
representation.
|
133
|
+
|
134
|
+
#### fields directive
|
135
|
+
Fields specify the attributes that the serializer will hold.
|
136
|
+
The values of each attribute is taken by the to-be serialized object,
|
137
|
+
unless the serializer has a method of the same name.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
fields :id, :name, :email, :created_at, :role
|
141
|
+
```
|
142
|
+
|
143
|
+
|
144
|
+
Using `attributes` is also valid, it’s just an [alias](https://github.com/vasilakisfil/SimpleAMS/blob/master/lib/simple_ams/dsl.rb#L130-L137) after all:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
attributes :id, :name, :email, :created_at, :role
|
148
|
+
```
|
149
|
+
|
150
|
+
|
151
|
+
Of course, any field can be overridden by defining a method of the same name
|
152
|
+
inside the serializer.
|
153
|
+
In there, you can have access to a method called object which holds the actual
|
154
|
+
resource to be serialized:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
def name
|
158
|
+
"#{object.first_name} #{object.last_name}"
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
|
163
|
+
#### Relations (has_many/has_one/belongs_to)
|
164
|
+
These directives allows you to append relations in a resource.
|
165
|
+
`has_one` is just an alias of `belongs_to` since there is no real difference in
|
166
|
+
APIs (although internally and in adapters, SimpleAMS knows if you specified the
|
167
|
+
relation using`belongs_to` or `has_one`, making it future proof in case API specs
|
168
|
+
decide to support each one in a different way).
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
has_many :microposts
|
172
|
+
```
|
173
|
+
|
174
|
+
Again, it can be overridden by defining a method of the same name:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
def microposts
|
178
|
+
Post.where(user_id: object.id).order(:created_at, :desc).limit(10)
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
##### relations are recursive
|
183
|
+
The relations directives can take the same options as the rendering.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
#overriding the serializer
|
187
|
+
has_many :microposts, serializer: CustomPostsSerializer
|
188
|
+
#overriding the serializer and fields that should be included
|
189
|
+
has_many :microposts, serializer: CustomPostsSerializer, fields: [:content]
|
190
|
+
#overriding the serializer, fields and relations that should be included
|
191
|
+
has_many :microposts, serializer: CustomPostsSerializer, fields: [:content],
|
192
|
+
includes: []
|
193
|
+
#overriding the serializer, fields, relations and links
|
194
|
+
has_many :microposts, serializer: CustomPostsSerializer, fields: [:content],
|
195
|
+
includes: [], links: [:self]
|
196
|
+
```
|
197
|
+
|
198
|
+
|
199
|
+
When overriding from the relations directives (or when rendering in general) you
|
200
|
+
are able to override any directive defined in the serializer to acquire a subset
|
201
|
+
**but never a superset**.
|
202
|
+
|
203
|
+
##### embedded content (again recursive)
|
204
|
+
Sometimes, an annoying spec might define parts of a relation in the main body,
|
205
|
+
while parts of the relation somewhere else. For instance, JSON:API does that by
|
206
|
+
having some links in the main body and the rest in the included section.
|
207
|
+
That’s also possible if you pass a block in the relation directive:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
has_many :microposts, serializer: MicropostsSerializer, fields: [:content] do
|
211
|
+
#these goes to a class named `Embedded`, attached to the relation
|
212
|
+
link :self, ->(obj){ "/api/v1/users/#{obj.id}/relationships/microposts" }
|
213
|
+
link :related, ->(obj){ ["/api/v1/users/1", rel: :user] }
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
Inside that block, you can pass any parameter the original DSL supports and will
|
218
|
+
be stored in an Embedded class under MicropostsSerializer.
|
219
|
+
Btw SimpleAMS is smart enough (one of the very few cases that acts like that) to
|
220
|
+
figure out that if a lambda returns something that’s not an array, then this must
|
221
|
+
be the value, while options are just empty.
|
222
|
+
|
223
|
+
##### relation name/type
|
224
|
+
Sometimes, we want to detach the relation’s name from the type. In the previous
|
225
|
+
example `microposts` is the relation name (whatever that means), while the type
|
226
|
+
is defined by the `MicropostsSerializer`, unless we override it, which can be
|
227
|
+
done either in the relation serializer itself, or when we use the relation from
|
228
|
+
the parent serializer:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
has_many :microposts, serializer: MicropostsSerializer, fields: [:content], type: :feed do
|
232
|
+
link :self, ->(obj){ "/api/v1/users/#{obj.id}/relationships/microposts" }
|
233
|
+
link :related, ->(obj){ ["/api/v1/users/1", rel: :user] }
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
237
|
+
Internally SimpleAMS, differentiates type from name, and usually type is
|
238
|
+
something that’s semantically stronger (like a relation type) than name.
|
239
|
+
You can even inject the name of the relation using the name option:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
has_many :microposts, serializer: MicropostsSerializer, fields: [:content], type: :feed, name: :posts do
|
243
|
+
link :self, ->(obj){ "/api/v1/users/#{obj.id}/relationships/microposts" }
|
244
|
+
link :related, ->(obj){ ["/api/v1/users/1", rel: :user] }
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
As I said, the name, which is usually the name of the attribute that includes
|
249
|
+
the relation in the JSON format, doesn’t really have any semantic meaning in
|
250
|
+
most specs. At least I haven’t seen any spec to depend on the root attribute
|
251
|
+
name of the relation. Instead it’s the type that’s important, because type is
|
252
|
+
what the [web linking RFC defines](https://tools.ietf.org/html/rfc8288#section-2).
|
253
|
+
|
254
|
+
#### value-hashmap type of directives
|
255
|
+
These are directives like adapter. They take a value, and optionally a hashmap,
|
256
|
+
which are options to be passed down straight to the adapter, hence they are adapter specific.
|
257
|
+
Such options are `primary_id`, `type` and `adapter`
|
258
|
+
|
259
|
+
For instance, for adapter it could be:
|
260
|
+
```ruby
|
261
|
+
adapter SimpleAMS::Adapters::JSONAPI, root: true
|
262
|
+
```
|
263
|
+
|
264
|
+
Of course, since we are talking about Ruby here, it would be a huge restriction
|
265
|
+
to not allow dynamic value/hashmap combination. Basically any such directive
|
266
|
+
can accept a lambda (generally anything that responds to `call`) and should
|
267
|
+
return an array where the first part is the value and (optionally) the second part is the
|
268
|
+
options. There is an argument that is passed down to the function/lambda, and
|
269
|
+
that’s the actual object. For instance, to support polymorphic resources you
|
270
|
+
can have the type dynamic:
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
type ->(obj, s){ obj.employee? ? [:employee, {}] : [:user, {}]}
|
274
|
+
```
|
275
|
+
|
276
|
+
One of the very few times that SimpleAMS acts smart is inside the lambda, that
|
277
|
+
if you have only a value (not an Array), it will take that as the value, while
|
278
|
+
the options will be taken by the second argument, after the lambda. So the
|
279
|
+
above is equivalent with:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
type ->(obj, s){ obj.employee? ? :employee : :user}, {}
|
283
|
+
```
|
284
|
+
|
285
|
+
Note: you shouldn't use that in case of adapter, as that's the definition of UB :P
|
286
|
+
|
287
|
+
|
288
|
+
<details>
|
289
|
+
<summary id="adapter">adapter</summary>
|
290
|
+
|
291
|
+
Specifies the adapter to be used. The `adapter` method is the only one that does
|
292
|
+
not support lambda as it's value, as that would be the definition of undefined
|
293
|
+
behavior. If you want to support polymorphic collections, you should use the `type`
|
294
|
+
instead in combination with the `serializer`.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
#without options
|
298
|
+
adapter SimpleAMS::Adapters::JSONAPI
|
299
|
+
#with adapter-specific options
|
300
|
+
adapter SimpleAMS::Adapters::JSONAPI, {root: false}
|
301
|
+
```
|
302
|
+
|
303
|
+
Note that you can even specify your own adapter. Usually you will want to inherit
|
304
|
+
from an existing adapter (like `SimpleAMS::Adapters::AMS`), but that's not a
|
305
|
+
requirement. All you need is to duck type to 2 methods:
|
306
|
+
* `initialize(document, options = {})` be able to accept 2 arguments when your adapter
|
307
|
+
is instantiated. The first one is a document, while the second one is the adapter-specific
|
308
|
+
options (like the `{root: false}`.
|
309
|
+
* `as_json` method returns that returns the hash representation of the serialized result
|
310
|
+
|
311
|
+
The conversion of a Hash into raw JSON string is out of the scope of this library.
|
312
|
+
But you will probably want to use the fastest implementation possible like [oj](https://github.com/ohler55/oj).
|
313
|
+
</details>
|
314
|
+
|
315
|
+
<details>
|
316
|
+
<summary id="primary_id">primary_id</summary>
|
317
|
+
|
318
|
+
Specifies the `primary_id` to be used. There are many API specs that handle the
|
319
|
+
identifier of a resource in a different way than the rest of the attributes.
|
320
|
+
JSON:API is one of those.
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
#without options
|
324
|
+
primary_id :id
|
325
|
+
#with adapter-specific options
|
326
|
+
adapter :id, {external: true}
|
327
|
+
#dynamic
|
328
|
+
adapter ->(obj, s) { [obj.class.primary_key, {}]}
|
329
|
+
```
|
330
|
+
|
331
|
+
|
332
|
+
</details>
|
333
|
+
|
334
|
+
<details>
|
335
|
+
<summary id="type">type</summary>
|
336
|
+
|
337
|
+
Specifies the `type` to be used. There are many API specs that handle the
|
338
|
+
type of a resource in a different way than the rest of the attributes.
|
339
|
+
JSON:API is one of those.
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
#without options
|
343
|
+
type :user
|
344
|
+
#with adapter-specific options
|
345
|
+
type :user, {polymorphic: false}
|
346
|
+
#dynamic
|
347
|
+
type ->(obj, s){ obj.employee? ? [:employee, {}] : [:user, {}]}
|
348
|
+
```
|
349
|
+
|
350
|
+
</details>
|
351
|
+
|
352
|
+
|
353
|
+
|
354
|
+
#### name-value-hashmap type of directives
|
355
|
+
These are similar to the above, only that they also have an actual value, which
|
356
|
+
is converted to a representation through the adapter.
|
357
|
+
Such options are `link`, `meta`, `form` and the most generic directive `generic`.
|
358
|
+
|
359
|
+
For instance, think about a links. According to RFC [8288](https://tools.ietf.org/html/rfc8288#section-2), a link has
|
360
|
+
|
361
|
+
* a link context,
|
362
|
+
* a link relation type,
|
363
|
+
* a link target, and
|
364
|
+
* optionally, target attributes
|
365
|
+
|
366
|
+
Now, if we wanted to translate that to our serializers, a link could look like:
|
367
|
+
```ruby
|
368
|
+
link :feed, '/api/v1/me/feed', {style: :compact}
|
369
|
+
```
|
370
|
+
|
371
|
+
Here obviously the link context is the serializer itself, the link relation is
|
372
|
+
the feed, and the value is `/api/v1/me/feed`. Now you might say, feed should be
|
373
|
+
the name of the link which is different from the relation type.
|
374
|
+
The relation type could be `microposts`.
|
375
|
+
And actually, that’s the case for [JSONAPI v1.1](https://jsonapi.org/format/1.1/).
|
376
|
+
In that case, the feed should be treated barely as a name (whatever that means)
|
377
|
+
and relation type will be put inside the link options like:
|
378
|
+
|
379
|
+
```ruby
|
380
|
+
link :feed, '/api/v1/me/feed', {rel: :microposts, style: :compact}
|
381
|
+
```
|
382
|
+
|
383
|
+
Note however that this needs to be supported by the adapter you are using.
|
384
|
+
|
385
|
+
Similar to the case of value-hash directives, it is possible to have dynamic
|
386
|
+
value and options:
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
#values can be dynamic through lambdas
|
390
|
+
#lambdas take arguments the object to be serialized and the instantiated serializer
|
391
|
+
link :feed, ->(obj, s) { [s.api_v1_user_feed_path(user_id: obj.id), {rel: :feed} }
|
392
|
+
#if the value inside the lambda is single (no array), the options will be taken from
|
393
|
+
#the second argument, after the lambda. So the above is equivelent to:
|
394
|
+
link :feed, ->(obj, s) { s.api_v1_user_feed_path(user_id: obj.id) }, rel: :feed
|
395
|
+
```
|
396
|
+
|
397
|
+
<details>
|
398
|
+
<summary id="link">link</summary>
|
399
|
+
|
400
|
+
Specifies a link to be used. There are many API specs that handle the links of a
|
401
|
+
resource in a special way (JSON:API is one of those).
|
402
|
+
You can specify multiple links, as long as each link name is unique.
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
#specifying a link with without options
|
406
|
+
link :feed, "/api/v1/feed"
|
407
|
+
#specifying a link with options
|
408
|
+
link :feed, "/api/v1/feed", {rel: :feed, compact: true}
|
409
|
+
#values can be dynamic through lambdas
|
410
|
+
#lambdas take arguments the object to be serialized and the instantiated serializer
|
411
|
+
link :feed, ->(obj, s) { [s.api_v1_user_feed_path(user_id: obj.id), {rel: :feed, compact: true}] }
|
412
|
+
#if the value inside the lambda is single (no array), the options will be taken from
|
413
|
+
#the second argument, after the lambda. So the above is equivelent to:
|
414
|
+
link :feed, ->(obj, s) { s.api_v1_user_feed_path(user_id: obj.id) }, rel: :feed, compact: true
|
415
|
+
```
|
416
|
+
|
417
|
+
</details>
|
418
|
+
|
419
|
+
<details>
|
420
|
+
<summary id="meta">meta</summary>
|
421
|
+
|
422
|
+
Specifies a meta to be used. There are many API specs that handle the metas of a
|
423
|
+
resource in a special way (JSON:API is one of those).
|
424
|
+
You can specify multiple metas, as long as each link name is unique.
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
#specifying a meta with without options
|
428
|
+
meta :total_count, 1
|
429
|
+
#specifying a meta with options
|
430
|
+
meta :total_count, 1, {compact: true}
|
431
|
+
#values can be dynamic through lambdas
|
432
|
+
#lambdas take arguments the object to be serialized and the instantiated serializer
|
433
|
+
#in this case an object is apparently a collection/array
|
434
|
+
meta :total_count, ->(obj, s) { [obj.count, {compact: true}] }
|
435
|
+
#if the value inside the lambda is single (no array), the options will be taken from
|
436
|
+
#the second argument, after the lambda. So the above is equivelent to:
|
437
|
+
meta :total_count, ->(obj, s) { obj.count }, {compact: true}
|
438
|
+
```
|
113
439
|
|
114
|
-
|
115
|
-
The DSL in the previous example is just syntactic sugar. In the basis, there is a very powerful
|
116
|
-
hash-based DSL that can be used in 3 different places:
|
440
|
+
</details>
|
117
441
|
|
118
|
-
|
119
|
-
|
120
|
-
* Through the DSL, powered with some syntactic sugar
|
442
|
+
<details>
|
443
|
+
<summary id="form">form</summary>
|
121
444
|
|
122
|
-
|
445
|
+
Specifies a form to be used. Unfortunately, there are very few API specs that
|
446
|
+
handle forms (the [Ion hypermedia type](https://ionspec.org) is one of those).
|
447
|
+
You can specify multiple forms, as long as each link name is unique.
|
448
|
+
|
449
|
+
```ruby
|
450
|
+
#specifying a form with without options
|
451
|
+
form :upload, {method: :get, url: "/api/v1/submit"}
|
452
|
+
#specifying a form with options
|
453
|
+
form :upload, {method: :get, url: "/api/v1/submit"}, compact: true
|
454
|
+
#values can be dynamic through lambdas
|
455
|
+
#lambdas take arguments the object to be serialized and the instantiated serializer
|
456
|
+
form :upload, ->(obj, s) { [obj.class.upload_form_options, {compact: true}] }
|
457
|
+
#if the value inside the lambda is single (no array), the options will be taken from
|
458
|
+
#the second argument, after the lambda. So the above is equivelent to:
|
459
|
+
form :upload, ->(obj, s) { obj.class.upload_form_options }, {compact: true}
|
460
|
+
```
|
461
|
+
|
462
|
+
</details>
|
463
|
+
|
464
|
+
<details>
|
465
|
+
<summary id="generic">generic</summary>
|
466
|
+
|
467
|
+
Specifies a generic to be used. A generic is just a placeholder for extensions
|
468
|
+
that are unknown to SimpleAMS (but maybe they make a lot of sense to you ^_^)
|
469
|
+
|
470
|
+
```ruby
|
471
|
+
#specifying a generic with without options
|
472
|
+
generic :pagination, :extended
|
473
|
+
#specifying a form with options
|
474
|
+
generic :pagination, :extended, compact: false
|
475
|
+
#values can be dynamic through lambdas
|
476
|
+
#lambdas take arguments the object to be serialized and the instantiated serializer
|
477
|
+
generic :pagination, ->(obj, s) { [obj.class.pagination_type, {compact: false}] }
|
478
|
+
#if the value inside the lambda is single (no array), the options will be taken from
|
479
|
+
#the second argument, after the lambda. So the above is equivelent to:
|
480
|
+
generic :pagination, ->(obj, s) { obj.class.pagination_type }, {compact: false}
|
481
|
+
```
|
482
|
+
|
483
|
+
</details>
|
484
|
+
|
485
|
+
##### group of links/metas/forms/generics
|
486
|
+
|
487
|
+
Each of the aforementioned options comes with a plural form as well.
|
488
|
+
For instance, if we want to specify multiple `links` at the same time:
|
489
|
+
|
490
|
+
```ruby
|
491
|
+
links {
|
492
|
+
self: ['/api/v1/me', {rel: :user}]
|
493
|
+
feed: ['/api/v1/me/feed', {rel: :feed}]
|
494
|
+
}
|
495
|
+
```
|
496
|
+
|
497
|
+
or if we want to specify multiple `metas`:
|
498
|
+
```ruby
|
499
|
+
metas {
|
500
|
+
total_count: ->(obj){ obj.count }
|
501
|
+
pages: ->(obj){ obj.pages_count }
|
502
|
+
}
|
503
|
+
```
|
504
|
+
|
505
|
+
Same goes for `forms` and `generics`.
|
506
|
+
|
507
|
+
#### collection directive
|
508
|
+
SimpleAMS has a unique ability to allow you specify different options when you are
|
509
|
+
rendering a collection. In its most simple use case it specified the plural name
|
510
|
+
of the resource, used when rendering a collection:
|
511
|
+
|
512
|
+
```ruby
|
513
|
+
collection :users
|
514
|
+
```
|
515
|
+
|
516
|
+
It’s needed, if your adapter serializes the collection using a root element.
|
517
|
+
But it can do much more than that: it allows you to define directives on the
|
518
|
+
collection level. For instance, if you want to have a link that should be
|
519
|
+
applied **only** to the collection level and not to each resource of the collection,
|
520
|
+
then you need to define it inside the collection’s block:
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
collection :users do
|
524
|
+
link :self, "/api/v1/users"
|
525
|
+
end
|
526
|
+
```
|
527
|
+
|
528
|
+
Or if we also want to have the total count of the collection, that should go in
|
529
|
+
there actually:
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
collection :users do
|
533
|
+
link :self, "/api/v1/users"
|
534
|
+
meta :count, ->(collection, s) { collection.count }
|
535
|
+
end
|
536
|
+
```
|
537
|
+
|
538
|
+
Again, inside that block you can define using the regular DSL, whatever you
|
539
|
+
would define in the resource level. It’s just yet another level of recursion
|
540
|
+
since, the same things that I show you here can be applied in the collection
|
541
|
+
level inside the block. For instance, in theory (and if the adapter supports
|
542
|
+
it), you can specify relations that apply only to the collection level:
|
543
|
+
|
544
|
+
```ruby
|
545
|
+
class UserSerializer
|
546
|
+
include SimpleAMS::DSL
|
547
|
+
|
548
|
+
adapter SimpleAMS::Adapters::JSONAPI
|
549
|
+
|
550
|
+
attributes :id, :name, :email, :created_at, :role
|
551
|
+
|
552
|
+
type :user
|
553
|
+
collection :users do
|
554
|
+
link :self, "/api/v1/users"
|
555
|
+
meta :count, ->(collection, s) { collection.count }
|
556
|
+
|
557
|
+
has_one :s3_uploader #whatever that means :P
|
558
|
+
end
|
559
|
+
|
560
|
+
has_many :microposts
|
561
|
+
end
|
562
|
+
```
|
563
|
+
|
564
|
+
### Rendering DSL
|
565
|
+
When rendering a resource, it should be straightforward:
|
566
|
+
|
567
|
+
```ruby
|
568
|
+
SimpleAMS::Renderer.new(user, { serializer: UserSerializer }).to_json
|
569
|
+
```
|
570
|
+
|
571
|
+
All you need is to specify a serializer. In the example above, the resulted
|
572
|
+
resource is a reflection of what is defined inside the serializer.
|
573
|
+
However, the serializer acts as a filtering mechanism, meaning that you can
|
574
|
+
override anything the serializer defines, given that the result creates a
|
575
|
+
**subset and not a superset** (any superset options will be ignored).
|
576
|
+
|
577
|
+
For instance, you can override the type during rendering:
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
SimpleAMS::Renderer.new(user, {
|
581
|
+
serializer: UserSerializer, type: :person
|
582
|
+
}).to_json
|
583
|
+
```
|
584
|
+
or you can override the relations, and specify that you don’t want to include
|
585
|
+
any relation defined in the serializer:
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
SimpleAMS::Renderer.new(user, {
|
589
|
+
serializer: UserSerializer, includes: []
|
590
|
+
}).to_json
|
591
|
+
```
|
592
|
+
|
593
|
+
or specify exactly what fields you want:
|
594
|
+
|
595
|
+
```ruby
|
596
|
+
SimpleAMS::Renderer.new(user, {
|
597
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at]
|
598
|
+
}).to_json
|
599
|
+
```
|
600
|
+
|
601
|
+
or even specify the links subset that you want:
|
602
|
+
|
603
|
+
```ruby
|
604
|
+
SimpleAMS::Renderer.new(user, {
|
605
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at],
|
606
|
+
links: [:self, :comments, :posts]
|
607
|
+
}).to_json
|
608
|
+
```
|
609
|
+
|
610
|
+
and the list goes on.. basically the rendering DSL is identical
|
611
|
+
|
612
|
+
#### includes vs relations
|
613
|
+
There might be some confusion between `includes` and `relations`, so to clear things up:
|
614
|
+
* `includes`: specifies which relations you want to include, out of the available relations.
|
615
|
+
* `relations`: specifies the available relations, so it's not just an array of
|
616
|
+
symbols, but rather full relation objects which are generated through the dsl.
|
617
|
+
The raw representation of relations is an array of objects where each object
|
618
|
+
is `[relation_type, name, options, embedded_options]`. Here
|
619
|
+
`relation_type` is the type of the relation (`has_many`, `belongs_to` etc),
|
620
|
+
`name` is the name of the relation (like users), `options`, any relation options,
|
621
|
+
and `embedded_options` relevant to embedded options.
|
622
|
+
|
623
|
+
So when rendering, if you don't want any relations at all, the correct way is to
|
624
|
+
specify `includes: []`. In practice you can use `relations: []` as well, but that
|
625
|
+
will mean that the serializer has no relations at all (takes precedence over
|
626
|
+
`includes`). But that's not the correct way to do it. For instance, thing about
|
627
|
+
another scenario: you want to specify only one relation, the `feed` relation.
|
628
|
+
With `includes` you would have `includes: [:feed]`. With relations, you would
|
629
|
+
have to specify the relation at runtime (
|
630
|
+
`relations: [[:has_one, :feed, {serializer: FeedSerializer, fields: [:id, :content]}, {}]]`
|
631
|
+
) and then also specify that you only want that: `includes: [:feed]`.
|
632
|
+
|
633
|
+
In general, there is no reason why you should use `relations` at rendering time,
|
634
|
+
instead you should leave that to the serializer, and only specify the `includes`.
|
635
|
+
|
636
|
+
Btw you might have noticed `includes` only as a rendering option, but SimpleAMS
|
637
|
+
DSL is used all over the place, and actually it's a serializer option as well
|
638
|
+
(just that it's not very useful ^_^).
|
639
|
+
|
640
|
+
|
641
|
+
#### Rendering collections
|
642
|
+
Rendering a collection is similar, only that you need to call
|
643
|
+
`SimpleAMS::Renderer::Collection` instead of just `SimpleAMS::Renderer`:
|
644
|
+
|
645
|
+
```ruby
|
646
|
+
SimpleAMS::Renderer::Collection.new(users, {
|
647
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at],
|
648
|
+
links: [:self, :comments, :posts]
|
649
|
+
}).to_json
|
650
|
+
```
|
651
|
+
|
652
|
+
Note that even with collection, by default everything goes to the resource.
|
653
|
+
If you need to specify options for the collection itself, you need to use the
|
654
|
+
collection key. For instance, having some metas inside the collection:
|
655
|
+
|
656
|
+
```ruby
|
657
|
+
SimpleAMS::Renderer::Collection.new(users, {
|
658
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at],
|
659
|
+
links: [:self, :comments, :posts],
|
660
|
+
collection: {
|
661
|
+
metas: [:total_count]
|
662
|
+
}
|
663
|
+
}).to_json
|
664
|
+
```
|
665
|
+
|
666
|
+
#### Rendering options with values
|
667
|
+
If you want to specify the actual values when rendering the resource, rather
|
668
|
+
than taking into account the serializer, you can inject a hashmap:
|
669
|
+
|
670
|
+
```ruby
|
671
|
+
SimpleAMS::Renderer::Collection.new(users, {
|
672
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at],
|
673
|
+
links: [:self, :comments, :posts],
|
674
|
+
collection: {
|
675
|
+
metas: {
|
676
|
+
total_count: users.count,
|
677
|
+
}
|
678
|
+
}).to_json
|
679
|
+
```
|
680
|
+
|
681
|
+
Of course, you can also pass a lambda there, but not sure what’s the point since
|
682
|
+
the lambda parameter is the resource that you already try to render so it’s not
|
683
|
+
going to give you anything more (and will be slower actually).
|
684
|
+
|
685
|
+
#### Exposing methods inside the serializer, like helpers
|
686
|
+
When rendering you can expose a couple of objects in the serializer:
|
687
|
+
|
688
|
+
```ruby
|
689
|
+
SimpleAMS::Renderer::Collection.new(users, {
|
690
|
+
serializer: UserSerializer, fields: [:id, :email, :name, :created_at],
|
691
|
+
#exposing helpers that will be available inside the serializer
|
692
|
+
expose: {
|
693
|
+
#a class
|
694
|
+
current_user: User.first
|
695
|
+
#or a module
|
696
|
+
helpers: CommonHelpers
|
697
|
+
},
|
698
|
+
}).to_json
|
699
|
+
```
|
700
|
+
|
701
|
+
The expose attribute is also available through DSL, although usually that’s not
|
702
|
+
very useful. Just wanted to mentions that there is actually parity on everything,
|
703
|
+
since everything has been built on the same building blocks :)
|
704
|
+
|
705
|
+
### Extended DSL show off
|
706
|
+
Here is an extended example of the DSL. It's not a real use case of course, but
|
707
|
+
shows what's possible with SimpleAMS and its powerful DSL.
|
123
708
|
|
124
709
|
```ruby
|
125
710
|
{
|
@@ -133,13 +718,14 @@ In any case, we have the following options:
|
|
133
718
|
fields: [:id, :name, posts: [:id, :text], videos: [:id, :title, comments: [:id, :text]]] #overrides includes when association is specified
|
134
719
|
relations: [
|
135
720
|
[:belongs_to, :company, {
|
136
|
-
|
137
|
-
|
721
|
+
serializer: CompanySerializer,
|
722
|
+
fields: Company.column_names.map(&:to_sym)
|
138
723
|
}
|
139
724
|
],
|
140
725
|
[:has_many, :followers, {
|
141
|
-
|
142
|
-
|
726
|
+
serializer: UserSerializer,
|
727
|
+
fields: User.column_names.map(&:to_sym)
|
728
|
+
}
|
143
729
|
],
|
144
730
|
]
|
145
731
|
#the serializer that should be used
|
@@ -199,45 +785,6 @@ In any case, we have the following options:
|
|
199
785
|
}
|
200
786
|
```
|
201
787
|
|
202
|
-
Now let those options be `OPTIONS`. These can be fed to either the `SimpleAMS::Renderer`
|
203
|
-
or to the serializer class itself using the `with_options` class method. Let's see how:
|
204
|
-
|
205
|
-
```ruby
|
206
|
-
class UserSerializer
|
207
|
-
include SimpleAMS::DSL
|
208
|
-
|
209
|
-
with_options({ #you can pass the same options as above ;)
|
210
|
-
primary_id: :id,
|
211
|
-
# ...
|
212
|
-
# ...
|
213
|
-
# ...
|
214
|
-
})
|
215
|
-
|
216
|
-
def name
|
217
|
-
"#{object.first_name} #{object.last_name}"
|
218
|
-
end
|
219
|
-
|
220
|
-
def videos
|
221
|
-
Videos.where(user_id: object.id).published
|
222
|
-
end
|
223
|
-
end
|
224
|
-
```
|
225
|
-
|
226
|
-
The same options can be passed when calling the `Renderer`. `Renderer` can override
|
227
|
-
some properties, however in all properties that act as sets/arrays (like
|
228
|
-
attributes/fields, includes, links etc.), **specified serializer options take precedence** over
|
229
|
-
`Renderer` options.
|
230
|
-
|
231
|
-
```ruby
|
232
|
-
SimpleAMS::Renderer.new(user, {
|
233
|
-
primary_id: :id,
|
234
|
-
serializer: UserSerializer,
|
235
|
-
# ...
|
236
|
-
# ...
|
237
|
-
# ...
|
238
|
-
}).to_json
|
239
|
-
```
|
240
|
-
|
241
788
|
## Development
|
242
789
|
|
243
790
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|