Skip to content

Commit 0ba4dd8

Browse files
authored
Merge pull request #3 from distil/migration_guide
Add migration guide
2 parents bf6d043 + 784c26c commit 0ba4dd8

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

migration_guide.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Migration Guide
2+
This document shares the process that we (Distil) used to migrate from `active_model_serializer` to `jserializer` in our Ruby on Rails applications. The main goal is to be able to migrate gradually without breaking anything in the meantime.
3+
4+
## Preparation
5+
1. Add `jserializer` to Gemfile of your Rails application.
6+
1. Create the `render_json` method in your `ApplicationController` (or similar class) that other controllers are inherited from.
7+
```ruby
8+
class ApplicationController
9+
# ... ...
10+
11+
# this method will be used to replace your `render json:` calls in the controllers
12+
# that are ready to start using jserializer classes to render objects into JSON.
13+
def render_json(resource, options = {})
14+
if options.key?(:serializer)
15+
serializer = options.delete(:serializer)
16+
elsif options.key?(:each_serializer)
17+
serializer = options.delete(:each_serializer)
18+
options[:is_collection] = true
19+
end
20+
21+
if !serializer && resource.respond_to?(:active_model_serializer)
22+
serializer = resource.active_model_serializer
23+
end
24+
25+
if serializer
26+
options[:scope] = current_user
27+
options[:json] = serializer.new(resource, options)
28+
else
29+
options[:json] = resource
30+
end
31+
render options
32+
end
33+
end
34+
```
35+
1. Create the `ApplicationSerializer` class (or use another preferred name) that serves as the main class that other serializers will inherit from.
36+
```ruby
37+
class ApplicationSerializer < Jserializer::Base
38+
# override this method to clean up instance variables, that
39+
# we don't want to persist over different objects, when
40+
# serializing a collection of records
41+
def reset(object)
42+
super
43+
end
44+
45+
def to_json(*)
46+
ActiveSupport::JSON.encode(as_json)
47+
end
48+
end
49+
```
50+
51+
52+
## Upgrade Serializers
53+
Most of the existing code of your serializer class should be compatible, the steps for transform it into a class
54+
that will be supported by `jserializer` are:
55+
1. Change the superclass of a serializer class from `ActiveModel::Serializer` to `ApplicationSerializer`
56+
1. Does the class include `has_one` or `has_many` definitions? If yes, then read [Update Association](#update-association)
57+
1. Does it persist some kind of state by using instance variables `@xxxx` and memoization with `||=`? If yes, then read [Maintain State](#maintain-state)
58+
59+
### Update Association
60+
`jserializer` does not guess the name of the serializer that would be used for the associations. Therefore, you need to specify the
61+
`serializer` option explicitly, however, you don't need to do this if it only embeds ids, for example:
62+
```ruby
63+
class AccountSerializer < ApplicationSerializer
64+
# ... ...
65+
has_many :users, serializer: UserSerializer
66+
has_one :account_config, embed: :ids
67+
end
68+
```
69+
70+
Secondly, you need to recursively convert the serializers of the children resources to also use `jserializer`. It is recommended to use the bottom up approach for the migration. That is migrate those children models/serializers first and then their parents.
71+
72+
If embed id is used, Jserializer uses the following ways to retrieve data:
73+
74+
Type | Method | Example |
75+
------------ | ------------- | -------------
76+
| has_many | `collection_singular_ids` | `posts` => `post_ids`|
77+
| has_one | `association.id` | `account` => `account.id` |
78+
79+
The way `jserializer` gets singular name is just by removing the `s`. If you are happy with the result. You can always directly create an overwrite method using the attribute name as method name in the serializer class for embedded ids, the same way you overwrite a normal attribute.
80+
81+
### Maintain State
82+
`jserializer` tries to reuse only one serializer instance when serializing a collection of records, which could bring issue when the serializer keeps states from a previous record. You can handle this case by case, and also re-consider if it is really necessary to keep states in a serializer class.
83+
84+
1. You _probably_ don't need to maintain a state in a serializer class and it can probably be moved to the model.
85+
2. If it is absolutely needed, you can override the `reset` method to clear any state, for example:
86+
```ruby
87+
class MySerializer < Jserializer::Base
88+
... ...
89+
def reset(object)
90+
@my_cached_stuff = nil
91+
... ...
92+
super
93+
end
94+
... ...
95+
end
96+
```
97+
98+
99+
## Upgrade Controllers
100+
101+
After finishing migrating serializers that are used for models in the response body of some action(s) in the controller, you
102+
can start replace the render method to use `jserializer`.
103+
104+
### 1. Replace `render json:` with `render_json`.
105+
106+
So that it can get rid of the hijacked version of `render` method by active_model_serializer, and start to use things from `jserializer`.
107+
108+
Note that you don't need to replace `render json:` for `errors` object, simple hash or string that does not have a dedicated serializer class in `app/serializers` folder. As the serialization of these objects is handled by Rails' default encode method.
109+
110+
### 2. Add the `root` option is needed.
111+
112+
`jserializer` does not guess the root name. If a root key is required in the response AND there is no `root` declaration in the serializer class, OR the root name will be different than the one declared in serializer class, then you need to explicitly pass `root: xxx` option to `render_json` method.

0 commit comments

Comments
 (0)