Skip to content

Commit

Permalink
--wip--
Browse files Browse the repository at this point in the history
  • Loading branch information
Артём Большаков committed Jul 6, 2015
1 parent 4b75b34 commit 3806e36
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 50 deletions.
76 changes: 53 additions & 23 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class << self
end

def self.inherited(base)
base._attributes = self._attributes.try(:dup) || []
base._attributes = self._attributes.try(:dup) || []
base._attributes_keys = self._attributes_keys.try(:dup) || {}
base._associations = self._associations.try(:dup) || {}
base._associations = self._associations.try(:dup) || []
base._urls = []
serializer_file = File.open(caller.first[/^[^:]+/])
base._cache_digest = Digest::MD5.hexdigest(serializer_file.read)
Expand All @@ -45,7 +45,7 @@ def self.attributes(*attrs)

def self.attribute(attr, options = {})
key = options.fetch(:key, attr)
@_attributes_keys[attr] = {key: key} if key != attr
@_attributes_keys[attr] = { key: key } if key != attr
@_attributes << key unless @_attributes.include?(key)
define_method key do
object.read_attribute_for_serialization(attr)
Expand All @@ -58,21 +58,27 @@ def self.fragmented(serializer)

# Enables a serializer to be automatically cached
def self.cache(options = {})
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
@_cache_key = options.delete(:key)
@_cache_only = options.delete(:only)
@_cache_except = options.delete(:except)
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
@_cache_key = options.delete(:key)
@_cache_only = options.delete(:only)
@_cache_except = options.delete(:except)
@_cache_options = (options.empty?) ? nil : options
end

Association = Struct.new(:name, :options)
SingularAssociation = Class.new(Association)
CollectionAssociation = Class.new(Association)

# Defines an association in the object should be rendered.
#
# The serializer object should implement the association name
# as a method which should return an array when invoked. If a method
# with the association name does not exist, the association name is
# dispatched to the serialized object.
def self.has_many(*attrs)
associate(:has_many, attrs)
associate attrs do |name, options|
CollectionAssociation.new(name, options)
end
end

# Defines an association in the object that should be rendered.
Expand All @@ -82,7 +88,9 @@ def self.has_many(*attrs)
# with the association name does not exist, the association name is
# dispatched to the serialized object.
def self.belongs_to(*attrs)
associate(:belongs_to, attrs)
associate attrs do |name, options|
SingularAssociation.new(name, options)
end
end

# Defines an association in the object should be rendered.
Expand All @@ -92,21 +100,24 @@ def self.belongs_to(*attrs)
# with the association name does not exist, the association name is
# dispatched to the serialized object.
def self.has_one(*attrs)
associate(:has_one, attrs)
associate attrs do |name, options|
SingularAssociation.new(name, options)
end
end

def self.associate(type, attrs) #:nodoc:
# has_one :author, :image, only: :object
def self.associate(attrs, &block) #:nodoc:
options = attrs.extract_options!
self._associations = _associations.dup

attrs.each do |attr|
unless method_defined?(attr)
define_method attr do
object.send attr
attrs.each do |name|
unless method_defined?(name)
define_method name do
object.send name
end
end

self._associations[attr] = {type: type, association_options: options}
self._associations << block.call(name, options)
end
end

Expand Down Expand Up @@ -198,31 +209,50 @@ def attributes(options = {})
end
end

def associations
Enumerator.new do |y|
self.class._associations.dup.each do |association|
y.yield association
end
end
end

# @return [Enumerable]
#
def associations

end
require 'pp'
def each_association(&block)
return unless object
return unless block_given?

self.class._associations.dup.each do |name, association_options|
association_value = send(name)
# self._associations[attr] = {type: type, association_options: options}
# @param [Hash<Symbol,Hash<Symbol,Object>] before
#
self.class._associations.dup.each do |association|
association_options = association.options

association_value = send(association.name)

serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association.options)

if serializer_class
begin
serializer = serializer_class.new(
association_value,
options.except(:serializer).merge(serializer_from_options(association_options))
options.except(:serializer).merge(serializer_from_options(association.options))
)
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError
virtual_value = association_value
virtual_value = virtual_value.as_json if virtual_value.respond_to?(:as_json)
association_options[:association_options][:virtual_value] = virtual_value
association.options[:virtual_value] = virtual_value
end
elsif !association_value.nil? && !association_value.instance_of?(Object)
association_options[:association_options][:virtual_value] = association_value
association.options[:virtual_value] = association_value
end

block.call(name, serializer, association_options[:association_options])
block.call(association.name, serializer, association.options)
end
end

Expand Down
6 changes: 3 additions & 3 deletions test/adapter/json_api/has_many_explicit_serializer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class JsonApi
class HasManyExplicitSerializerTest < Minitest::Test
def setup
@post = Post.new(title: 'New Post', body: 'Body')
@author = Author.new(name: 'Jane Blogger')
@author = Author.new(name: 'Jane Blogger', roles: [], bio: nil)
@author.posts = [@post]
@post.author = @author
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
Expand Down Expand Up @@ -58,9 +58,9 @@ def test_includes_linked_data
},
{
id: @author.id.to_s,
type: "authors",
type: 'authors',
relationships: {
posts: { data: [ {type: "posts", id: @post.id.to_s } ] }
posts: { data: [ {type: 'posts', id: @post.id.to_s } ] }
}
}
]
Expand Down
41 changes: 17 additions & 24 deletions test/serializers/associations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,22 @@ def setup
@post.author = @author
@author.posts = [@post]

@post_serializer = PostSerializer.new(@post, {custom_options: true})
@post_serializer = PostSerializer.new(@post, {custom_options: true})
@author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment)
end

def test_has_many_and_has_one
assert_equal(
{ posts: { type: :has_many, association_options: { embed: :ids } },
roles: { type: :has_many, association_options: { embed: :ids } },
bio: { type: :has_one, association_options: {} } },
@author_serializer.class._associations
)
@author_serializer.each_association do |name, serializer, options|
if name == :posts
case name
when :posts
assert_equal({embed: :ids}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer)
elsif name == :bio
when :bio
assert_equal({}, options)
assert_nil serializer
elsif name == :roles
when :roles
assert_equal({embed: :ids}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer)
else
Expand All @@ -84,16 +80,12 @@ def test_serializer_options_are_passed_into_associations_serializers
end

def test_belongs_to
assert_equal(
{ post: { type: :belongs_to, association_options: {} },
author: { type: :belongs_to, association_options: {} } },
@comment_serializer.class._associations
)
@comment_serializer.each_association do |name, serializer, options|
if name == :post
case name
when :post
assert_equal({}, options)
assert_kind_of(PostSerializer, serializer)
elsif name == :author
when :author
assert_equal({}, options)
assert_nil serializer
else
Expand Down Expand Up @@ -122,15 +114,16 @@ def test_associations_inheritance_with_new_association
inherited_klass = Class.new(PostSerializer) do
has_many :top_comments, serializer: CommentSerializer
end
expected_associations = PostSerializer._associations.merge(
top_comments: {
type: :has_many,
association_options: {
serializer: CommentSerializer
}
}

inherited_associations = inherited_klass._associations
parent_associations = PostSerializer._associations

assert(
parent_associations.all? do |association|
inherited_associations.include?(association)
end
)
assert_equal(inherited_klass._associations, expected_associations)
assert(inherited_associations.map(&:name).include?(:top_comments))
end
end
end
Expand Down

0 comments on commit 3806e36

Please sign in to comment.