Class: ActiveModel::Serializer
- Inherits:
-
Object
- Object
- ActiveModel::Serializer
- Extended by:
- ActiveSupport::DescendantsTracker
- Includes:
- Serializable, Caching
- Defined in:
- lib/active_model/serializer.rb,
lib/active_model/serializer/caching.rb,
lib/active_model/serializer/version.rb,
lib/active_model/serializer/associations.rb
Overview
Active Model Serializer
Provides a basic serializer implementation that allows you to easily control how a given object is going to be serialized. On initialization, it expects two objects as arguments, a resource and options. For example, one may do in a controller:
PostSerializer.new(@post, scope: current_user).to_json
The object to be serialized is the @post and the current user is passed in for authorization purposes.
We use the scope to check if a given attribute should be serialized or not. For example, some attributes may only be returned if current_user is the author of the post:
class PostSerializer < ActiveModel::Serializer
attributes :title, :body
has_many :comments
private
def attributes
hash = super
hash.merge!(email: post.email) if
hash
end
def
post. == scope
end
end
Defined Under Namespace
Modules: Caching Classes: Association, IncludeError
Constant Summary collapse
- INCLUDE_METHODS =
{}
- INSTRUMENT =
{ serialize: :"serialize.serializer", associations: :"associations.serializer" }
- VERSION =
"0.8.2"
Instance Attribute Summary collapse
-
#object ⇒ Object
readonly
Returns the value of attribute object.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Class Method Summary collapse
-
.associate(klass, attrs) ⇒ Object
:nodoc:.
- .attribute(attr, options = {}) ⇒ Object
-
.attributes(*attrs) ⇒ Object
Define attributes to be used in the serialization.
-
.build_json(controller, resource, options) ⇒ Object
Used internally to create a new serializer object based on controller settings and options for a given resource.
- .cached(value = true) ⇒ Object
- .define_include_method(name) ⇒ Object
-
.embed(type, options = {}) ⇒ Object
Define how associations should be embedded.
-
.has_many(*attrs) ⇒ Object
Defines an association in the object should be rendered.
-
.has_one(*attrs) ⇒ Object
Defines an association in the object should be rendered.
-
.model_class ⇒ Object
The model class associated with this serializer.
-
.root(name) ⇒ Object
(also: root=)
Defines the root used on serialization.
-
.schema ⇒ Object
Return a schema hash for the current serializer.
Instance Method Summary collapse
-
#as_json(args = {}) ⇒ Object
Returns a json representation of the serializable object including the root.
-
#attributes ⇒ Object
Returns a hash representation of the serializable object attributes.
- #include!(name, options = {}) ⇒ Object
- #include?(name) ⇒ Boolean
- #include_associations! ⇒ Object
-
#initialize(object, options = {}) ⇒ Serializer
constructor
A new instance of Serializer.
-
#instrument(name, payload = {}, &block) ⇒ Object
Use ActiveSupport::Notifications to send events to external systems.
-
#merge_association(hash, key, serializables, unique_values) ⇒ Object
In some cases, an Array of associations is built by merging the associated content for all of the children.
- #root_name ⇒ Object
-
#scope ⇒ Object
Returns options.
-
#serializable_hash ⇒ Object
Returns a hash representation of the serializable object without the root.
- #serialize_object ⇒ Object
- #url_options ⇒ Object
Methods included from Caching
Constructor Details
#initialize(object, options = {}) ⇒ Serializer
Returns a new instance of Serializer.
296 297 298 299 300 301 302 303 304 305 |
# File 'lib/active_model/serializer.rb', line 296 def initialize(object, ={}) @object, @options = object, scope_name = @options[:scope_name] if scope_name && !respond_to?(scope_name) self.class.class_eval do define_method scope_name, lambda { scope } end end end |
Instance Attribute Details
#object ⇒ Object (readonly)
Returns the value of attribute object.
294 295 296 |
# File 'lib/active_model/serializer.rb', line 294 def object @object end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
294 295 296 |
# File 'lib/active_model/serializer.rb', line 294 def @options end |
Class Method Details
.associate(klass, attrs) ⇒ Object
:nodoc:
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/active_model/serializer.rb', line 119 def associate(klass, attrs) #:nodoc: = attrs. self._associations = _associations.dup attrs.each do |attr| unless method_defined?(attr) define_method attr do object.send attr end end define_include_method attr self._associations[attr] = [klass, ] end end |
.attribute(attr, options = {}) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/active_model/serializer.rb', line 96 def attribute(attr, ={}) self._attributes = _attributes.merge(attr.is_a?(Hash) ? attr : {attr => [:key] || attr.to_s.gsub(/\?$/, '').to_sym}) attr = attr.keys[0] if attr.is_a? Hash unless method_defined?(attr) define_method attr do object.read_attribute_for_serialization(attr.to_sym) end end define_include_method attr # protect inheritance chains and open classes # if a serializer inherits from another OR # attributes are added later in a classes lifecycle # poison the cache define_method :_fast_attributes do raise NameError end end |
.attributes(*attrs) ⇒ Object
Define attributes to be used in the serialization.
83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/active_model/serializer.rb', line 83 def attributes(*attrs) self._attributes = _attributes.dup attrs.each do |attr| if Hash === attr attr.each {|attr_real, key| attribute(attr_real, key: key) } else attribute attr end end end |
.build_json(controller, resource, options) ⇒ Object
Used internally to create a new serializer object based on controller settings and options for a given resource. These settings are typically set during the request lifecycle or by the controller class, and should not be manually defined for this method.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/active_model/serializer.rb', line 264 def build_json(controller, resource, ) = controller.send(:default_serializer_options) || {} = .merge( || {}) serializer = .delete(:serializer) || (resource.respond_to?(:active_model_serializer) && resource.active_model_serializer) return serializer unless serializer if resource.respond_to?(:to_ary) unless serializer <= ActiveModel::ArraySerializer raise ArgumentError.new("#{serializer.name} is not an ArraySerializer. " + "You may want to use the :each_serializer option instead.") end if [:root] != false && serializer.root != false # the serializer for an Array is ActiveModel::ArraySerializer [:root] ||= serializer.root || controller.controller_name end end [:scope] = controller.serialization_scope unless .has_key?(:scope) [:scope_name] = controller._serialization_scope unless .has_key?(:scope_name) [:url_options] = controller. serializer.new(resource, ) end |
.cached(value = true) ⇒ Object
78 79 80 |
# File 'lib/active_model/serializer.rb', line 78 def cached(value = true) self.perform_caching = value end |
.define_include_method(name) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/active_model/serializer.rb', line 136 def define_include_method(name) method = "include_#{name}?".to_sym INCLUDE_METHODS[name] = method unless method_defined?(method) define_method method do true end end end |
.embed(type, options = {}) ⇒ Object
Define how associations should be embedded.
:objects # Embed associations as full objects
:ids # Embed only the association ids
:ids, include: true # Embed the association ids and include objects in the root
249 250 251 252 |
# File 'lib/active_model/serializer.rb', line 249 def (type, ={}) self. = type self. = true if [:include] end |
.has_many(*attrs) ⇒ Object
Defines an association in the object should be rendered.
The serializer object should implement the association name as a method which should return an array when invoked. If a method with the association name does not exist, the association name is dispatched to the serialized object.
154 155 156 |
# File 'lib/active_model/serializer.rb', line 154 def has_many(*attrs) associate(Association::HasMany, attrs) end |
.has_one(*attrs) ⇒ Object
Defines an association in the object should be rendered.
The serializer object should implement the association name as a method which should return an object when invoked. If a method with the association name does not exist, the association name is dispatched to the serialized object.
164 165 166 |
# File 'lib/active_model/serializer.rb', line 164 def has_one(*attrs) associate(Association::HasOne, attrs) end |
.model_class ⇒ Object
The model class associated with this serializer.
239 240 241 |
# File 'lib/active_model/serializer.rb', line 239 def model_class name.sub(/Serializer$/, '').constantize end |
.root(name) ⇒ Object Also known as: root=
Defines the root used on serialization. If false, disables the root.
255 256 257 |
# File 'lib/active_model/serializer.rb', line 255 def root(name) self._root = name end |
.schema ⇒ Object
Return a schema hash for the current serializer. This information can be used to generate clients for the serialized output.
The schema hash has two keys: attributes and associations.
The attributes hash looks like this:
{ name: :string, age: :integer }
The associations hash looks like this:
{ posts: { has_many: :posts } }
If :key is used:
class PostsSerializer < ActiveModel::Serializer
has_many :posts, key: :my_posts
end
the hash looks like this:
{ my_posts: { has_many: :posts }
This information is extracted from the serializer’s model class, which is provided by SerializerClass.model_class.
The schema method uses the columns_hash and reflect_on_association methods, provided by default by ActiveRecord. You can implement these methods on your custom models if you want the serializer’s schema method to work.
TODO: This is currently coupled to Active Record. We need to figure out a way to decouple those two.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/active_model/serializer.rb', line 200 def schema klass = model_class columns = klass.columns_hash attrs = {} _attributes.each do |name, key| if column = columns[name.to_s] attrs[key] = column.type else # Computed attribute (method on serializer or model). We cannot # infer the type, so we put nil, unless specified in the attribute declaration if name != key attrs[name] = key else attrs[key] = nil end end end associations = {} _associations.each do |attr, (association_class, )| association = association_class.new(attr, ) if model_association = klass.reflect_on_association(association.name) # Real association. associations[association.key] = { model_association.macro => model_association.name } else # Computed association. We could infer has_many vs. has_one from # the association class, but that would make it different from # real associations, which read has_one vs. belongs_to from the # model. associations[association.key] = nil end end { attributes: attrs, associations: associations } end |
Instance Method Details
#as_json(args = {}) ⇒ Object
Returns a json representation of the serializable object including the root.
325 326 327 |
# File 'lib/active_model/serializer.rb', line 325 def as_json(args={}) super(root: args.fetch(:root, .fetch(:root, root_name))) end |
#attributes ⇒ Object
Returns a hash representation of the serializable object attributes.
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/active_model/serializer.rb', line 420 def attributes _fast_attributes rescue NameError method = "def _fast_attributes\n" method << " h = {}\n" _attributes.each do |name,key| method << " h[:\"#{key}\"] = read_attribute_for_serialization(:\"#{name}\") if include?(:\"#{name}\")\n" end method << " h\nend" self.class.class_eval method _fast_attributes end |
#include!(name, options = {}) ⇒ Object
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/active_model/serializer.rb', line 354 def include!(name, ={}) hash = @options[:hash] unique_values = @options[:unique_values] ||= {} node = [:node] ||= @node value = [:value] if [:include] == nil if @options.key?(:include) [:include] = @options[:include].include?(name) elsif @options.include?(:exclude) [:include] = !@options[:exclude].include?(name) end end klass, = _associations[name] association_class = if klass = .merge klass elsif value.respond_to?(:to_ary) Association::HasMany else Association::HasOne end = .merge!() [:value] ||= send(name) association = association_class.new(name, , self.) if association. node[association.key] = association.serialize_ids if association. && hash.nil? raise IncludeError.new(self.class, association.name) elsif association. && association. merge_association hash, association.root, association.serializables, unique_values end elsif association. node[association.key] = association.serialize end end |
#include?(name) ⇒ Boolean
348 349 350 351 352 |
# File 'lib/active_model/serializer.rb', line 348 def include?(name) return false if @options.key?(:only) && !Array(@options[:only]).include?(name) return false if @options.key?(:except) && Array(@options[:except]).include?(name) send INCLUDE_METHODS[name] end |
#include_associations! ⇒ Object
342 343 344 345 346 |
# File 'lib/active_model/serializer.rb', line 342 def include_associations! _associations.each_key do |name| include!(name) if include?(name) end end |
#instrument(name, payload = {}, &block) ⇒ Object
Use ActiveSupport::Notifications to send events to external systems. The event name is: name.class_name.serializer
445 446 447 448 |
# File 'lib/active_model/serializer.rb', line 445 def instrument(name, payload = {}, &block) event_name = INSTRUMENT[name] ActiveSupport::Notifications.instrument(event_name, payload, &block) end |
#merge_association(hash, key, serializables, unique_values) ⇒ Object
In some cases, an Array of associations is built by merging the associated content for all of the children. For instance, if a Post has_many comments, which has_many tags, the top-level :tags key will contain the merged list of all tags for all comments of the post.
In order to make this efficient, we store a :unique_values hash containing a unique list of all of the objects that are already in the Array. This avoids the need to scan through the Array looking for entries every time we want to merge a new list of values.
406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/active_model/serializer.rb', line 406 def merge_association(hash, key, serializables, unique_values) already_serialized = (unique_values[key] ||= {}) serializable_hashes = (hash[key] ||= []) serializables.each do |serializable| unless already_serialized.include? serializable.object already_serialized[serializable.object] = true serializable_hashes << serializable.serializable_hash end end end |
#root_name ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/active_model/serializer.rb', line 307 def root_name return false if self._root == false class_name = self.class.name.demodulize.underscore.sub(/_serializer$/, '').to_sym unless self.class.name.blank? if self._root == true class_name else self._root || class_name end end |
#scope ⇒ Object
Returns options
437 438 439 |
# File 'lib/active_model/serializer.rb', line 437 def scope @options[:scope] end |
#serializable_hash ⇒ Object
Returns a hash representation of the serializable object without the root.
335 336 337 338 339 340 |
# File 'lib/active_model/serializer.rb', line 335 def serializable_hash return nil if @object.nil? @node = attributes include_associations! if @node end |
#serialize_object ⇒ Object
329 330 331 |
# File 'lib/active_model/serializer.rb', line 329 def serialize_object serializable_hash end |
#url_options ⇒ Object
319 320 321 |
# File 'lib/active_model/serializer.rb', line 319 def @options[:url_options] || {} end |