Skip to content

Commit

Permalink
Break up concerns for choosing what attributes should be serialized a…
Browse files Browse the repository at this point in the history
…nd the actual serializer
  • Loading branch information
josh committed Aug 14, 2009
1 parent 7a26c21 commit c6bc8e6
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 163 deletions.
2 changes: 1 addition & 1 deletion activemodel/lib/active_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module ActiveModel
autoload :Naming, 'active_model/naming'
autoload :Observer, 'active_model/observing'
autoload :Observing, 'active_model/observing'
autoload :Serializer, 'active_model/serializer'
autoload :Serialization, 'active_model/serialization'
autoload :StateMachine, 'active_model/state_machine'
autoload :TestCase, 'active_model/test_case'
autoload :Validations, 'active_model/validations'
Expand Down
30 changes: 30 additions & 0 deletions activemodel/lib/active_model/serialization.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'

module ActiveModel
module Serialization
def serializable_hash(options = nil)
options ||= {}

options[:only] = Array.wrap(options[:only]).map { |n| n.to_s }
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }

attribute_names = attributes.keys.sort
if options[:only].any?
attribute_names &= options[:only]
elsif options[:except].any?
attribute_names -= options[:except]
end

method_names = Array.wrap(options[:methods]).inject([]) do |methods, name|
methods << name if respond_to?(name.to_s)
methods
end

(attribute_names + method_names).inject({}) { |hash, name|
hash[name] = send(name)
hash
}
end
end
end
60 changes: 0 additions & 60 deletions activemodel/lib/active_model/serializer.rb

This file was deleted.

18 changes: 4 additions & 14 deletions activemodel/lib/active_model/serializers/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,14 @@ module ActiveModel
module Serializers
module JSON
extend ActiveSupport::Concern
include ActiveModel::Serialization

included do
extend ActiveModel::Naming

cattr_accessor :include_root_in_json, :instance_writer => false
end

class Serializer < ActiveModel::Serializer
def serializable_hash
model = super
@serializable.include_root_in_json ?
{ @serializable.class.model_name.element => model } :
model
end

def serialize
ActiveSupport::JSON.encode(serializable_hash)
end
end

# Returns a JSON string representing the model. Some configuration is
# available through +options+.
#
Expand Down Expand Up @@ -92,7 +80,9 @@ def serialize
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def encode_json(encoder)
Serializer.new(self, encoder.options).to_s
hash = serializable_hash(encoder.options)
hash = { self.class.model_name.element => hash } if include_root_in_json
ActiveSupport::JSON.encode(hash)
end

def as_json(options = nil)
Expand Down
76 changes: 52 additions & 24 deletions activemodel/lib/active_model/serializers/xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ module ActiveModel
module Serializers
module Xml
extend ActiveSupport::Concern
include ActiveModel::Serialization

class Serializer < ActiveModel::Serializer #:nodoc:
class Serializer #:nodoc:
class Attribute #:nodoc:
attr_reader :name, :value, :type

Expand Down Expand Up @@ -74,32 +75,32 @@ def compute_type
end
end

def builder
@builder ||= begin
require 'builder' unless defined? ::Builder
options[:indent] ||= 2
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
attr_reader :options

unless options[:skip_instruct]
builder.instruct!
options[:skip_instruct] = true
end
def initialize(serializable, options = nil)
@serializable = serializable
@options = options ? options.dup : {}

builder
end
@options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
end

def root
root = (options[:root] || @serializable.class.model_name.singular).to_s
reformat_name(root)
end

def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end
# To replicate the behavior in ActiveRecord#attributes,
# <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
# for a N level model but is set for the N+1 level models,
# then because <tt>:except</tt> is set to a default value, the second
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
# <tt>:only</tt> is set, always delete <tt>:except</tt>.
def serializable_attribute_names
attribute_names = @serializable.attributes.keys.sort

if options[:only].any?
attribute_names &= options[:only]
elsif options[:except].any?
attribute_names -= options[:except]
end

def camelize?
options.has_key?(:camelize) && options[:camelize]
attribute_names
end

def serializable_attributes
Expand Down Expand Up @@ -134,6 +135,34 @@ def serialize
end

private
def builder
@builder ||= begin
require 'builder' unless defined? ::Builder
options[:indent] ||= 2
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])

unless options[:skip_instruct]
builder.instruct!
options[:skip_instruct] = true
end

builder
end
end

def root
root = (options[:root] || @serializable.class.model_name.singular).to_s
reformat_name(root)
end

def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end

def camelize?
options.has_key?(:camelize) && options[:camelize]
end

def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
Expand Down Expand Up @@ -163,8 +192,7 @@ def add_procs
end

def to_xml(options = {}, &block)
serializer = Serializer.new(self, options)
block_given? ? serializer.to_s(&block) : serializer.to_s
Serializer.new(self, options).serialize(&block)
end

def from_xml(xml)
Expand Down
80 changes: 39 additions & 41 deletions activerecord/lib/active_record/serialization.rb
Original file line number Diff line number Diff line change
@@ -1,60 +1,58 @@
module ActiveRecord #:nodoc:
module Serialization
module RecordSerializer #:nodoc:
def initialize(*args)
super
options[:except] |= Array.wrap(@serializable.class.inheritance_column)
extend ActiveSupport::Concern
include ActiveModel::Serializers::JSON

def serializable_hash(options = nil)
options ||= {}

options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
options[:except] |= Array.wrap(self.class.inheritance_column)

hash = super(options)

serializable_add_includes(options) do |association, records, opts|
hash[association] = records.is_a?(Enumerable) ?
records.map { |r| r.serializable_hash(opts) } :
records.serializable_hash(opts)
end

hash
end

private
# Add associations specified via the <tt>:includes</tt> option.
# Expects a block that takes as arguments:
# +association+ - name of the association
# +records+ - the association record(s) to be serialized
# +opts+ - options for the association records
def add_includes(&block)
if include_associations = options.delete(:include)
base_only_or_except = { :except => options[:except],
:only => options[:only] }

include_has_options = include_associations.is_a?(Hash)
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)

for association in associations
records = case @serializable.class.reflect_on_association(association).macro
when :has_many, :has_and_belongs_to_many
@serializable.send(association).to_a
when :has_one, :belongs_to
@serializable.send(association)
end

unless records.nil?
association_options = include_has_options ? include_associations[association] : base_only_or_except
opts = options.merge(association_options)
yield(association, records, opts)
end
end
def serializable_add_includes(options = {})
return unless include_associations = options.delete(:include)

options[:include] = include_associations
end
end
base_only_or_except = { :except => options[:except],
:only => options[:only] }

include_has_options = include_associations.is_a?(Hash)
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)

def serializable_hash
hash = super
for association in associations
records = case self.class.reflect_on_association(association).macro
when :has_many, :has_and_belongs_to_many
send(association).to_a
when :has_one, :belongs_to
send(association)
end

add_includes do |association, records, opts|
hash[association] =
if records.is_a?(Enumerable)
records.collect { |r| self.class.new(r, opts).serializable_hash }
else
self.class.new(records, opts).serializable_hash
end
unless records.nil?
association_options = include_has_options ? include_associations[association] : base_only_or_except
opts = options.merge(association_options)
yield(association, records, opts)
end
end

hash
options[:include] = include_associations
end
end
end
end

require 'active_record/serializers/xml_serializer'
require 'active_record/serializers/json_serializer'
14 changes: 0 additions & 14 deletions activerecord/lib/active_record/serializers/json_serializer.rb

This file was deleted.

Loading

0 comments on commit c6bc8e6

Please sign in to comment.