Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This implements the ActiveModel::Serializer object. Includes code, tests, generators and guides. From José and Yehuda with love. Conflicts: railties/CHANGELOG.md
- Loading branch information
Showing
35 changed files
with
2,074 additions
and
482 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
module ActionController | ||
# Action Controller Serialization | ||
# | ||
# Overrides render :json to check if the given object implements +active_model_serializer+ | ||
# as a method. If so, use the returned serializer instead of calling +to_json+ in the object. | ||
# | ||
# This module also provides a serialization_scope method that allows you to configure the | ||
# +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+ | ||
# to the current user: | ||
# | ||
# class ApplicationController < ActionController::Base | ||
# serialization_scope :current_user | ||
# end | ||
# | ||
# If you need more complex scope rules, you can simply override the serialization_scope: | ||
# | ||
# class ApplicationController < ActionController::Base | ||
# private | ||
# | ||
# def serialization_scope | ||
# current_user | ||
# end | ||
# end | ||
# | ||
module Serialization | ||
extend ActiveSupport::Concern | ||
|
||
include ActionController::Renderers | ||
|
||
included do | ||
class_attribute :_serialization_scope | ||
end | ||
|
||
def serialization_scope | ||
send(_serialization_scope) | ||
end | ||
|
||
def _render_option_json(json, options) | ||
if json.respond_to?(:active_model_serializer) && (serializer = json.active_model_serializer) | ||
json = serializer.new(json, serialization_scope) | ||
end | ||
super | ||
end | ||
|
||
module ClassMethods | ||
def serialization_scope(scope) | ||
self._serialization_scope = scope | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' | ||
require 'active_support/core_ext/string/inflections' | ||
|
||
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" | ||
|
||
module ClassMethods #:nodoc: | ||
def active_model_serializer | ||
return @active_model_serializer if defined?(@active_model_serializer) | ||
@active_model_serializer = "#{self.name}Serializer".safe_constantize | ||
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 active_model_serializer | ||
self.class.active_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 |
Oops, something went wrong.