aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb162
-rw-r--r--activemodel/lib/active_model/validations.rb3
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb6
-rw-r--r--activemodel/test/cases/serializeration/xml_serialization_test.rb15
-rw-r--r--activemodel/test/models/contact.rb8
5 files changed, 79 insertions, 115 deletions
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index ee3e0eab06..df7026b3ec 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -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 @@ module ActiveModel
def initialize(name, serializable, raw_value=nil)
@name, @serializable = name, serializable
- @raw_value = raw_value || @serializable.send(name)
-
+ @value = value || @serializable.send(name)
@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 @@ module ActiveModel
# 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,10 +69,12 @@ module ActiveModel
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
@@ -115,80 +82,53 @@ module ActiveModel
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)
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 708557f4ae..c69cabc888 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -133,6 +133,9 @@ module ActiveModel
_validators[attribute.to_sym]
end
+ def attribute_method?(attribute)
+ method_defined?(attribute)
+ end
private
def _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 0423fcd17f..fbd622eb6d 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -14,8 +14,10 @@ module ActiveModel
def setup(klass)
# Note: instance_methods.map(&:to_s) is important for 1.9 compatibility
# as instance_methods returns symbols unlike 1.8 which returns strings.
- new_attributes = attributes.reject { |name| klass.instance_methods.map(&:to_s).include?("#{name}=") }
- klass.send(:attr_accessor, *new_attributes)
+ attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
+ attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
+ klass.send(:attr_reader, *attr_readers)
+ klass.send(:attr_writer, *attr_writers)
end
end
diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb
index 6340aad531..3ba826a8d0 100644
--- a/activemodel/test/cases/serializeration/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb
@@ -1,6 +1,7 @@
require 'cases/helper'
require 'models/contact'
require 'active_support/core_ext/object/instance_variables'
+require 'ostruct'
class Contact
extend ActiveModel::Naming
@@ -23,7 +24,9 @@ class XmlSerializationTest < ActiveModel::TestCase
@contact.age = 25
@contact.created_at = Time.utc(2006, 8, 1)
@contact.awesome = false
- @contact.preferences = { :gem => 'ruby' }
+ customer = OpenStruct.new
+ customer.name = "John"
+ @contact.preferences = customer
end
test "should serialize default root" do
@@ -92,8 +95,16 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<awesome type=\"boolean\">false</awesome>}, @contact.to_xml
end
+ test "should serialize array" do
+ assert_match %r{<social type=\"array\">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(:methods => :social)
+ end
+
+ test "should serialize hash" do
+ assert_match %r{<network>\s*<git type=\"symbol\">github</git>\s*</network>}, @contact.to_xml(:methods => :network)
+ end
+
test "should serialize yaml" do
- assert_match %r{<preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>}, @contact.to_xml
+ assert_match %r{<preferences type=\"yaml\">--- !ruby/object:OpenStruct \ntable:\s*:name: John\n</preferences>}, @contact.to_xml
end
test "should call proc on object" do
diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb
index a9009fbdef..605e435f39 100644
--- a/activemodel/test/models/contact.rb
+++ b/activemodel/test/models/contact.rb
@@ -3,6 +3,14 @@ class Contact
attr_accessor :id, :name, :age, :created_at, :awesome, :preferences
+ def social
+ %w(twitter github)
+ end
+
+ def network
+ {:git => :github}
+ end
+
def initialize(options = {})
options.each { |name, value| send("#{name}=", value) }
end