18a2513729
The current code base is not uniform. After some discussion, we have chosen to go with double quotes by default.
191 lines
5.9 KiB
Ruby
191 lines
5.9 KiB
Ruby
require "active_support/core_ext/hash/except"
|
|
require "active_support/core_ext/hash/slice"
|
|
|
|
module ActiveModel
|
|
# == Active \Model \Serialization
|
|
#
|
|
# Provides a basic serialization to a serializable_hash for your objects.
|
|
#
|
|
# A minimal implementation could be:
|
|
#
|
|
# class Person
|
|
# include ActiveModel::Serialization
|
|
#
|
|
# attr_accessor :name
|
|
#
|
|
# def attributes
|
|
# {'name' => nil}
|
|
# end
|
|
# end
|
|
#
|
|
# Which would provide you with:
|
|
#
|
|
# person = Person.new
|
|
# person.serializable_hash # => {"name"=>nil}
|
|
# person.name = "Bob"
|
|
# person.serializable_hash # => {"name"=>"Bob"}
|
|
#
|
|
# An +attributes+ hash must be defined and should contain any attributes you
|
|
# need to be serialized. Attributes must be strings, not symbols.
|
|
# When called, serializable hash will use instance methods that match the name
|
|
# of the attributes hash's keys. In order to override this behavior, take a look
|
|
# at the private method +read_attribute_for_serialization+.
|
|
#
|
|
# ActiveModel::Serializers::JSON module automatically includes
|
|
# the <tt>ActiveModel::Serialization</tt> module, so there is no need to
|
|
# explicitly include <tt>ActiveModel::Serialization</tt>.
|
|
#
|
|
# A minimal implementation including JSON would be:
|
|
#
|
|
# class Person
|
|
# include ActiveModel::Serializers::JSON
|
|
#
|
|
# attr_accessor :name
|
|
#
|
|
# def attributes
|
|
# {'name' => nil}
|
|
# 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.name = "Bob"
|
|
# person.serializable_hash # => {"name"=>"Bob"}
|
|
# person.as_json # => {"name"=>"Bob"}
|
|
# person.to_json # => "{\"name\":\"Bob\"}"
|
|
#
|
|
# Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
|
|
# <tt>:include</tt>. The following are all valid examples:
|
|
#
|
|
# person.serializable_hash(only: 'name')
|
|
# person.serializable_hash(include: :address)
|
|
# person.serializable_hash(include: { address: { only: 'city' }})
|
|
module Serialization
|
|
# Returns a serialized hash of your object.
|
|
#
|
|
# class Person
|
|
# include ActiveModel::Serialization
|
|
#
|
|
# attr_accessor :name, :age
|
|
#
|
|
# def attributes
|
|
# {'name' => nil, 'age' => nil}
|
|
# end
|
|
#
|
|
# def capitalized_name
|
|
# name.capitalize
|
|
# end
|
|
# end
|
|
#
|
|
# person = Person.new
|
|
# person.name = 'bob'
|
|
# person.age = 22
|
|
# person.serializable_hash # => {"name"=>"bob", "age"=>22}
|
|
# person.serializable_hash(only: :name) # => {"name"=>"bob"}
|
|
# person.serializable_hash(except: :name) # => {"age"=>22}
|
|
# person.serializable_hash(methods: :capitalized_name)
|
|
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
|
|
#
|
|
# Example with <tt>:include</tt> option
|
|
#
|
|
# class User
|
|
# include ActiveModel::Serializers::JSON
|
|
# attr_accessor :name, :notes # Emulate has_many :notes
|
|
# def attributes
|
|
# {'name' => nil}
|
|
# end
|
|
# end
|
|
#
|
|
# class Note
|
|
# include ActiveModel::Serializers::JSON
|
|
# attr_accessor :title, :text
|
|
# def attributes
|
|
# {'title' => nil, 'text' => nil}
|
|
# end
|
|
# end
|
|
#
|
|
# note = Note.new
|
|
# note.title = 'Battle of Austerlitz'
|
|
# note.text = 'Some text here'
|
|
#
|
|
# user = User.new
|
|
# user.name = 'Napoleon'
|
|
# user.notes = [note]
|
|
#
|
|
# user.serializable_hash
|
|
# # => {"name" => "Napoleon"}
|
|
# user.serializable_hash(include: { notes: { only: 'title' }})
|
|
# # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
|
|
def serializable_hash(options = nil)
|
|
options ||= {}
|
|
|
|
attribute_names = attributes.keys
|
|
if only = options[:only]
|
|
attribute_names &= Array(only).map(&:to_s)
|
|
elsif except = options[:except]
|
|
attribute_names -= Array(except).map(&:to_s)
|
|
end
|
|
|
|
hash = {}
|
|
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
|
|
|
|
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) }
|
|
|
|
serializable_add_includes(options) do |association, records, opts|
|
|
hash[association.to_s] = if records.respond_to?(:to_ary)
|
|
records.to_ary.map { |a| a.serializable_hash(opts) }
|
|
else
|
|
records.serializable_hash(opts)
|
|
end
|
|
end
|
|
|
|
hash
|
|
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::Serialization
|
|
#
|
|
# 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 includes = options[:include]
|
|
|
|
unless includes.is_a?(Hash)
|
|
includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
|
|
end
|
|
|
|
includes.each do |association, opts|
|
|
if records = send(association)
|
|
yield association, records, opts
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|