Add PORO serializable base class: ActiveModelSerializers::Model

This commit is contained in:
Benjamin Fleischer 2015-10-09 00:19:03 -05:00
parent 526b56e9a6
commit 124faaa829
10 changed files with 85 additions and 43 deletions

View File

@ -374,6 +374,13 @@ class PostSerializer < ActiveModel::Serializer
end
```
## Serializing non-ActiveRecord objects
All serializable resources must pass the ActiveModel::Serializer::Lint::Tests.
See the ActiveModelSerializers::Model for a base class that implements the full
API for a plain-old Ruby object (PORO).
## Getting Help
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).

View File

@ -80,8 +80,8 @@ module ActiveModel::Serializer::Lint
# arguments (Rails 4.0) or a splat (Rails 4.1+).
# Fails otherwise.
#
# <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
# which is used by the adapter.
# <tt>cache_key</tt> returns a (self-expiring) unique key for the object, and
# is part of the (self-expiring) cache_key, which is used by the adapter.
# It is not required unless caching is enabled.
def test_cache_key
assert_respond_to resource, :cache_key
@ -92,6 +92,19 @@ module ActiveModel::Serializer::Lint
assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
end
# Passes if the object responds to <tt>updated_at</tt> and if it takes no
# arguments.
# Fails otherwise.
#
# <tt>updated_at</tt> returns a Time object or iso8601 string and
# is part of the (self-expiring) cache_key, which is used by the adapter.
# It is not required unless caching is enabled.
def test_updated_at
assert_respond_to resource, :updated_at
actual_arity = resource.method(:updated_at).arity
assert_equal actual_arity, 0, "expected #{actual_arity.inspect} to be 0"
end
# Passes if the object responds to <tt>id</tt> and if it takes no
# arguments.
# Fails otherwise.

View File

@ -7,6 +7,9 @@ module ActiveModelSerializers
mattr_accessor :logger
self.logger = Rails.logger || Logger.new(IO::NULL)
extend ActiveSupport::Autoload
autoload :Model
module_function
# @note

View File

@ -0,0 +1,39 @@
# ActiveModelSerializers::Model is a convenient
# serializable class to inherit from when making
# serializable non-activerecord objects.
module ActiveModelSerializers
class Model
include ActiveModel::Model
include ActiveModel::Serializers::JSON
attr_reader :attributes
def initialize(attributes = {})
@attributes = attributes
super
end
# Defaults to the downcased model name.
def id
attributes.fetch(:id) { self.class.name.downcase }
end
# Defaults to the downcased model name and updated_at
def cache_key
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime("%Y%m%d%H%M%S%9N")}" }
end
# Defaults to the time the serializer file was modified.
def updated_at
attributes.fetch(:updated_at) { File.mtime(__FILE__) }
end
def read_attribute_for_serialization(key)
if key == :id || key == 'id'
attributes.fetch(key) { id }
else
attributes[key]
end
end
end
end

View File

@ -46,7 +46,7 @@ module ActionController
def test_render_skipping_adapter
get :render_skipping_adapter
assert_equal '{"attributes":{"name":"Name 1","description":"Description 1","comments":"Comments 1"}}', response.body
assert_equal '{"name":"Name 1","description":"Description 1","comments":"Comments 1"}', response.body
end
end
end

View File

@ -0,0 +1,9 @@
require 'test_helper'
class ActiveModelSerializers::ModelTest < Minitest::Test
include ActiveModel::Serializer::Lint::Tests
def setup
@resource = ActiveModelSerializers::Model.new
end
end

View File

@ -36,7 +36,7 @@ module ActiveModel
assert_equal({
id: 42,
tags: [
{ 'attributes' => { 'id' => 1, 'name' => '#hash_tag' } }
{ 'id' => 1, 'name' => '#hash_tag' }
]
}.to_json, adapter.serializable_hash[:post].to_json)
end

44
test/fixtures/poro.rb vendored
View File

@ -1,44 +1,16 @@
verbose = $VERBOSE
$VERBOSE = nil
class Model
class Model < ActiveModelSerializers::Model
FILE_DIGEST = Digest::MD5.hexdigest(File.open(__FILE__).read)
def self.model_name
@_model_name ||= ActiveModel::Name.new(self)
end
def initialize(hash = {})
@attributes = hash
end
def cache_key
"#{self.class.name.downcase}/#{self.id}-#{self.updated_at.strftime("%Y%m%d%H%M%S%9N")}"
end
def serializable_hash(options = nil)
@attributes
end
def read_attribute_for_serialization(name)
if name == :id || name == 'id'
id
else
@attributes[name]
end
end
def id
@attributes[:id] || @attributes['id'] || object_id
end
### Helper methods, not required to be serializable
#
# Convenience for adding @attributes readers and writers
# Convenience when not adding @attributes readers and writers
def method_missing(meth, *args)
if meth.to_s =~ /^(.*)=$/
@attributes[$1.to_sym] = args[0]
elsif @attributes.key?(meth)
@attributes[meth]
attributes[$1.to_sym] = args[0]
elsif attributes.key?(meth)
attributes[meth]
else
super
end
@ -47,10 +19,6 @@ class Model
def cache_key_with_digest
"#{cache_key}/#{FILE_DIGEST}"
end
def updated_at
@attributes[:updated_at] ||= DateTime.now.to_time
end
end
class Profile < Model

View File

@ -24,6 +24,9 @@ module ActiveModel
def id
end
def updated_at
end
def self.model_name
@_model_name ||= ActiveModel::Name.new(self)
end

View File

@ -54,7 +54,7 @@ module ActiveModel
assert_equal key, :tags
assert_equal serializer, nil
assert_equal [{ attributes: { name: '#hashtagged' } }].to_json, options[:virtual_value].to_json
assert_equal [{ name: '#hashtagged' }].to_json, options[:virtual_value].to_json
end
end