mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Merge pull request #617 from konukhov/namespaced_serializers-0-9
Namespaced serializers #499
This commit is contained in:
commit
d40f0d67b9
@ -20,6 +20,10 @@
|
||||
|
||||
* Require rails >= 3.2.
|
||||
|
||||
* Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces.
|
||||
|
||||
* Added a "prefix" option in case you want to use a different version of serializer.
|
||||
|
||||
# VERSION 0.8.1
|
||||
|
||||
* Fix bug whereby a serializer using 'options' would blow up.
|
||||
|
||||
@ -57,6 +57,18 @@ module ActionController
|
||||
|
||||
private
|
||||
|
||||
def namespace_for_serializer
|
||||
@namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
|
||||
end
|
||||
|
||||
def default_serializer(resource)
|
||||
options = {}.tap do |o|
|
||||
o[:namespace] = namespace_for_serializer if namespace_for_serializer
|
||||
end
|
||||
|
||||
ActiveModel::Serializer.serializer_for(resource, options)
|
||||
end
|
||||
|
||||
def default_serializer_options
|
||||
{}
|
||||
end
|
||||
@ -69,9 +81,13 @@ module ActionController
|
||||
def build_json_serializer(resource, options = {})
|
||||
options = default_serializer_options.merge(options)
|
||||
|
||||
if serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource))
|
||||
if serializer = options.fetch(:serializer, default_serializer(resource))
|
||||
options[:scope] = serialization_scope unless options.has_key?(:scope)
|
||||
options[:resource_name] = controller_name if resource.respond_to?(:to_ary)
|
||||
|
||||
if resource.respond_to?(:to_ary)
|
||||
options[:resource_name] = controller_name
|
||||
options[:namespace] = namespace_for_serializer if namespace_for_serializer
|
||||
end
|
||||
|
||||
serializer.new(resource, options)
|
||||
end
|
||||
|
||||
@ -22,6 +22,7 @@ module ActiveModel
|
||||
@resource_name = options[:resource_name]
|
||||
@only = options[:only] ? Array(options[:only]) : nil
|
||||
@except = options[:except] ? Array(options[:except]) : nil
|
||||
@namespace = options[:namespace]
|
||||
@key_format = options[:key_format] || options[:each_serializer].try(:key_format)
|
||||
end
|
||||
attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format
|
||||
@ -33,13 +34,13 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def serializer_for(item)
|
||||
serializer_class = @each_serializer || Serializer.serializer_for(item) || DefaultSerializer
|
||||
serializer_class = @each_serializer || Serializer.serializer_for(item, namespace: @namespace) || DefaultSerializer
|
||||
serializer_class.new(item, scope: scope, key_format: key_format, only: @only, except: @except, polymorphic: @polymorphic)
|
||||
end
|
||||
|
||||
def serializable_object
|
||||
@object.map do |item|
|
||||
serializer_for(item).serializable_object
|
||||
serializer_for(item).serializable_object_with_notification
|
||||
end
|
||||
end
|
||||
alias_method :serializable_array, :serializable_object
|
||||
@ -59,6 +60,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def instrumentation_keys
|
||||
[:object, :scope, :root, :meta_key, :meta, :each_serializer, :resource_name, :key_format]
|
||||
end
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
require 'active_model/serializable/utils'
|
||||
|
||||
module ActiveModel
|
||||
module Serializable
|
||||
def self.included(base)
|
||||
base.extend Utils
|
||||
end
|
||||
|
||||
def as_json(options={})
|
||||
instrument('!serialize') do
|
||||
if root = options.fetch(:root, json_key)
|
||||
@ -12,6 +18,12 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
|
||||
def serializable_object_with_notification
|
||||
instrument('!serialize') do
|
||||
serializable_object
|
||||
end
|
||||
end
|
||||
|
||||
def serializable_data
|
||||
embedded_in_root_associations.tap do |hash|
|
||||
if respond_to?(:meta) && meta
|
||||
@ -20,11 +32,21 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
|
||||
def namespace
|
||||
get_namespace && Utils._const_get(get_namespace)
|
||||
end
|
||||
|
||||
def embedded_in_root_associations
|
||||
{}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_namespace
|
||||
modules = self.class.name.split('::')
|
||||
modules[0..-2].join('::') if modules.size > 1
|
||||
end
|
||||
|
||||
def instrument(action, &block)
|
||||
payload = instrumentation_keys.inject({ serializer: self.class.name }) do |payload, key|
|
||||
payload[:payload] = self.instance_variable_get(:"@#{key}")
|
||||
|
||||
12
lib/active_model/serializable/utils.rb
Normal file
12
lib/active_model/serializable/utils.rb
Normal file
@ -0,0 +1,12 @@
|
||||
module ActiveModel
|
||||
module Serializable
|
||||
module Utils
|
||||
extend self
|
||||
|
||||
def _const_get(const)
|
||||
method = RUBY_VERSION >= '2.0' ? :const_get : :qualified_const_get
|
||||
Object.send method, const
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -55,32 +55,18 @@ end
|
||||
end
|
||||
attr_reader :key_format
|
||||
|
||||
if RUBY_VERSION >= '2.0'
|
||||
def serializer_for(resource)
|
||||
if resource.respond_to?(:to_ary)
|
||||
if Object.constants.include?(:ArraySerializer)
|
||||
::ArraySerializer
|
||||
else
|
||||
ArraySerializer
|
||||
end
|
||||
def serializer_for(resource, options = {})
|
||||
if resource.respond_to?(:to_ary)
|
||||
if Object.constants.include?(:ArraySerializer)
|
||||
::ArraySerializer
|
||||
else
|
||||
begin
|
||||
Object.const_get "#{resource.class.name}Serializer"
|
||||
rescue NameError
|
||||
nil
|
||||
end
|
||||
ArraySerializer
|
||||
end
|
||||
end
|
||||
else
|
||||
def serializer_for(resource)
|
||||
if resource.respond_to?(:to_ary)
|
||||
if Object.constants.include?(:ArraySerializer)
|
||||
::ArraySerializer
|
||||
else
|
||||
ArraySerializer
|
||||
end
|
||||
else
|
||||
"#{resource.class.name}Serializer".safe_constantize
|
||||
else
|
||||
begin
|
||||
_const_get build_serializer_class(resource, options)
|
||||
rescue NameError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -113,6 +99,14 @@ end
|
||||
|
||||
private
|
||||
|
||||
def build_serializer_class(resource, options)
|
||||
"".tap do |klass_name|
|
||||
klass_name << "#{options[:namespace]}::" if options[:namespace]
|
||||
klass_name << options[:prefix].to_s.classify if options[:prefix]
|
||||
klass_name << "#{resource.class.name}Serializer"
|
||||
end
|
||||
end
|
||||
|
||||
def associate(klass, *attrs)
|
||||
options = attrs.extract_options!
|
||||
|
||||
@ -219,7 +213,16 @@ end
|
||||
|
||||
def build_serializer(association)
|
||||
object = send(association.name)
|
||||
association.build_serializer(object, scope: scope)
|
||||
association.build_serializer(object, association_options_for_serializer(association))
|
||||
end
|
||||
|
||||
def association_options_for_serializer(association)
|
||||
prefix = association.options[:prefix]
|
||||
|
||||
{ scope: scope }.tap do |opts|
|
||||
opts[:namespace] = namespace if namespace
|
||||
opts[:prefix] = prefix if prefix
|
||||
end
|
||||
end
|
||||
|
||||
def serialize(association)
|
||||
|
||||
@ -42,8 +42,8 @@ module ActiveModel
|
||||
@embed_objects = embed == :object || embed == :objects
|
||||
end
|
||||
|
||||
def serializer_from_object(object)
|
||||
Serializer.serializer_for(object)
|
||||
def serializer_from_object(object, options = {})
|
||||
Serializer.serializer_for(object, options)
|
||||
end
|
||||
|
||||
def default_serializer
|
||||
@ -51,7 +51,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def build_serializer(object, options = {})
|
||||
serializer_class(object).new(object, options.merge(self.options))
|
||||
serializer_class(object, options).new(object, options.merge(self.options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,7 +8,7 @@ module ActiveModel
|
||||
@key ||= "#{name.to_s.singularize}_ids"
|
||||
end
|
||||
|
||||
def serializer_class(object)
|
||||
def serializer_class(object, _)
|
||||
if use_array_serializer?
|
||||
ArraySerializer
|
||||
else
|
||||
|
||||
@ -8,8 +8,8 @@ module ActiveModel
|
||||
@key ||= "#{name}_id"
|
||||
end
|
||||
|
||||
def serializer_class(object)
|
||||
serializer_from_options || serializer_from_object(object) || default_serializer
|
||||
def serializer_class(object, options = {})
|
||||
serializer_from_options || serializer_from_object(object, options) || default_serializer
|
||||
end
|
||||
|
||||
def build_serializer(object, options = {})
|
||||
|
||||
7
test/fixtures/poro.rb
vendored
7
test/fixtures/poro.rb
vendored
@ -135,3 +135,10 @@ end
|
||||
class VideoSerializer < ActiveModel::Serializer
|
||||
attributes :html
|
||||
end
|
||||
|
||||
class ShortProfileSerializer < ::ProfileSerializer; end
|
||||
|
||||
module TestNamespace
|
||||
class ProfileSerializer < ::ProfileSerializer; end
|
||||
class UserSerializer < ::UserSerializer; end
|
||||
end
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActionController
|
||||
module Serialization
|
||||
class NamespacedSerializationTest < ActionController::TestCase
|
||||
class TestNamespace::MyController < ActionController::Base
|
||||
def render_profile_with_namespace
|
||||
render json: Profile.new({ name: 'Name 1', description: 'Description 1'})
|
||||
end
|
||||
|
||||
def render_profiles_with_namespace
|
||||
render json: [Profile.new({ name: 'Name 1', description: 'Description 1'})]
|
||||
end
|
||||
|
||||
def render_comment
|
||||
render json: Comment.new(content: 'Comment 1')
|
||||
end
|
||||
|
||||
def render_comments
|
||||
render json: [Comment.new(content: 'Comment 1')]
|
||||
end
|
||||
end
|
||||
|
||||
tests TestNamespace::MyController
|
||||
|
||||
def test_render_profile_with_namespace
|
||||
get :render_profile_with_namespace
|
||||
assert_serializer TestNamespace::ProfileSerializer
|
||||
end
|
||||
|
||||
def test_render_profiles_with_namespace
|
||||
get :render_profiles_with_namespace
|
||||
assert_serializer TestNamespace::ProfileSerializer
|
||||
end
|
||||
|
||||
def test_fallback_to_a_version_without_namespace
|
||||
get :render_comment
|
||||
assert_serializer CommentSerializer
|
||||
end
|
||||
|
||||
def test_array_fallback_to_a_version_without_namespace
|
||||
get :render_comments
|
||||
assert_serializer CommentSerializer
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -7,6 +7,7 @@ module ActiveModel
|
||||
def setup
|
||||
@association = Association::HasOne.new('post', serializer: PostSerializer)
|
||||
@post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' })
|
||||
@user = User.new
|
||||
end
|
||||
|
||||
def test_build_serializer_for_array_called_twice
|
||||
@ -15,6 +16,20 @@ module ActiveModel
|
||||
assert_instance_of(PostSerializer, serializer)
|
||||
end
|
||||
end
|
||||
|
||||
def test_build_serializer_from_in_a_namespace
|
||||
assoc = Association::HasOne.new('profile')
|
||||
serializer = TestNamespace::UserSerializer.new(@user).build_serializer(assoc)
|
||||
|
||||
assert_instance_of(TestNamespace::ProfileSerializer, serializer)
|
||||
end
|
||||
|
||||
def test_build_serializer_with_prefix
|
||||
assoc = Association::HasOne.new('profile', prefix: :short)
|
||||
serializer = UserSerializer.new(@user).build_serializer(assoc)
|
||||
|
||||
assert_instance_of(ShortProfileSerializer, serializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user