aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2011-11-23 23:18:13 +0000
committerJosé Valim <jose.valim@gmail.com>2011-11-23 23:18:15 +0000
commit8896b4fdc8a543157cdf4dfc378607ebf6c10ab0 (patch)
tree5deca0d54d2c103d0a9e6167a756d638fc25e9ec /activemodel/lib
parent0536ea8c7855222111fad6df71d0d09b77ea4317 (diff)
downloadrails-8896b4fdc8a543157cdf4dfc378607ebf6c10ab0.tar.gz
rails-8896b4fdc8a543157cdf4dfc378607ebf6c10ab0.tar.bz2
rails-8896b4fdc8a543157cdf4dfc378607ebf6c10ab0.zip
Implement ArraySerializer and move old serialization API to a new namespace.
The following constants were renamed: ActiveModel::Serialization => ActiveModel::Serializable ActiveModel::Serializers::JSON => ActiveModel::Serializable::JSON ActiveModel::Serializers::Xml => ActiveModel::Serializable::XML The main motivation for such a change is that `ActiveModel::Serializers::JSON` was not actually a serializer, but a module that when included allows the target to be serializable to JSON. With such changes, we were able to clean up the namespace to add true serializers as the ArraySerializer.
Diffstat (limited to 'activemodel/lib')
-rw-r--r--activemodel/lib/active_model.rb4
-rw-r--r--activemodel/lib/active_model/serializable.rb156
-rw-r--r--activemodel/lib/active_model/serializable/json.rb108
-rw-r--r--activemodel/lib/active_model/serializable/xml.rb195
-rw-r--r--activemodel/lib/active_model/serialization.rb139
-rw-r--r--activemodel/lib/active_model/serializer.rb60
-rw-r--r--activemodel/lib/active_model/serializers/json.rb102
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb191
8 files changed, 535 insertions, 420 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 28765b00bb..6c4fb44b0f 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -29,6 +29,7 @@ require 'active_model/version'
module ActiveModel
extend ActiveSupport::Autoload
+ autoload :ArraySerializer, 'active_model/serializer'
autoload :AttributeMethods
autoload :BlockValidator, 'active_model/validator'
autoload :Callbacks
@@ -43,8 +44,9 @@ module ActiveModel
autoload :Observer, 'active_model/observing'
autoload :Observing
autoload :SecurePassword
- autoload :Serializer
+ autoload :Serializable
autoload :Serialization
+ autoload :Serializer
autoload :TestCase
autoload :Translation
autoload :Validations
diff --git a/activemodel/lib/active_model/serializable.rb b/activemodel/lib/active_model/serializable.rb
new file mode 100644
index 0000000000..769e934dbe
--- /dev/null
+++ b/activemodel/lib/active_model/serializable.rb
@@ -0,0 +1,156 @@
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/array/wrap'
+
+module ActiveModel
+ # == Active Model Serializable
+ #
+ # Provides a basic serialization to a serializable_hash for your object.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ #
+ # include ActiveModel::Serializable
+ #
+ # attr_accessor :name
+ #
+ # def attributes
+ # {'name' => name}
+ # end
+ #
+ # end
+ #
+ # Which would provide you with:
+ #
+ # person = Person.new
+ # person.serializable_hash # => {"name"=>nil}
+ # person.name = "Bob"
+ # person.serializable_hash # => {"name"=>"Bob"}
+ #
+ # You need to declare some sort of attributes hash which contains the attributes
+ # you want to serialize and their current value.
+ #
+ # Most of the time though, you will want to include the JSON or XML
+ # serializations. Both of these modules automatically include the
+ # ActiveModel::Serialization module, so there is no need to explicitly
+ # include it.
+ #
+ # So a minimal implementation including XML and JSON would be:
+ #
+ # class Person
+ #
+ # include ActiveModel::Serializable::JSON
+ # include ActiveModel::Serializable::XML
+ #
+ # attr_accessor :name
+ #
+ # def attributes
+ # {'name' => name}
+ # end
+ #
+ # end
+ #
+ # Which would provide you with:
+ #
+ # person = Person.new
+ # 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> and <tt>:methods</tt> .
+ module Serializable
+ extend ActiveSupport::Concern
+
+ autoload :JSON, "active_model/serializable/json"
+ autoload :XML, "active_model/serializable/xml"
+
+ include ActiveModel::Serializer::Scope
+
+ module ClassMethods #:nodoc:
+ def _model_serializer
+ @_model_serializer ||= ActiveModel::Serializer::Finder.find(self, self)
+ end
+ end
+
+ def serializable_hash(options = nil)
+ options ||= {}
+
+ attribute_names = attributes.keys.sort
+ if only = options[:only]
+ attribute_names &= Array.wrap(only).map(&:to_s)
+ elsif except = options[:except]
+ attribute_names -= Array.wrap(except).map(&:to_s)
+ end
+
+ hash = {}
+ attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
+
+ method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
+ method_names.each { |n| hash[n] = send(n) }
+
+ serializable_add_includes(options) do |association, records, opts|
+ hash[association] = if records.is_a?(Enumerable)
+ records.map { |a| a.serializable_hash(opts) }
+ else
+ records.serializable_hash(opts)
+ end
+ end
+
+ hash
+ end
+
+ # Returns a model serializer for this object considering its namespace.
+ def model_serializer
+ self.class._model_serializer
+ end
+
+ private
+
+ # Hook method defining how an attribute value should be retrieved for
+ # serialization. By default this is assumed to be an instance named after
+ # the attribute. Override this method in subclasses should you need to
+ # retrieve the value for a given attribute differently:
+ #
+ # class MyClass
+ # include ActiveModel::Validations
+ #
+ # def initialize(data = {})
+ # @data = data
+ # end
+ #
+ # def read_attribute_for_serialization(key)
+ # @data[key]
+ # end
+ # end
+ #
+ alias :read_attribute_for_serialization :send
+
+ # Add associations specified via the <tt>:include</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 serializable_add_includes(options = {}) #:nodoc:
+ return unless include = options[:include]
+
+ unless include.is_a?(Hash)
+ include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
+ end
+
+ include.each do |association, opts|
+ if records = send(association)
+ yield association, records, opts
+ end
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/serializable/json.rb b/activemodel/lib/active_model/serializable/json.rb
new file mode 100644
index 0000000000..79173929e4
--- /dev/null
+++ b/activemodel/lib/active_model/serializable/json.rb
@@ -0,0 +1,108 @@
+require 'active_support/json'
+require 'active_support/core_ext/class/attribute'
+
+module ActiveModel
+ # == Active Model Serializable as JSON
+ module Serializable
+ module JSON
+ extend ActiveSupport::Concern
+ include ActiveModel::Serializable
+
+ included do
+ extend ActiveModel::Naming
+
+ class_attribute :include_root_in_json
+ self.include_root_in_json = true
+ end
+
+ # Returns a hash representing the model. Some configuration can be
+ # passed through +options+.
+ #
+ # The option <tt>include_root_in_json</tt> controls the top-level behavior
+ # of +as_json+. If true (the default) +as_json+ will emit a single root
+ # node named after the object's type. For example:
+ #
+ # user = User.find(1)
+ # user.as_json
+ # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true} }
+ #
+ # ActiveRecord::Base.include_root_in_json = false
+ # user.as_json
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true}
+ #
+ # This behavior can also be achieved by setting the <tt>:root</tt> option to +false+ as in:
+ #
+ # user = User.find(1)
+ # user.as_json(root: false)
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true}
+ #
+ # The remainder of the examples in this section assume include_root_in_json is set to
+ # <tt>false</tt>.
+ #
+ # Without any +options+, the returned Hash will include all the model's
+ # attributes. For example:
+ #
+ # user = User.find(1)
+ # user.as_json
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true}
+ #
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
+ # included, and work similar to the +attributes+ method. For example:
+ #
+ # user.as_json(:only => [ :id, :name ])
+ # # => {"id": 1, "name": "Konata Izumi"}
+ #
+ # user.as_json(:except => [ :id, :created_at, :age ])
+ # # => {"name": "Konata Izumi", "awesome": true}
+ #
+ # To include the result of some method calls on the model use <tt>:methods</tt>:
+ #
+ # user.as_json(:methods => :permalink)
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "permalink": "1-konata-izumi"}
+ #
+ # To include associations use <tt>:include</tt>:
+ #
+ # user.as_json(:include => :posts)
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
+ # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
+ #
+ # Second level and higher order associations work as well:
+ #
+ # user.as_json(:include => { :posts => {
+ # :include => { :comments => {
+ # :only => :body } },
+ # :only => :title } })
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
+ # "title": "Welcome to the weblog"},
+ # {"comments": [{"body": "Don't think too hard"}],
+ # "title": "So I was thinking"}]}
+ def as_json(options = nil)
+ root = include_root_in_json
+ root = options[:root] if options.try(:key?, :root)
+ if root
+ root = self.class.model_name.element if root == true
+ { root => serializable_hash(options) }
+ else
+ serializable_hash(options)
+ end
+ end
+
+ def from_json(json, include_root=include_root_in_json)
+ hash = ActiveSupport::JSON.decode(json)
+ hash = hash.values.first if include_root
+ self.attributes = hash
+ self
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/serializable/xml.rb b/activemodel/lib/active_model/serializable/xml.rb
new file mode 100644
index 0000000000..d11cee9b42
--- /dev/null
+++ b/activemodel/lib/active_model/serializable/xml.rb
@@ -0,0 +1,195 @@
+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'
+
+module ActiveModel
+ # == Active Model Serializable as XML
+ module Serializable
+ module XML
+ extend ActiveSupport::Concern
+ include ActiveModel::Serializable
+
+ class Serializer #:nodoc:
+ class Attribute #:nodoc:
+ attr_reader :name, :value, :type
+
+ def initialize(name, serializable, value)
+ @name, @serializable = name, serializable
+ value = value.in_time_zone if value.respond_to?(:in_time_zone)
+ @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.wrap(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.class.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
+
+ if records.is_a?(Enumerable)
+ 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
+ records.to_xml(merged_options)
+ end
+ 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
+
+ # 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. For example:
+ #
+ # 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 activerecord/lib/active_record/serializers/xml_serializer.xml.
+ def to_xml(options = {}, &block)
+ Serializer.new(self, options).serialize(&block)
+ end
+
+ def from_xml(xml)
+ self.attributes = Hash.from_xml(xml).values.first
+ self
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index a4b58ab456..439302c632 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -1,139 +1,10 @@
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/hash/slice'
-require 'active_support/core_ext/array/wrap'
-
-
module ActiveModel
- # == Active Model Serialization
- #
- # Provides a basic serialization to a serializable_hash for your object.
- #
- # A minimal implementation could be:
- #
- # class Person
- #
- # include ActiveModel::Serialization
- #
- # attr_accessor :name
- #
- # def attributes
- # {'name' => name}
- # end
- #
- # end
- #
- # Which would provide you with:
- #
- # person = Person.new
- # person.serializable_hash # => {"name"=>nil}
- # person.name = "Bob"
- # person.serializable_hash # => {"name"=>"Bob"}
- #
- # You need to declare some sort of attributes hash which contains the attributes
- # you want to serialize and their current value.
- #
- # Most of the time though, you will want to include the JSON or XML
- # serializations. Both of these modules automatically include the
- # ActiveModel::Serialization module, so there is no need to explicitly
- # include it.
- #
- # So a minimal implementation including XML and JSON would be:
- #
- # class Person
- #
- # include ActiveModel::Serializers::JSON
- # include ActiveModel::Serializers::Xml
- #
- # attr_accessor :name
- #
- # def attributes
- # {'name' => name}
- # end
- #
- # end
- #
- # Which would provide you with:
- #
- # person = Person.new
- # 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> and <tt>:methods</tt> .
module Serialization
- def serializable_hash(options = nil)
- options ||= {}
-
- attribute_names = attributes.keys.sort
- if only = options[:only]
- attribute_names &= Array.wrap(only).map(&:to_s)
- elsif except = options[:except]
- attribute_names -= Array.wrap(except).map(&:to_s)
- end
-
- hash = {}
- attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
-
- method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
- method_names.each { |n| hash[n] = send(n) }
-
- serializable_add_includes(options) do |association, records, opts|
- hash[association] = if records.is_a?(Enumerable)
- records.map { |a| a.serializable_hash(opts) }
- else
- records.serializable_hash(opts)
- end
- end
+ extend ActiveSupport::Concern
+ include ActiveModel::Serializable
- hash
+ included do
+ ActiveSupport::Deprecation.warn "ActiveModel::Serialization is deprecated in favor of ActiveModel::Serializable"
end
-
- private
-
- # Hook method defining how an attribute value should be retrieved for
- # serialization. By default this is assumed to be an instance named after
- # the attribute. Override this method in subclasses should you need to
- # retrieve the value for a given attribute differently:
- #
- # class MyClass
- # include ActiveModel::Validations
- #
- # def initialize(data = {})
- # @data = data
- # end
- #
- # def read_attribute_for_serialization(key)
- # @data[key]
- # end
- # end
- #
- alias :read_attribute_for_serialization :send
-
- # Add associations specified via the <tt>:include</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 serializable_add_includes(options = {}) #:nodoc:
- return unless include = options[:include]
-
- unless include.is_a?(Hash)
- include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
- end
-
- include.each do |association, opts|
- if records = send(association)
- yield association, records, opts
- end
- end
- end
end
-end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb
index 6d0746a3e8..a541a1053d 100644
--- a/activemodel/lib/active_model/serializer.rb
+++ b/activemodel/lib/active_model/serializer.rb
@@ -1,10 +1,70 @@
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/module/anonymous"
+require "active_support/core_ext/module/introspection"
require "set"
module ActiveModel
+ # Active Model Array Serializer
+ class ArraySerializer
+ attr_reader :object, :scope
+
+ def initialize(object, scope)
+ @object, @scope = object, scope
+ end
+
+ def serializable_array
+ @object.map do |item|
+ if serializer = Serializer::Finder.find(item, scope)
+ serializer.new(item, scope)
+ else
+ item
+ end
+ end
+ end
+
+ def as_json(*args)
+ serializable_array.as_json(*args)
+ end
+ end
+
+ # Active Model Serializer
class Serializer
+ module Finder
+ mattr_accessor :constantizer
+ @@constantizer = ActiveSupport::Inflector
+
+ # Finds a serializer for the given object in the given scope.
+ # If the object implements a +model_serializer+ method, it does
+ # not do a scope lookup but uses the model_serializer method instead.
+ def self.find(object, scope)
+ if object.respond_to?(:model_serializer)
+ object.model_serializer
+ else
+ scope = scope.class unless scope.respond_to?(:const_defined?)
+ object = object.class unless object.respond_to?(:name)
+ serializer = "#{object.name.demodulize}Serializer"
+
+ begin
+ scope.const_get serializer
+ rescue NameError => e
+ raise unless e.message =~ /uninitialized constant ([\w_]+::)*#{serializer}$/
+ scope.parents.each do |parent|
+ return parent.const_get(serializer) if parent.const_defined?(serializer)
+ end
+ nil
+ end
+ end
+ end
+ end
+
+ # Defines the serialization scope. Core extension serializers
+ # are defined in this module so a scoped lookup is able to find
+ # core extension serializers.
+ module Scope
+ ArraySerializer = ::ActiveModel::ArraySerializer
+ end
+
module Associations
class Config < Struct.new(:name, :options)
def serializer
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index c845440120..9efd7c5f69 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -1,108 +1,12 @@
-require 'active_support/json'
-require 'active_support/core_ext/class/attribute'
-
module ActiveModel
- # == Active Model JSON Serializer
module Serializers
module JSON
extend ActiveSupport::Concern
- include ActiveModel::Serialization
+ include ActiveModel::Serializable::JSON
included do
- extend ActiveModel::Naming
-
- class_attribute :include_root_in_json
- self.include_root_in_json = true
- end
-
- # Returns a hash representing the model. Some configuration can be
- # passed through +options+.
- #
- # The option <tt>include_root_in_json</tt> controls the top-level behavior
- # of +as_json+. If true (the default) +as_json+ will emit a single root
- # node named after the object's type. For example:
- #
- # user = User.find(1)
- # user.as_json
- # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true} }
- #
- # ActiveRecord::Base.include_root_in_json = false
- # user.as_json
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true}
- #
- # This behavior can also be achieved by setting the <tt>:root</tt> option to +false+ as in:
- #
- # user = User.find(1)
- # user.as_json(root: false)
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true}
- #
- # The remainder of the examples in this section assume include_root_in_json is set to
- # <tt>false</tt>.
- #
- # Without any +options+, the returned Hash will include all the model's
- # attributes. For example:
- #
- # user = User.find(1)
- # user.as_json
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true}
- #
- # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
- # included, and work similar to the +attributes+ method. For example:
- #
- # user.as_json(:only => [ :id, :name ])
- # # => {"id": 1, "name": "Konata Izumi"}
- #
- # user.as_json(:except => [ :id, :created_at, :age ])
- # # => {"name": "Konata Izumi", "awesome": true}
- #
- # To include the result of some method calls on the model use <tt>:methods</tt>:
- #
- # user.as_json(:methods => :permalink)
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true,
- # "permalink": "1-konata-izumi"}
- #
- # To include associations use <tt>:include</tt>:
- #
- # user.as_json(:include => :posts)
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true,
- # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
- # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
- #
- # Second level and higher order associations work as well:
- #
- # user.as_json(:include => { :posts => {
- # :include => { :comments => {
- # :only => :body } },
- # :only => :title } })
- # # => {"id": 1, "name": "Konata Izumi", "age": 16,
- # "created_at": "2006/08/01", "awesome": true,
- # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
- # "title": "Welcome to the weblog"},
- # {"comments": [{"body": "Don't think too hard"}],
- # "title": "So I was thinking"}]}
- def as_json(options = nil)
- root = include_root_in_json
- root = options[:root] if options.try(:key?, :root)
- if root
- root = self.class.model_name.element if root == true
- { root => serializable_hash(options) }
- else
- serializable_hash(options)
- end
- end
-
- def from_json(json, include_root=include_root_in_json)
- hash = ActiveSupport::JSON.decode(json)
- hash = hash.values.first if include_root
- self.attributes = hash
- self
+ ActiveSupport::Deprecation.warn "ActiveModel::Serializers::JSON is deprecated in favor of ActiveModel::Serializable::JSON"
end
end
end
-end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index d61d9d7119..620390da6b 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -1,195 +1,14 @@
-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'
-
module ActiveModel
- # == Active Model XML Serializer
module Serializers
module Xml
extend ActiveSupport::Concern
- include ActiveModel::Serialization
-
- class Serializer #:nodoc:
- class Attribute #:nodoc:
- attr_reader :name, :value, :type
-
- def initialize(name, serializable, value)
- @name, @serializable = name, serializable
- value = value.in_time_zone if value.respond_to?(:in_time_zone)
- @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.wrap(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]
+ include ActiveModel::Serializable::XML
- root = (options[:root] || @serializable.class.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
-
- if records.is_a?(Enumerable)
- 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
- records.to_xml(merged_options)
- end
- 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
-
- # 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. For example:
- #
- # 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 activerecord/lib/active_record/serializers/xml_serializer.xml.
- def to_xml(options = {}, &block)
- Serializer.new(self, options).serialize(&block)
- end
+ Serializer = ActiveModel::Serializable::XML::Serializer
- def from_xml(xml)
- self.attributes = Hash.from_xml(xml).values.first
- self
+ included do
+ ActiveSupport::Deprecation.warn "ActiveModel::Serializers::Xml is deprecated in favor of ActiveModel::Serializable::XML"
end
end
end
-end
+end \ No newline at end of file