Commit
…, which both Hash and Array depends on. Also, refactored ActiveModel serializers to just use ActiveSupport::XmlMini.to_tag. As consequence, if a serialized attribute is an array or a hash, it's not encoded as yaml, but as a hash or array.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
require 'active_support/core_ext/array/wrap' | ||
require 'active_support/core_ext/class/attribute_accessors' | ||
require 'active_support/core_ext/array/conversions' | ||
require 'active_support/core_ext/hash/conversions' | ||
require 'active_support/core_ext/hash/slice' | ||
|
||
|
@@ -15,65 +16,29 @@ class Attribute #:nodoc: | |
|
||
def initialize(name, serializable, raw_value=nil) | ||
@name, @serializable = name, serializable | ||
@raw_value = raw_value || @serializable.send(name) | ||
|
||
@value = value || @serializable.send(name) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
farnoy
|
||
@type = compute_type | ||
@value = compute_value | ||
end | ||
|
||
# There is a significant speed improvement if the value | ||
# does not need to be escaped, as <tt>tag!</tt> escapes all values | ||
# to ensure that valid XML is generated. For known binary | ||
# values, it is at least an order of magnitude faster to | ||
# Base64 encode binary values and directly put them in the | ||
# output XML than to pass the original value or the Base64 | ||
# encoded value to the <tt>tag!</tt> method. It definitely makes | ||
# no sense to Base64 encode the value and then give it to | ||
# <tt>tag!</tt>, since that just adds additional overhead. | ||
def needs_encoding? | ||
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type) | ||
end | ||
|
||
def decorations(include_types = true) | ||
def decorations | ||
decorations = {} | ||
|
||
if type == :binary | ||
decorations[:encoding] = 'base64' | ||
end | ||
|
||
if include_types && type != :string | ||
decorations[:type] = type | ||
end | ||
|
||
if value.nil? | ||
decorations[:nil] = true | ||
end | ||
|
||
decorations[:encoding] = 'base64' if type == :binary | ||
decorations[:type] = type unless type == :string | ||
decorations[:nil] = true if value.nil? | ||
decorations | ||
end | ||
|
||
protected | ||
def compute_type | ||
type = Hash::XML_TYPE_NAMES[@raw_value.class.name] | ||
type ||= :string if @raw_value.respond_to?(:to_str) | ||
type ||= :yaml | ||
type | ||
end | ||
protected | ||
|
||
def compute_value | ||
if formatter = Hash::XML_FORMATTING[type.to_s] | ||
@raw_value ? formatter.call(@raw_value) : nil | ||
else | ||
@raw_value | ||
end | ||
end | ||
def compute_type | ||
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] | ||
type ||= :string if value.respond_to?(:to_str) | ||
type ||= :yaml | ||
type | ||
end | ||
end | ||
|
||
class MethodAttribute < Attribute #:nodoc: | ||
protected | ||
def compute_type | ||
Hash::XML_TYPE_NAMES[@raw_value.class.name] || :string | ||
end | ||
end | ||
|
||
attr_reader :options | ||
|
@@ -92,7 +57,7 @@ def initialize(serializable, options = nil) | |
# 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_attributes_hash | ||
def attributes_hash | ||
attributes = @serializable.attributes | ||
if options[:only].any? | ||
attributes.slice(*options[:only]) | ||
|
@@ -104,91 +69,66 @@ def serializable_attributes_hash | |
end | ||
|
||
def serializable_attributes | ||
serializable_attributes_hash.map { |name, value| self.class::Attribute.new(name, @serializable, value) } | ||
attributes_hash.map do |name, value| | ||
self.class::Attribute.new(name, @serializable, value) | ||
end | ||
end | ||
|
||
def serializable_method_attributes | ||
def serializable_methods | ||
Array.wrap(options[:methods]).inject([]) do |methods, name| | ||
methods << self.class::MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s) | ||
methods | ||
end | ||
end | ||
|
||
def serialize | ||
args = [root] | ||
|
||
if options[:namespace] | ||
args << {:xmlns => options[:namespace]} | ||
end | ||
require 'builder' unless defined? ::Builder | ||
|
||
if options[:type] | ||
args << {:type => options[:type]} | ||
end | ||
|
||
builder.tag!(*args) do | ||
add_attributes | ||
procs = options.delete(:procs) | ||
options[:procs] = procs | ||
add_procs | ||
yield builder if block_given? | ||
end | ||
end | ||
options[:indent] ||= 2 | ||
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) | ||
|
||
private | ||
def builder | ||
@builder ||= begin | ||
require 'builder' unless defined? ::Builder | ||
options[:indent] ||= 2 | ||
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) | ||
@builder = options[:builder] | ||
@builder.instruct! unless options[:skip_instruct] | ||
|
||
unless options[:skip_instruct] | ||
builder.instruct! | ||
options[:skip_instruct] = true | ||
end | ||
root = (options[:root] || @serializable.class.model_name.singular).to_s | ||
root = ActiveSupport::XmlMini.rename_key(root, options) | ||
|
||
builder | ||
end | ||
end | ||
|
||
def root | ||
root = (options[:root] || @serializable.class.model_name.singular).to_s | ||
reformat_name(root) | ||
end | ||
args = [root] | ||
args << {:xmlns => options[:namespace]} if options[:namespace] | ||
args << {:type => options[:type]} if options[:type] && !options[:skip_types] | ||
|
||
def dasherize? | ||
!options.has_key?(:dasherize) || options[:dasherize] | ||
@builder.tag!(*args) do | ||
add_attributes_and_methods | ||
add_extra_behavior | ||
add_procs | ||
yield @builder if block_given? | ||
end | ||
end | ||
|
||
def camelize? | ||
options.has_key?(:camelize) && options[:camelize] | ||
end | ||
private | ||
|
||
def reformat_name(name) | ||
name = name.camelize if camelize? | ||
dasherize? ? name.dasherize : name | ||
end | ||
def add_extra_behavior | ||
end | ||
|
||
def add_attributes | ||
(serializable_attributes + serializable_method_attributes).each do |attribute| | ||
builder.tag!( | ||
reformat_name(attribute.name), | ||
attribute.value.to_s, | ||
attribute.decorations(!options[:skip_types]) | ||
) | ||
end | ||
def add_attributes_and_methods | ||
(serializable_attributes + serializable_methods).each do |attribute| | ||
key = ActiveSupport::XmlMini.rename_key(attribute.name, options) | ||
ActiveSupport::XmlMini.to_tag(key, attribute.value, | ||
options.merge(attribute.decorations)) | ||
end | ||
end | ||
|
||
def add_procs | ||
if procs = options.delete(:procs) | ||
[ *procs ].each do |proc| | ||
if proc.arity > 1 | ||
proc.call(options, @serializable) | ||
else | ||
proc.call(options) | ||
end | ||
def add_procs | ||
if procs = options.delete(:procs) | ||
Array.wrap(procs).each do |proc| | ||
if proc.arity == 1 | ||
proc.call(options) | ||
else | ||
proc.call(options, @serializable) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
def to_xml(options = {}, &block) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,16 +182,31 @@ def initialize(*args) | |
options[:except] |= Array.wrap(@serializable.class.inheritance_column) | ||
end | ||
|
||
def add_extra_behavior | ||
add_includes | ||
end | ||
|
||
def add_includes | ||
procs = options.delete(:procs) | ||
@serializable.send(:serializable_add_includes, options) do |association, records, opts| | ||
add_associations(association, records, opts) | ||
end | ||
options[:procs] = procs | ||
end | ||
|
||
# TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well. | ||
def add_associations(association, records, opts) | ||
association_name = association.to_s.singularize | ||
merged_options = options.merge(opts).merge!(:root => association_name) | ||
|
||
if records.is_a?(Enumerable) | ||
tag = reformat_name(association.to_s) | ||
type = options[:skip_types] ? {} : {:type => "array"} | ||
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options) | ||
type = options[:skip_types] ? { } : {:type => "array"} | ||
|
||
if records.empty? | ||
builder.tag!(tag, type) | ||
@builder.tag!(tag, type) | ||
else | ||
builder.tag!(tag, type) do | ||
association_name = association.to_s.singularize | ||
@builder.tag!(tag, type) do | ||
records.each do |record| | ||
if options[:skip_types] | ||
record_type = {} | ||
|
@@ -200,60 +215,30 @@ def add_associations(association, records, opts) | |
record_type = {:type => record_class} | ||
end | ||
|
||
record.to_xml opts.merge(:root => association_name).merge(record_type) | ||
record.to_xml merged_options.merge(record_type) | ||
end | ||
end | ||
end | ||
else | ||
if record = @serializable.send(association) | ||
record.to_xml(opts.merge(:root => association)) | ||
end | ||
end | ||
end | ||
|
||
def serialize | ||
args = [root] | ||
if options[:namespace] | ||
args << {:xmlns=>options[:namespace]} | ||
end | ||
|
||
if options[:type] | ||
args << {:type=>options[:type]} | ||
end | ||
|
||
builder.tag!(*args) do | ||
add_attributes | ||
procs = options.delete(:procs) | ||
@serializable.send(:serializable_add_includes, options) { |association, records, opts| | ||
add_associations(association, records, opts) | ||
} | ||
options[:procs] = procs | ||
add_procs | ||
yield builder if block_given? | ||
elsif record = @serializable.send(association) | ||
record.to_xml(merged_options) | ||
end | ||
end | ||
|
||
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: | ||
protected | ||
def compute_type | ||
type = @serializable.class.serialized_attributes.has_key?(name) ? :yaml : @serializable.class.columns_hash[name].type | ||
|
||
case type | ||
when :text | ||
:string | ||
when :time | ||
:datetime | ||
else | ||
type | ||
end | ||
end | ||
end | ||
def compute_type | ||
type = @serializable.class.serialized_attributes.has_key?(name) ? | ||
super : @serializable.class.columns_hash[name].type | ||
This comment has been minimized.
Sorry, something went wrong.
knoopx
Contributor
|
||
|
||
class MethodAttribute < Attribute #:nodoc: | ||
protected | ||
def compute_type | ||
Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string | ||
case type | ||
when :text | ||
:string | ||
when :time | ||
:datetime | ||
else | ||
type | ||
end | ||
end | ||
protected :compute_type | ||
end | ||
end | ||
end |
Shouldn't this line be
@value = raw_value || @serializable.send(name)
or change the name of the argument from "raw_value" to "value"?