diff options
Diffstat (limited to 'activemodel')
-rw-r--r-- | activemodel/CHANGELOG.md | 4 | ||||
-rw-r--r-- | activemodel/README.rdoc | 9 | ||||
-rw-r--r-- | activemodel/lib/active_model.rb | 1 | ||||
-rw-r--r-- | activemodel/lib/active_model/attribute_methods.rb | 8 | ||||
-rw-r--r-- | activemodel/lib/active_model/serialization.rb | 3 | ||||
-rw-r--r-- | activemodel/lib/active_model/serializers/xml.rb | 238 | ||||
-rw-r--r-- | activemodel/test/cases/serializers/xml_serialization_test.rb | 251 | ||||
-rw-r--r-- | activemodel/test/models/contact.rb | 1 |
8 files changed, 9 insertions, 506 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index dddfd940bb..f25c901ecc 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -3,6 +3,10 @@ *Jay Elaraj* +* Remove `ActiveModel::Serializers::Xml` from core. + + *Zachary Scott* + * Add `ActiveModel::Dirty#[attr_name]_previously_changed?` and `ActiveModel::Dirty#[attr_name]_previous_change` to improve access to recorded changes after the model has been saved. diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc index d954467387..20414c1d61 100644 --- a/activemodel/README.rdoc +++ b/activemodel/README.rdoc @@ -155,7 +155,7 @@ behavior out of the box: * Making objects serializable <tt>ActiveModel::Serialization</tt> provides a standard interface for your object - to provide +to_json+ or +to_xml+ serialization. + to provide +to_json+ serialization. class SerialPerson include ActiveModel::Serialization @@ -177,13 +177,6 @@ behavior out of the box: s = SerialPerson.new s.to_json # => "{\"name\":null}" - class SerialPerson - include ActiveModel::Serializers::Xml - end - - s = SerialPerson.new - s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person... - {Learn more}[link:classes/ActiveModel/Serialization.html] * Internationalization (i18n) support diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 8aa1b6f664..4e1b3f7495 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -58,7 +58,6 @@ module ActiveModel eager_autoload do autoload :JSON - autoload :Xml end end diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 9033ec0dca..77e4ce3afe 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -342,7 +342,7 @@ module ActiveModel private # The methods +method_missing+ and +respond_to?+ of this module are # invoked often in a typical rails, both of which invoke the method - # +match_attribute_method?+. The latter method iterates through an + # +matched_attribute_method+. The latter method iterates through an # array doing regular expression matches, which results in a lot of # object creations. Most of the time it returns a +nil+ match. As the # match result is always the same given a +method_name+, this cache is @@ -429,7 +429,7 @@ module ActiveModel if respond_to_without_attributes?(method, true) super else - match = match_attribute_method?(method.to_s) + match = matched_attribute_method(method.to_s) match ? attribute_missing(match, *args, &block) : super end end @@ -454,7 +454,7 @@ module ActiveModel # but found among all methods. Which means that the given method is private. false else - !match_attribute_method?(method.to_s).nil? + !matched_attribute_method(method.to_s).nil? end end @@ -466,7 +466,7 @@ module ActiveModel private # Returns a struct representing the matching attribute method. # The struct's attributes are prefix, base and suffix. - def match_attribute_method?(method_name) + def matched_attribute_method(method_name) matches = self.class.send(:attribute_method_matchers_matching, method_name) matches.detect { |match| attribute_method?(match.attr_name) } end diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index f95849eb84..58d68d9620 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -40,7 +40,6 @@ module ActiveModel # # class Person # include ActiveModel::Serializers::JSON - # include ActiveModel::Serializers::Xml # # attr_accessor :name # @@ -55,13 +54,11 @@ module ActiveModel # person.serializable_hash # => {"name"=>nil} # person.as_json # => {"name"=>nil} # person.to_json # => "{\"name\":null}" - # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person... # # person.name = "Bob" # person.serializable_hash # => {"name"=>"Bob"} # person.as_json # => {"name"=>"Bob"} # person.to_json # => "{\"name\":\"Bob\"}" - # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person... # # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and # <tt>:include</tt>. The following are all valid examples: diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb deleted file mode 100644 index e33c766627..0000000000 --- a/activemodel/lib/active_model/serializers/xml.rb +++ /dev/null @@ -1,238 +0,0 @@ -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/core_ext/array/conversions' -require 'active_support/core_ext/hash/conversions' -require 'active_support/core_ext/hash/slice' -require 'active_support/core_ext/time/acts_like' - -module ActiveModel - module Serializers - # == \Active \Model XML Serializer - module Xml - extend ActiveSupport::Concern - include ActiveModel::Serialization - - included do - extend ActiveModel::Naming - end - - class Serializer #:nodoc: - class Attribute #:nodoc: - attr_reader :name, :value, :type - - def initialize(name, serializable, value) - @name, @serializable = name, serializable - - if value.acts_like?(:time) && value.respond_to?(:in_time_zone) - value = value.in_time_zone - end - - @value = value - @type = compute_type - end - - def decorations - decorations = {} - decorations[:encoding] = 'base64' if type == :binary - decorations[:type] = (type == :string) ? nil : type - decorations[:nil] = true if value.nil? - decorations - end - - protected - - def compute_type - return if value.nil? - 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: - end - - attr_reader :options - - def initialize(serializable, options = nil) - @serializable = serializable - @options = options ? options.dup : {} - end - - def serializable_hash - @serializable.serializable_hash(@options.except(:include)) - end - - def serializable_collection - methods = Array(options[:methods]).map(&:to_s) - serializable_hash.map do |name, value| - name = name.to_s - if methods.include?(name) - self.class::MethodAttribute.new(name, @serializable, value) - else - self.class::Attribute.new(name, @serializable, value) - end - end - end - - def serialize - require 'builder' unless defined? ::Builder - - options[:indent] ||= 2 - options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent]) - - @builder = options[:builder] - @builder.instruct! unless options[:skip_instruct] - - root = (options[:root] || @serializable.model_name.element).to_s - root = ActiveSupport::XmlMini.rename_key(root, options) - - args = [root] - args << { xmlns: options[:namespace] } if options[:namespace] - args << { type: options[:type] } if options[:type] && !options[:skip_types] - - @builder.tag!(*args) do - add_attributes_and_methods - add_includes - add_extra_behavior - add_procs - yield @builder if block_given? - end - end - - private - - def add_extra_behavior - end - - def add_attributes_and_methods - serializable_collection.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_includes - @serializable.send(:serializable_add_includes, options) do |association, records, opts| - add_associations(association, records, opts) - end - end - - # TODO: This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well. - def add_associations(association, records, opts) - merged_options = opts.merge(options.slice(:builder, :indent)) - merged_options[:skip_instruct] = true - - [:skip_types, :dasherize, :camelize].each do |key| - merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil? - end - - if records.respond_to?(:to_ary) - records = records.to_ary - - tag = ActiveSupport::XmlMini.rename_key(association.to_s, options) - type = options[:skip_types] ? { } : { type: "array" } - association_name = association.to_s.singularize - merged_options[:root] = association_name - - if records.empty? - @builder.tag!(tag, type) - else - @builder.tag!(tag, type) do - records.each do |record| - if options[:skip_types] - record_type = {} - else - record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name - record_type = { type: record_class } - end - - record.to_xml merged_options.merge(record_type) - end - end - end - else - merged_options[:root] = association.to_s - - unless records.class.to_s.underscore == association.to_s - merged_options[:type] = records.class.name - end - - records.to_xml merged_options - end - end - - def add_procs - if procs = options.delete(:procs) - Array(procs).each do |proc| - if proc.arity == 1 - proc.call(options) - else - proc.call(options, @serializable) - end - end - end - end - end - - # Returns XML representing the model. Configuration can be - # passed through +options+. - # - # Without any +options+, the returned XML string will include all the - # model's attributes. - # - # user = User.find(1) - # user.to_xml - # - # <?xml version="1.0" encoding="UTF-8"?> - # <user> - # <id type="integer">1</id> - # <name>David</name> - # <age type="integer">16</age> - # <created-at type="dateTime">2011-01-30T22:29:23Z</created-at> - # </user> - # - # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the - # attributes included, and work similar to the +attributes+ method. - # - # To include the result of some method calls on the model use <tt>:methods</tt>. - # - # To include associations use <tt>:include</tt>. - # - # For further documentation, see <tt>ActiveRecord::Serialization#to_xml</tt> - def to_xml(options = {}, &block) - Serializer.new(self, options).serialize(&block) - end - - # Sets the model +attributes+ from an XML string. Returns +self+. - # - # class Person - # include ActiveModel::Serializers::Xml - # - # attr_accessor :name, :age, :awesome - # - # def attributes=(hash) - # hash.each do |key, value| - # instance_variable_set("@#{key}", value) - # end - # end - # - # def attributes - # instance_values - # end - # end - # - # xml = { name: 'bob', age: 22, awesome:true }.to_xml - # person = Person.new - # person.from_xml(xml) # => #<Person:0x007fec5e3b3c40 @age=22, @awesome=true, @name="bob"> - # person.name # => "bob" - # person.age # => 22 - # person.awesome # => true - def from_xml(xml) - self.attributes = Hash.from_xml(xml).values.first - self - end - end - end -end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb deleted file mode 100644 index 37faf6cef8..0000000000 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'cases/helper' -require 'models/contact' -require 'active_support/core_ext/object/instance_variables' -require 'ostruct' -require 'yaml' - -module Admin - class Contact < ::Contact - end -end - -class Customer < Struct.new(:name) -end - -class Address - include ActiveModel::Serializers::Xml - - attr_accessor :street, :city, :state, :zip, :apt_number - - def attributes - instance_values - end -end - -class SerializableContact < Contact - def serializable_hash(options={}) - super(options.merge(only: [:name, :age])) - end -end - -class XmlSerializationTest < ActiveModel::TestCase - def setup - @contact = Contact.new - @contact.name = 'aaron stack' - @contact.age = 25 - @contact.created_at = Time.utc(2006, 8, 1) - @contact.awesome = false - customer = Customer.new - customer.name = "John" - @contact.preferences = customer - @contact.address = Address.new - @contact.address.city = "Springfield" - @contact.address.apt_number = 35 - @contact.friends = [Contact.new, Contact.new] - @contact.contact = SerializableContact.new - end - - test "should serialize default root" do - xml = @contact.to_xml - assert_match %r{^<contact>}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize namespaced root" do - xml = Admin::Contact.new(@contact.attributes).to_xml - assert_match %r{^<contact>}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize default root with namespace" do - xml = @contact.to_xml namespace: "http://xml.rubyonrails.org/contact" - assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize custom root" do - xml = @contact.to_xml root: 'xml_contact' - assert_match %r{^<xml-contact>}, xml - assert_match %r{</xml-contact>$}, xml - end - - test "should allow undasherized tags" do - xml = @contact.to_xml root: 'xml_contact', dasherize: false - assert_match %r{^<xml_contact>}, xml - assert_match %r{</xml_contact>$}, xml - assert_match %r{<created_at}, xml - end - - test "should allow camelized tags" do - xml = @contact.to_xml root: 'xml_contact', camelize: true - assert_match %r{^<XmlContact>}, xml - assert_match %r{</XmlContact>$}, xml - assert_match %r{<CreatedAt}, xml - end - - test "should allow lower-camelized tags" do - xml = @contact.to_xml root: 'xml_contact', camelize: :lower - assert_match %r{^<xmlContact>}, xml - assert_match %r{</xmlContact>$}, xml - assert_match %r{<createdAt}, xml - end - - test "should use serializable hash" do - @contact = SerializableContact.new - @contact.name = 'aaron stack' - @contact.age = 25 - - xml = @contact.to_xml - assert_match %r{<name>aaron stack</name>}, xml - assert_match %r{<age type="integer">25</age>}, xml - assert_no_match %r{<awesome>}, xml - end - - test "should allow skipped types" do - xml = @contact.to_xml skip_types: true - assert_match %r{<age>25</age>}, xml - end - - test "should include yielded additions" do - xml_output = @contact.to_xml do |xml| - xml.creator "David" - end - assert_match %r{<creator>David</creator>}, xml_output - end - - test "should serialize string" do - assert_match %r{<name>aaron stack</name>}, @contact.to_xml - end - - test "should serialize nil" do - assert_match %r{<pseudonyms nil="true"/>}, @contact.to_xml(methods: :pseudonyms) - end - - test "should serialize integer" do - assert_match %r{<age type="integer">25</age>}, @contact.to_xml - end - - test "should serialize datetime" do - assert_match %r{<created-at type="dateTime">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml - end - - test "should serialize boolean" do - 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">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml - end - - test "should call proc on object" do - proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') } - xml = @contact.to_xml(procs: [ proc ]) - assert_match %r{<nationality>unknown</nationality>}, xml - end - - test "should supply serializable to second proc argument" do - proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) } - xml = @contact.to_xml(procs: [ proc ]) - assert_match %r{<name-reverse>kcats noraa</name-reverse>}, xml - end - - test "should serialize string correctly when type passed" do - xml = @contact.to_xml type: 'Contact' - assert_match %r{<contact type="Contact">}, xml - assert_match %r{<name>aaron stack</name>}, xml - end - - test "include option with singular association" do - xml = @contact.to_xml include: :address, indent: 0 - assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true)) - end - - test "include option with plural association" do - xml = @contact.to_xml include: :friends, indent: 0 - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - class FriendList - def initialize(friends) - @friends = friends - end - - def to_ary - @friends - end - end - - test "include option with ary" do - @contact.friends = FriendList.new(@contact.friends) - xml = @contact.to_xml include: :friends, indent: 0 - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - test "multiple includes" do - xml = @contact.to_xml indent: 0, skip_instruct: true, include: [ :address, :friends ] - assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true)) - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - test "include with options" do - xml = @contact.to_xml indent: 0, skip_instruct: true, include: { address: { only: :city } } - assert xml.include?(%(><address><city>Springfield</city></address>)) - end - - test "propagates skip_types option to included associations" do - xml = @contact.to_xml include: :friends, indent: 0, skip_types: true - assert_match %r{<friends>}, xml - assert_match %r{<friend>}, xml - end - - test "propagates skip-types option to included associations and attributes" do - xml = @contact.to_xml skip_types: true, include: :address, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number>}, xml - end - - test "propagates camelize option to included associations and attributes" do - xml = @contact.to_xml camelize: true, include: :address, indent: 0 - assert_match %r{<Address>}, xml - assert_match %r{<AptNumber type="integer">}, xml - end - - test "propagates dasherize option to included associations and attributes" do - xml = @contact.to_xml dasherize: false, include: :address, indent: 0 - assert_match %r{<apt_number type="integer">}, xml - end - - test "don't propagate skip_types if skip_types is defined at the included association level" do - xml = @contact.to_xml skip_types: true, include: { address: { skip_types: false } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "don't propagate camelize if camelize is defined at the included association level" do - xml = @contact.to_xml camelize: true, include: { address: { camelize: false } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "don't propagate dasherize if dasherize is defined at the included association level" do - xml = @contact.to_xml dasherize: false, include: { address: { dasherize: true } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "association with sti" do - xml = @contact.to_xml(include: :contact) - assert xml.include?(%(<contact type="SerializableContact">)) - end -end diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb index bcfd267a34..113ab0bc1f 100644 --- a/activemodel/test/models/contact.rb +++ b/activemodel/test/models/contact.rb @@ -4,7 +4,6 @@ class Contact include ActiveModel::Validations include ActiveModel::Serializers::JSON - include ActiveModel::Serializers::Xml attr_accessor :id, :name, :age, :created_at, :awesome, :preferences attr_accessor :address, :friends, :contact |