Class: ActiveModel::Serializer
- Inherits:
-
Object
- Object
- ActiveModel::Serializer
- Extended by:
- ActiveSupport::DescendantsTracker
- Defined in:
- lib/active_model/serializer.rb,
lib/active_model/serializers/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: Associations Classes: 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
set perform caching like root.
- .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
- #_serializable_hash ⇒ Object
-
#as_json(options = {}) ⇒ Object
Returns a json representation of the serializable object including the root.
-
#attributes ⇒ Object
Returns a hash representation of the serializable object attributes.
- #expand_cache_key(*args) ⇒ Object
- #include!(name, options = {}) ⇒ Object
- #include?(name) ⇒ Boolean
- #include_associations! ⇒ Object
- #include_meta(hash) ⇒ 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.
- #meta_key ⇒ Object
- #perform_caching? ⇒ Boolean
- #root_name ⇒ Object
-
#scope ⇒ Object
Returns options.
-
#serializable_hash ⇒ Object
Returns a hash representation of the serializable object without the root.
- #to_json(*args) ⇒ Object
- #url_options ⇒ Object
Constructor Details
#initialize(object, options = {}) ⇒ Serializer
Returns a new instance of Serializer.
292 293 294 295 296 297 298 299 300 301 |
# File 'lib/active_model/serializer.rb', line 292 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.
290 291 292 |
# File 'lib/active_model/serializer.rb', line 290 def object @object end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
290 291 292 |
# File 'lib/active_model/serializer.rb', line 290 def @options end |
Class Method Details
.associate(klass, attrs) ⇒ Object
:nodoc:
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/active_model/serializer.rb', line 115 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.refine(attr, ) end end |
.attribute(attr, options = {}) ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/active_model/serializer.rb', line 92 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.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/active_model/serializer.rb', line 79 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.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/active_model/serializer.rb', line 260 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 [:url_options] = controller. serializer.new(resource, ) end |
.cached(value = true) ⇒ Object
set perform caching like root
74 75 76 |
# File 'lib/active_model/serializer.rb', line 74 def cached(value = true) self.perform_caching = value end |
.define_include_method(name) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/active_model/serializer.rb', line 132 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
245 246 247 248 |
# File 'lib/active_model/serializer.rb', line 245 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.
150 151 152 |
# File 'lib/active_model/serializer.rb', line 150 def has_many(*attrs) associate(Associations::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.
160 161 162 |
# File 'lib/active_model/serializer.rb', line 160 def has_one(*attrs) associate(Associations::HasOne, attrs) end |
.model_class ⇒ Object
The model class associated with this serializer.
235 236 237 |
# File 'lib/active_model/serializer.rb', line 235 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.
251 252 253 |
# File 'lib/active_model/serializer.rb', line 251 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.
196 197 198 199 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 |
# File 'lib/active_model/serializer.rb', line 196 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, self) 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
#_serializable_hash ⇒ Object
477 478 479 480 481 482 |
# File 'lib/active_model/serializer.rb', line 477 def _serializable_hash return nil if @object.nil? @node = attributes include_associations! if @node end |
#as_json(options = {}) ⇒ Object
Returns a json representation of the serializable object including the root.
339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/active_model/serializer.rb', line 339 def as_json(={}) ||= {} if root = .fetch(:root, @options.fetch(:root, root_name)) @options[:hash] = hash = {} @options[:unique_values] = {} hash.merge!(root => serializable_hash) hash hash else serializable_hash end end |
#attributes ⇒ Object
Returns a hash representation of the serializable object attributes.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/active_model/serializer.rb', line 454 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 |
#expand_cache_key(*args) ⇒ Object
488 489 490 |
# File 'lib/active_model/serializer.rb', line 488 def (*args) ActiveSupport::Cache.(args) end |
#include!(name, options = {}) ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/active_model/serializer.rb', line 377 def include!(name, ={}) # Make sure that if a special options[:hash] was passed in, we generate # a new unique values hash and don't clobber the original. If the hash # passed in is the same as the current options hash, use the current # unique values. # # TODO: Should passing in a Hash even be public API here? unique_values = if hash = [:hash] if @options[:hash] == hash @options[:unique_values] ||= {} else {} end else hash = @options[:hash] @options[:unique_values] ||= {} end 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 association_class = if klass = _associations[name] klass elsif value.respond_to?(:to_ary) Associations::HasMany else Associations::HasOne end 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
371 372 373 374 375 |
# File 'lib/active_model/serializer.rb', line 371 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
365 366 367 368 369 |
# File 'lib/active_model/serializer.rb', line 365 def include_associations! _associations.each_key do |name| include!(name) if include?(name) end end |
#include_meta(hash) ⇒ Object
323 324 325 |
# File 'lib/active_model/serializer.rb', line 323 def (hash) hash[] = @options[:meta] if @options.has_key?(:meta) end |
#instrument(name, payload = {}, &block) ⇒ Object
Use ActiveSupport::Notifications to send events to external systems. The event name is: name.class_name.serializer
494 495 496 497 |
# File 'lib/active_model/serializer.rb', line 494 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.
440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/active_model/serializer.rb', line 440 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 |
#meta_key ⇒ Object
319 320 321 |
# File 'lib/active_model/serializer.rb', line 319 def @options[:meta_key].try(:to_sym) || :meta end |
#perform_caching? ⇒ Boolean
484 485 486 |
# File 'lib/active_model/serializer.rb', line 484 def perform_caching? perform_caching && cache && respond_to?(:cache_key) end |
#root_name ⇒ Object
303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/active_model/serializer.rb', line 303 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
471 472 473 |
# File 'lib/active_model/serializer.rb', line 471 def scope @options[:scope] end |
#serializable_hash ⇒ Object
Returns a hash representation of the serializable object without the root.
355 356 357 358 359 360 361 362 363 |
# File 'lib/active_model/serializer.rb', line 355 def serializable_hash if perform_caching? cache.fetch ([self.class.to_s.underscore, cache_key, 'serializable-hash']) do _serializable_hash end else _serializable_hash end end |
#to_json(*args) ⇒ Object
327 328 329 330 331 332 333 334 335 |
# File 'lib/active_model/serializer.rb', line 327 def to_json(*args) if perform_caching? cache.fetch ([self.class.to_s.underscore, cache_key, 'to-json']) do super end else super end end |
#url_options ⇒ Object
315 316 317 |
# File 'lib/active_model/serializer.rb', line 315 def @options[:url_options] || {} end |