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.5"
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.
- .hidden_attributes(*attrs) ⇒ Object
-
.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.
303 304 305 306 307 308 309 310 311 312 |
# File 'lib/active_model/serializer.rb', line 303 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.
301 302 303 |
# File 'lib/active_model/serializer.rb', line 301 def object @object end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
301 302 303 |
# File 'lib/active_model/serializer.rb', line 301 def @options end |
Class Method Details
.associate(klass, attrs) ⇒ Object
:nodoc:
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/active_model/serializer.rb', line 126 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
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/active_model/serializer.rb', line 103 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.
86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/active_model/serializer.rb', line 86 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.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/active_model/serializer.rb', line 271 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
81 82 83 |
# File 'lib/active_model/serializer.rb', line 81 def cached(value = true) self.perform_caching = value end |
.define_include_method(name) ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/active_model/serializer.rb', line 143 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
256 257 258 259 |
# File 'lib/active_model/serializer.rb', line 256 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.
161 162 163 |
# File 'lib/active_model/serializer.rb', line 161 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.
171 172 173 |
# File 'lib/active_model/serializer.rb', line 171 def has_one(*attrs) associate(Association::HasOne, attrs) end |
.hidden_attributes(*attrs) ⇒ Object
99 100 101 |
# File 'lib/active_model/serializer.rb', line 99 def hidden_attributes(*attrs) self._hidden_attributes.concat(attrs) end |
.model_class ⇒ Object
The model class associated with this serializer.
246 247 248 |
# File 'lib/active_model/serializer.rb', line 246 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.
262 263 264 |
# File 'lib/active_model/serializer.rb', line 262 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.
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 237 238 239 240 241 242 243 |
# File 'lib/active_model/serializer.rb', line 207 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.
332 333 334 |
# File 'lib/active_model/serializer.rb', line 332 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.
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/active_model/serializer.rb', line 429 def attributes _fast_attributes rescue NameError method = "def _fast_attributes\n" method << " h = {}\n" _attributes.each do |name,key| method << " if include?(:\"#{name}\") && (!_hidden_attributes.include?(:\"#{name}\") || (@options.key?(:only) && Array(@options[:only]).include?(:\"#{name}\")))\n" method << " h[:\"#{key}\"] = read_attribute_for_serialization(:\"#{name}\")\n" method << " end\n" end method << " h\nend" self.class.class_eval method _fast_attributes end |
#include!(name, options = {}) ⇒ Object
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 396 397 398 399 400 401 402 403 404 |
# File 'lib/active_model/serializer.rb', line 363 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
357 358 359 360 361 |
# File 'lib/active_model/serializer.rb', line 357 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
349 350 351 352 353 354 355 |
# File 'lib/active_model/serializer.rb', line 349 def include_associations! _associations.each_key do |name| if include?(name) && (!_hidden_attributes.include?(name) || (@options.key?(:only) && Array(@options[:only]).include?(name))) include!(name) end end end |
#instrument(name, payload = {}, &block) ⇒ Object
Use ActiveSupport::Notifications to send events to external systems. The event name is: name.class_name.serializer
456 457 458 459 |
# File 'lib/active_model/serializer.rb', line 456 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.
415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/active_model/serializer.rb', line 415 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
314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/active_model/serializer.rb', line 314 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
448 449 450 |
# File 'lib/active_model/serializer.rb', line 448 def scope @options[:scope] end |
#serializable_hash ⇒ Object
Returns a hash representation of the serializable object without the root.
342 343 344 345 346 347 |
# File 'lib/active_model/serializer.rb', line 342 def serializable_hash return nil if @object.nil? @node = attributes include_associations! if @node end |
#serialize_object ⇒ Object
336 337 338 |
# File 'lib/active_model/serializer.rb', line 336 def serialize_object serializable_hash end |
#url_options ⇒ Object
326 327 328 |
# File 'lib/active_model/serializer.rb', line 326 def @options[:url_options] || {} end |