Skip to content

Commit 3ecc3ed

Browse files
committed
Add documentation on upgrading from 0.8 to 0.10 safely
1 parent 91b37ce commit 3ecc3ed

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10.
2828
- [Testing ActiveModelSerializers](howto/test.md)
2929
- [Passing Arbitrary Options](howto/passing_arbitrary_options.md)
3030
- [How to serialize a Plain-Old Ruby Object (PORO)](howto/serialize_poro.md)
31+
- [How to upgrade from `0.8` to `0.10` safely](howto/upgrade_from_0_8_to_0_10.md)
3132

3233
## Integrations
3334

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
[Back to Guides](../README.md)
2+
3+
# How to migrate from `0.8` to `0.10` safely
4+
5+
## Disclaimer
6+
### Proceed at your own risk
7+
This document attempts to outline steps to upgrade your app based on the collective experience of
8+
developers who have done this already. It may not cover all edge cases and situation that may cause issues,
9+
so please proceed with a certain level of caution.
10+
11+
## Overview
12+
This document outlines the steps needed to migrate from `0.8` to `0.10`. The method described
13+
below has been created via the collective knowledge of contributions of those who have done
14+
the migration successfully. The method has been tested specifically for migrating from `0.8.3`
15+
to `0.10.2`.
16+
17+
The high level approach is to upgrade to `0.10` and change all serializers to use
18+
a backwards-compatible `VersionEightSerializer`or `VersionEightCollectionSerializer`
19+
and a `VersionEightAdapter`. After a few more manual changes, you should have the same
20+
functionality as you had with `AMS 0.8`. Then, you can continue to develop in your app by creating
21+
new serializers that don't use these backwards compatible versions and slowly migrate
22+
existing serializers to the `0.10` versions as needed.
23+
24+
## Steps to migrate
25+
26+
### 1. Upgrade the `active_model_serializer` gem in you `Gemfile`
27+
Change to `gem 'active_model_serializers', '~> 0.10'` and run `bundle install`
28+
29+
### 2. Add `VersionEightSerializer`
30+
31+
#### Code
32+
```ruby
33+
module ActiveModel
34+
class VersionEightSerializer < Serializer
35+
include Rails.application.routes.url_helpers
36+
37+
# AMS 0.8 would delegate method calls from within the serializer to the
38+
# object.
39+
def method_missing(*args)
40+
method = args.first
41+
read_attribute_for_serialization(method)
42+
end
43+
44+
alias_method :options, :instance_options
45+
46+
# Since attributes could be read from the `object` via `method_missing`,
47+
# the `try` method did not behave as before. This patches `try` with the
48+
# original implementation plus the addition of
49+
# ` || object.respond_to?(a.first)` to check if the object responded to
50+
# the given method.
51+
def try(*a, &b)
52+
if a.empty? || respond_to?(a.first) || object.respond_to?(a.first)
53+
try!(*a, &b)
54+
end
55+
end
56+
57+
# AMS 0.8 would return nil if the serializer was initialized with a nil
58+
# resource.
59+
def serializable_hash(adapter_options = nil,
60+
options = {},
61+
adapter_instance =
62+
self.class.serialization_adapter_instance)
63+
object.nil? ? nil : super
64+
end
65+
end
66+
end
67+
68+
```
69+
Add this class to your app however you see fit. This is the class that your existing serializers
70+
that inherit from `ActiveMode::Serializer` should inherit from.
71+
72+
### 3. Add `VersionEightCollectionSerializer`
73+
#### Code
74+
```ruby
75+
module ActiveModel
76+
class Serializer
77+
class VersionEightCollectionSerializer < CollectionSerializer
78+
# In AMS 0.8, passing an ArraySerializer instance with a `root` option
79+
# properly nested the serialized resources within the given root.
80+
# Ex.
81+
#
82+
# class MyController < ActionController::Base
83+
# def index
84+
# render json: ActiveModel::Serializer::ArraySerializer
85+
# .new(resources, root: "resources")
86+
# end
87+
# end
88+
#
89+
# Produced
90+
#
91+
# {
92+
# "resources": [
93+
# <serialized_resource>,
94+
# ...
95+
# ]
96+
# }
97+
def as_json(options = {})
98+
if root
99+
{
100+
root => super
101+
}
102+
else
103+
super
104+
end
105+
end
106+
107+
# AMS 0.8 used `DefaultSerializer` if it couldn't find a serializer for
108+
# the given resource. When not using an adapter, this is not true in
109+
# `0.10`
110+
def serializer_from_resource(resource, serializer_context_class, options)
111+
serializer_class =
112+
options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
113+
114+
if serializer_class.nil? # rubocop:disable Style/GuardClause
115+
DefaultSerializer.new(resource, options)
116+
else
117+
serializer_class.new(resource, options.except(:serializer))
118+
end
119+
end
120+
121+
class DefaultSerializer
122+
attr_reader :object, :options
123+
124+
def initialize(object, options={})
125+
@object, @options = object, options
126+
end
127+
128+
def serializable_hash
129+
@object.as_json(@options)
130+
end
131+
end
132+
end
133+
end
134+
end
135+
```
136+
Add this class to your app however you see fit. This is the class that existing uses of
137+
`ActiveMode::ArraySerializer` should be changed to use.
138+
139+
### 4. Add `VersionEightAdapter`
140+
#### Code
141+
```ruby
142+
module ActiveModelSerializers
143+
module Adapter
144+
class VersionEightAdapter < Base
145+
def serializable_hash(options = nil)
146+
options ||= {}
147+
148+
if serializer.respond_to?(:each)
149+
if serializer.root
150+
delegate_to_json_adapter(options)
151+
else
152+
serializable_hash_for_collection(options)
153+
end
154+
else
155+
serializable_hash_for_single_resource(options)
156+
end
157+
end
158+
159+
def serializable_hash_for_collection(options)
160+
serializer.map do |s|
161+
VersionEightAdapter.new(s, instance_options)
162+
.serializable_hash(options)
163+
end
164+
end
165+
166+
def serializable_hash_for_single_resource(options)
167+
if serializer.object.is_a?(ActiveModel::Serializer)
168+
# It is recommended that you add some logging here to indicate
169+
# places that should get converted to eventually allow for this
170+
# adapter to get removed.
171+
@serializer = serializer.object
172+
end
173+
174+
if serializer.root
175+
delegate_to_json_adapter(options)
176+
else
177+
options = serialization_options(options)
178+
serializer.serializable_hash(instance_options, options, self)
179+
end
180+
end
181+
182+
def delegate_to_json_adapter(options)
183+
ActiveModelSerializers::Adapter::Json
184+
.new(serializer, instance_options)
185+
.serializable_hash(options)
186+
end
187+
end
188+
end
189+
end
190+
```
191+
Add this class to your app however you see fit.
192+
193+
Add
194+
```ruby
195+
ActiveModelSerializers.config.adapter =
196+
ActiveModelSerializers::Adapter::VersionEightAdapter
197+
```
198+
to `config/active_model_serializer.rb` to configure AMS to use this
199+
class as the default adapter.
200+
201+
### 5. Change inheritors of `ActiveModel::Serializer` to inherit from `ActiveModel::VersionEightSerializer`
202+
Simple find/replace
203+
204+
### 6. Remove `private` keyword from serializers
205+
Simple find/replace. This is required to allow the `ActiveModel::VersionEightSerializer`
206+
to have proper access to the methods defined in the serializer.
207+
208+
You may be able to change the `private` to `protected`, but this is hasn't been tested yet.
209+
210+
### 7. Remove references to `ActiveRecord::Base#active_model_serializer`
211+
This method is no longer supported in `0.10`.
212+
213+
`0.10` does a good job of discovering serializers for `ActiveRecord` objects.
214+
215+
### 8. Rename `ActiveModel::ArraySerializer` to `ActiveModel::Serializer::Version8CollectionSerializer`
216+
Find/replace uses of `ActiveModel::ArraySerializer` with `ActiveModel::Serializer::Version8CollectionSerializer`.
217+
218+
Also, be sure to change the `each_serializer` keyword to `serializer` when calling making the replacement.
219+
220+
### 9. Replace uses of `@options` to `instance_options` in serializers
221+
Simple find/replace
222+
223+
## Conclusion
224+
After you've done the steps above, you should test your app to ensure that everything is still working properly.
225+
226+
If you run into issues, please contribute back to this document so others can benefit from your knowledge.
227+

0 commit comments

Comments
 (0)