diff options
Diffstat (limited to 'activemodel')
-rw-r--r-- | activemodel/lib/active_model/serializers/xml.rb | 162 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations.rb | 3 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/acceptance.rb | 6 | ||||
-rw-r--r-- | activemodel/test/cases/serializeration/xml_serialization_test.rb | 15 | ||||
-rw-r--r-- | activemodel/test/models/contact.rb | 8 |
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 |