Skip to content
This repository
tag: v3.2.0.rc2
Fetching contributors…

Cannot retrieve contributors at this time

file 139 lines (127 sloc) 4.304 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
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

      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::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
Something went wrong with that request. Please try again.