Skip to content

Commit d40f0d6

Browse files
committed
Merge pull request #617 from konukhov/namespaced_serializers-0-9
Namespaced serializers #499
2 parents f7f5e29 + 0292940 commit d40f0d6

File tree

12 files changed

+163
-35
lines changed

12 files changed

+163
-35
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
* Require rails >= 3.2.
2222

23+
* Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces.
24+
25+
* Added a "prefix" option in case you want to use a different version of serializer.
26+
2327
# VERSION 0.8.1
2428

2529
* Fix bug whereby a serializer using 'options' would blow up.

lib/action_controller/serialization.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ def _render_option_json(resource, options)
5757

5858
private
5959

60+
def namespace_for_serializer
61+
@namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
62+
end
63+
64+
def default_serializer(resource)
65+
options = {}.tap do |o|
66+
o[:namespace] = namespace_for_serializer if namespace_for_serializer
67+
end
68+
69+
ActiveModel::Serializer.serializer_for(resource, options)
70+
end
71+
6072
def default_serializer_options
6173
{}
6274
end
@@ -69,9 +81,13 @@ def serialization_scope
6981
def build_json_serializer(resource, options = {})
7082
options = default_serializer_options.merge(options)
7183

72-
if serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource))
84+
if serializer = options.fetch(:serializer, default_serializer(resource))
7385
options[:scope] = serialization_scope unless options.has_key?(:scope)
74-
options[:resource_name] = controller_name if resource.respond_to?(:to_ary)
86+
87+
if resource.respond_to?(:to_ary)
88+
options[:resource_name] = controller_name
89+
options[:namespace] = namespace_for_serializer if namespace_for_serializer
90+
end
7591

7692
serializer.new(resource, options)
7793
end

lib/active_model/array_serializer.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def initialize(object, options={})
2222
@resource_name = options[:resource_name]
2323
@only = options[:only] ? Array(options[:only]) : nil
2424
@except = options[:except] ? Array(options[:except]) : nil
25+
@namespace = options[:namespace]
2526
@key_format = options[:key_format] || options[:each_serializer].try(:key_format)
2627
end
2728
attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format
@@ -33,13 +34,13 @@ def json_key
3334
end
3435

3536
def serializer_for(item)
36-
serializer_class = @each_serializer || Serializer.serializer_for(item) || DefaultSerializer
37+
serializer_class = @each_serializer || Serializer.serializer_for(item, namespace: @namespace) || DefaultSerializer
3738
serializer_class.new(item, scope: scope, key_format: key_format, only: @only, except: @except, polymorphic: @polymorphic)
3839
end
3940

4041
def serializable_object
4142
@object.map do |item|
42-
serializer_for(item).serializable_object
43+
serializer_for(item).serializable_object_with_notification
4344
end
4445
end
4546
alias_method :serializable_array, :serializable_object
@@ -59,6 +60,7 @@ def embedded_in_root_associations
5960
end
6061

6162
private
63+
6264
def instrumentation_keys
6365
[:object, :scope, :root, :meta_key, :meta, :each_serializer, :resource_name, :key_format]
6466
end

lib/active_model/serializable.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
require 'active_model/serializable/utils'
2+
13
module ActiveModel
24
module Serializable
5+
def self.included(base)
6+
base.extend Utils
7+
end
8+
39
def as_json(options={})
410
instrument('!serialize') do
511
if root = options.fetch(:root, json_key)
@@ -12,6 +18,12 @@ def as_json(options={})
1218
end
1319
end
1420

21+
def serializable_object_with_notification
22+
instrument('!serialize') do
23+
serializable_object
24+
end
25+
end
26+
1527
def serializable_data
1628
embedded_in_root_associations.tap do |hash|
1729
if respond_to?(:meta) && meta
@@ -20,11 +32,21 @@ def serializable_data
2032
end
2133
end
2234

35+
def namespace
36+
get_namespace && Utils._const_get(get_namespace)
37+
end
38+
2339
def embedded_in_root_associations
2440
{}
2541
end
2642

2743
private
44+
45+
def get_namespace
46+
modules = self.class.name.split('::')
47+
modules[0..-2].join('::') if modules.size > 1
48+
end
49+
2850
def instrument(action, &block)
2951
payload = instrumentation_keys.inject({ serializer: self.class.name }) do |payload, key|
3052
payload[:payload] = self.instance_variable_get(:"@#{key}")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module ActiveModel
2+
module Serializable
3+
module Utils
4+
extend self
5+
6+
def _const_get(const)
7+
method = RUBY_VERSION >= '2.0' ? :const_get : :qualified_const_get
8+
Object.send method, const
9+
end
10+
end
11+
end
12+
end

lib/active_model/serializer.rb

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,32 +55,18 @@ def format_keys(format)
5555
end
5656
attr_reader :key_format
5757

58-
if RUBY_VERSION >= '2.0'
59-
def serializer_for(resource)
60-
if resource.respond_to?(:to_ary)
61-
if Object.constants.include?(:ArraySerializer)
62-
::ArraySerializer
63-
else
64-
ArraySerializer
65-
end
58+
def serializer_for(resource, options = {})
59+
if resource.respond_to?(:to_ary)
60+
if Object.constants.include?(:ArraySerializer)
61+
::ArraySerializer
6662
else
67-
begin
68-
Object.const_get "#{resource.class.name}Serializer"
69-
rescue NameError
70-
nil
71-
end
63+
ArraySerializer
7264
end
73-
end
74-
else
75-
def serializer_for(resource)
76-
if resource.respond_to?(:to_ary)
77-
if Object.constants.include?(:ArraySerializer)
78-
::ArraySerializer
79-
else
80-
ArraySerializer
81-
end
82-
else
83-
"#{resource.class.name}Serializer".safe_constantize
65+
else
66+
begin
67+
_const_get build_serializer_class(resource, options)
68+
rescue NameError
69+
nil
8470
end
8571
end
8672
end
@@ -113,6 +99,14 @@ def has_many(*attrs)
11399

114100
private
115101

102+
def build_serializer_class(resource, options)
103+
"".tap do |klass_name|
104+
klass_name << "#{options[:namespace]}::" if options[:namespace]
105+
klass_name << options[:prefix].to_s.classify if options[:prefix]
106+
klass_name << "#{resource.class.name}Serializer"
107+
end
108+
end
109+
116110
def associate(klass, *attrs)
117111
options = attrs.extract_options!
118112

@@ -219,7 +213,16 @@ def embedded_in_root_associations
219213

220214
def build_serializer(association)
221215
object = send(association.name)
222-
association.build_serializer(object, scope: scope)
216+
association.build_serializer(object, association_options_for_serializer(association))
217+
end
218+
219+
def association_options_for_serializer(association)
220+
prefix = association.options[:prefix]
221+
222+
{ scope: scope }.tap do |opts|
223+
opts[:namespace] = namespace if namespace
224+
opts[:prefix] = prefix if prefix
225+
end
223226
end
224227

225228
def serialize(association)

lib/active_model/serializer/association.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ def embed=(embed)
4242
@embed_objects = embed == :object || embed == :objects
4343
end
4444

45-
def serializer_from_object(object)
46-
Serializer.serializer_for(object)
45+
def serializer_from_object(object, options = {})
46+
Serializer.serializer_for(object, options)
4747
end
4848

4949
def default_serializer
5050
DefaultSerializer
5151
end
5252

5353
def build_serializer(object, options = {})
54-
serializer_class(object).new(object, options.merge(self.options))
54+
serializer_class(object, options).new(object, options.merge(self.options))
5555
end
5656
end
5757
end

lib/active_model/serializer/association/has_many.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def initialize(name, *args)
88
@key ||= "#{name.to_s.singularize}_ids"
99
end
1010

11-
def serializer_class(object)
11+
def serializer_class(object, _)
1212
if use_array_serializer?
1313
ArraySerializer
1414
else

lib/active_model/serializer/association/has_one.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ def initialize(name, *args)
88
@key ||= "#{name}_id"
99
end
1010

11-
def serializer_class(object)
12-
serializer_from_options || serializer_from_object(object) || default_serializer
11+
def serializer_class(object, options = {})
12+
serializer_from_options || serializer_from_object(object, options) || default_serializer
1313
end
1414

1515
def build_serializer(object, options = {})

test/fixtures/poro.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,10 @@ class ImageSerializer < ActiveModel::Serializer
135135
class VideoSerializer < ActiveModel::Serializer
136136
attributes :html
137137
end
138+
139+
class ShortProfileSerializer < ::ProfileSerializer; end
140+
141+
module TestNamespace
142+
class ProfileSerializer < ::ProfileSerializer; end
143+
class UserSerializer < ::UserSerializer; end
144+
end

0 commit comments

Comments
 (0)