Merge branch 'master' into domitian-move-namespace-of-adapter-to-active-model-serializers

Conflicts:
	CHANGELOG.md
	lib/active_model/serializer/adapter/attributes.rb
	lib/active_model/serializer/adapter/cached_serializer.rb
	lib/active_model/serializer/adapter/fragment_cache.rb
	lib/active_model/serializer/adapter/json_api.rb
	lib/active_model/serializer/adapter/json_api/link.rb
	test/adapter/fragment_cache_test.rb
	test/adapter/json_api/links_test.rb
	test/adapter/json_api/resource_type_config_test.rb
This commit is contained in:
Benjamin Fleischer 2016-02-23 23:21:49 -06:00
commit 25c9df0b97
39 changed files with 1357 additions and 341 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ tmp
.ruby-version
.ruby-gemset
vendor/bundle
tags

View File

@ -19,7 +19,6 @@ ENV['FULL_BUILD'] ||= ENV['CI']
# rubocop:enable Style/DoubleNegation
## CONFIGURE SIMPLECOV
SimpleCov.pid = $$ # In case there's any forking
SimpleCov.profiles.define 'app' do
coverage_dir 'coverage'

View File

@ -1,9 +1,30 @@
## 0.10.x
Breaking changes:
Features:
- [#1515](https://github.com/rails-api/active_model_serializers/pull/1515) Adds support for symbols to the
`ActiveModel::Serializer.type` method. (@groyoh)
- [#1504](https://github.com/rails-api/active_model_serializers/pull/1504) Adds the changes missing from #1454
and add more tests for resource identifier and relationship objects. Fix association block with link
returning `data: nil`.(@groyoh)
- [#1372](https://github.com/rails-api/active_model_serializers/pull/1372) Support
cache_store.read_multi. (@LcpMarvel)
- [#1018](https://github.com/rails-api/active_model_serializers/pull/1018) Add more tests and docs for top-level links. (@leandrocp)
- [#1454](https://github.com/rails-api/active_model_serializers/pull/1454) Add support for
relationship-level links and meta attributes. (@beauby)
- [#1340](https://github.com/rails-api/active_model_serializers/pull/1340) Add support for resource-level meta. (@beauby)
Fixes:
- [#1516](https://github.com/rails-api/active_model_serializers/pull/1501) No longer return a nil href when only
adding meta to a relationship link. (@groyoh)
- [#1458](https://github.com/rails-api/active_model_serializers/pull/1458) Preserve the serializer
type when fragment caching. (@bdmac)
- [#1477](https://github.com/rails-api/active_model_serializers/pull/1477) Fix `fragment_cached?`
method to check if caching. (@bdmac)
- [#1501](https://github.com/rails-api/active_model_serializers/pull/1501) Adds tests for SerializableResource::use_adapter?,doc typos (@domitian)
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)
Misc:
### v0.10.0.rc4 (2016/01/27 11:00 +00:00)
@ -415,7 +436,7 @@ Features:
'[Revert the serializers API as other alternatives are now also under discussion](https://github.com/rails/rails/commit/0a4035b12a6c59253cb60f9e3456513c6a6a9d33)'.
- [Proposed Implementation to Rails 3.2 by @wycats and @josevalim (November 25, 2011)](https://github.com/rails/rails/pull/3753)
- [Creation of `ActionController::Serialization`, initial serializer
support (September, 26 2011)](https://github.com/rails/rails/commit/8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe0).
support (September, 26 2011)](https://github.com/rails/rails/commit/8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe).
- [Docs and CHANGELOG](https://github.com/rails/rails/commit/696d01f7f4a8ed787924a41cce6df836cd73c46f)
- [Deprecation of ActiveModel::Serialization to ActiveModel::Serializable](https://github.com/rails/rails/blob/696d01f7f4a8ed787924a41cce6df836cd73c46f/activemodel/lib/active_model/serialization.rb)
- [Creation of `ActiveModel::Serialization` from `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/c6bc8e662614be711f45a8d4b231d5f993b024a7#diff-d029b9768d8df0407a35804a468e3ae5)

View File

@ -41,7 +41,6 @@ group :test do
gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
gem 'codeclimate-test-reporter', require: false
gem 'simplecov', '~> 0.10', require: false, group: :development
end
group :development, :test do

View File

@ -4,6 +4,7 @@
(Windows: [![Build status](https://ci.appveyor.com/api/projects/status/x6xdjydutm54gvyt/branch/master?svg=true)](https://ci.appveyor.com/project/joaomdmoura/active-model-serializers/branch/master))
[![Code Quality](https://codeclimate.com/github/rails-api/active_model_serializers/badges/gpa.svg)](https://codeclimate.com/github/rails-api/active_model_serializers)
[![Test Coverage](https://codeclimate.com/github/rails-api/active_model_serializers/badges/coverage.svg)](https://codeclimate.com/github/rails-api/active_model_serializers/coverage)
[![Issue Stats](http://issuestats.com/github/rails-api/active_model_serializers/badge/pr)](http://issuestats.com/github/rails-api/active_model_serializers) [![Issue Stats](http://issuestats.com/github/rails-api/active_model_serializers/badge/issue)](http://issuestats.com/github/rails-api/active_model_serializers)
## Documentation

View File

@ -29,7 +29,7 @@ else
Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
desc 'Execute rubocop'
RuboCop::RakeTask.new(:rubocop) do |task|
task.options = ['--rails', '--display-cop-names', '--display-style-guide']
task.options = ['--display-cop-names', '--display-style-guide']
task.fail_on_error = true
end
end

View File

@ -51,6 +51,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'will_paginate', '~> 3.0', '>= 3.0.7'
spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'simplecov', '~> 0.11'
spec.add_development_dependency 'timecop', '~> 0.7'
spec.add_development_dependency 'minitest-reporters'
spec.add_development_dependency 'grape', ['>= 0.13', '< 1.0']

View File

@ -56,7 +56,7 @@ High-level overview:
- `:each_serializer` specifies the serializer for each resource in the collection.
- For a single resource, the `:serializer` option is the resource serializer.
- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
[`ADAPTER_OPTIONS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializable_resource.rb#L4).
[`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializable_resource.rb#L4).
The remaining options are serializer options.
Details:
@ -64,7 +64,7 @@ Details:
1. **ActionController::Serialization**
1. `serializable_resource = ActiveModel::SerializableResource.new(resource, options)`
1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
The adapter options keys for the are defined by `ADAPTER_OPTIONS`.
The `adapter_opts` keys are defined in `ActiveModel::SerializableResource::ADAPTER_OPTION_KEYS`.
1. **ActiveModel::SerializableResource**
1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
- Where `serializer?` is `use_adapter? && !!(serializer)`

View File

@ -103,7 +103,46 @@ PR please :)
#### links
PR please :)
##### How to add top-level links
JsonApi supports a [links object](http://jsonapi.org/format/#document-links) to be specified at top-level, that you can specify in the `render`:
```ruby
links_object = {
href: "http://example.com/api/posts",
meta: {
count: 10
}
}
render json: @posts, links: links_object
```
That's the result:
```json
{
"data": [
{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API is awesome!",
"body": "You should be using JSON API",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
}
}
],
"links": {
"href": "http://example.com/api/posts",
"meta": {
"count": 10
}
}
}
```
This feature is specific to JsonApi, so you have to use the use the [JsonApi Adapter](adapters.md#jsonapi)
### serializer_opts

View File

@ -107,12 +107,30 @@ end
#### ::type
e.g.
The `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer.
It either takes a `String` or `Symbol` as parameter.
Note: This method is useful only when using the `:json_api` adapter.
Examples:
```ruby
class UserProfileSerializer < ActiveModel::Serializer
type 'profile'
end
class AuthorProfileSerializer < ActiveModel::Serializer
type :profile
end
```
With the `:json_api` adapter, the previous serializers would be rendered as:
``` json
{
"data": {
"id": "1",
"type": "profile"
}
}
```
#### ::link

View File

@ -74,32 +74,30 @@ ActiveModelSerializers pagination relies on a paginated collection with the meth
If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
In your action specify a custom serializer.
```ruby
render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer
```
Add this method to your base API controller.
And then, you could do something like the following class.
```ruby
class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer
def initialize(object, options={})
meta_key = options[:meta_key] || :meta
options[meta_key] ||= {}
options[meta_key] = {
current_page: object.current_page,
next_page: object.next_page,
prev_page: object.prev_page,
total_pages: object.total_pages,
total_count: object.total_count
}
super(object, options)
end
def pagination_dict(object)
{
current_page: object.current_page,
next_page: object.next_page,
prev_page: object.prev_page,
total_pages: object.total_pages,
total_count: object.total_count
}
end
```
Then, use it on your render method.
```ruby
render json: posts, meta: pagination_dict(posts)
```
ex.
```json
{
"articles": [
"posts": [
{
"id": 2,
"title": "JSON API paints my bikeshed!",

View File

@ -9,6 +9,7 @@ require 'active_model/serializer/configuration'
require 'active_model/serializer/fieldset'
require 'active_model/serializer/lint'
require 'active_model/serializer/links'
require 'active_model/serializer/meta'
require 'active_model/serializer/type'
# ActiveModel::Serializer is an abstract class that is
@ -20,6 +21,7 @@ module ActiveModel
include Attributes
include Caching
include Links
include Meta
include Type
# Deprecated
require 'active_model_serializers/adapter'
@ -70,7 +72,7 @@ module ActiveModel
# @api private
# Find a serializer from a class and caches the lookup.
# Preferentially retuns:
# Preferentially returns:
# 1. class name appended with "Serializer"
# 2. try again with superclass, if present
# 3. nil

View File

@ -0,0 +1,13 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
module ApiObjects
extend ActiveSupport::Autoload
autoload :Relationship
autoload :ResourceIdentifier
end
end
end
end
end

View File

@ -0,0 +1,52 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
module ApiObjects
class Relationship
def initialize(parent_serializer, serializer, options = {}, links = {}, meta = nil)
@object = parent_serializer.object
@scope = parent_serializer.scope
@options = options
@data = data_for(serializer, options)
@links = links.each_with_object({}) do |(key, value), hash|
hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
end
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
end
def as_json
hash = {}
hash[:data] = data if options[:include_data]
links = self.links
hash[:links] = links if links.any?
meta = self.meta
hash[:meta] = meta if meta
hash
end
protected
attr_reader :object, :scope, :data, :options, :links, :meta
private
def data_for(serializer, options)
if serializer.respond_to?(:each)
serializer.map { |s| ResourceIdentifier.new(s).as_json }
else
if options[:virtual_value]
options[:virtual_value]
elsif serializer && serializer.object
ResourceIdentifier.new(serializer).as_json
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,39 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
module ApiObjects
class ResourceIdentifier
def initialize(serializer)
@id = id_for(serializer)
@type = type_for(serializer)
end
def as_json
{ id: id, type: type }
end
protected
attr_reader :id, :type
private
def type_for(serializer)
return serializer._type if serializer._type
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
serializer.object.class.model_name.singular
else
serializer.object.class.model_name.plural
end
end
def id_for(serializer)
serializer.read_attribute_for_serialization(:id).to_s
end
end
end
end
end
end
end

View File

@ -0,0 +1,29 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
class Meta
def initialize(serializer)
@object = serializer.object
@scope = serializer.scope
# Use the return value of the block unless it is nil.
if serializer._meta.respond_to?(:call)
@value = instance_eval(&serializer._meta)
else
@value = serializer._meta
end
end
def as_json
@value
end
protected
attr_reader :object, :scope
end
end
end
end
end

View File

@ -9,7 +9,7 @@ module ActiveModel
# @example
# Association.new(:comments, CommentSummarySerializer)
#
Association = Struct.new(:name, :serializer, :options) do
Association = Struct.new(:name, :serializer, :options, :links, :meta) do
# @return [Symbol]
#
def key

View File

@ -0,0 +1,29 @@
module ActiveModel
class Serializer
module Meta
extend ActiveSupport::Concern
included do
with_options instance_writer: false, instance_reader: true do |serializer|
serializer.class_attribute :_meta # @api private
end
extend ActiveSupport::Autoload
end
module ClassMethods
# Set the JSON API meta attribute of a serializer.
# @example
# class AdminAuthorSerializer < ActiveModel::Serializer
# meta { stuff: 'value' }
# @example
# meta do
# { comment_count: object.comments.count }
# end
def meta(value = nil, &block)
self._meta = block || value
end
end
end
end
end

View File

@ -34,6 +34,43 @@ module ActiveModel
# So you can inspect reflections in your Adapters.
#
class Reflection < Field
def initialize(*)
super
@_links = {}
@_include_data = true
end
def link(name, value = nil, &block)
@_links[name] = block || value
:nil
end
def meta(value = nil, &block)
@_meta = block || value
:nil
end
def include_data(value = true)
@_include_data = value
:nil
end
def value(serializer)
@object = serializer.object
@scope = serializer.scope
if block
block_value = instance_eval(&block)
if block_value == :nil
serializer.read_attribute_for_serialization(name)
else
block_value
end
else
serializer.read_attribute_for_serialization(name)
end
end
# Build association. This method is used internally to
# build serializer's association by its reflection.
#
@ -59,6 +96,7 @@ module ActiveModel
association_value = value(subject)
reflection_options = options.dup
serializer_class = subject.class.serializer_for(association_value, reflection_options)
reflection_options[:include_data] = @_include_data
if serializer_class
begin
@ -73,9 +111,13 @@ module ActiveModel
reflection_options[:virtual_value] = association_value
end
Association.new(name, serializer, reflection_options)
Association.new(name, serializer, reflection_options, @_links, @_meta)
end
protected
attr_accessor :object, :scope
private
def serializer_options(subject, parent_serializer_options, reflection_options)

View File

@ -17,7 +17,7 @@ module ActiveModel
# class AdminAuthorSerializer < ActiveModel::Serializer
# type 'authors'
def type(type)
self._type = type
self._type = type && type.to_s
end
end
end

View File

@ -4,6 +4,7 @@ module ActiveModelSerializers
def initialize(serializer, options = {})
super
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*')
@cached_attributes = options[:cache_attributes] || {}
end
def serializable_hash(options = nil)
@ -23,9 +24,38 @@ module ActiveModelSerializers
private
def serializable_hash_for_collection(options)
cache_attributes
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
end
# Read cache from cache_store
# @return [Hash]
def cache_read_multi
return {} if ActiveModelSerializers.config.cache_store.blank?
keys = CachedSerializer.object_cache_keys(serializer, @include_tree)
return {} if keys.blank?
ActiveModelSerializers.config.cache_store.read_multi(*keys)
end
# Set @cached_attributes
def cache_attributes
return if @cached_attributes.present?
@cached_attributes = cache_read_multi
end
# Get attributes from @cached_attributes
# @return [Hash] cached attributes
def cached_attributes(cached_serializer)
return yield unless cached_serializer.cached?
@cached_attributes.fetch(cached_serializer.cache_key) { yield }
end
def serializable_hash_for_single_resource(options)
resource = resource_object_for(options)
relationships = resource_relationships(options)
@ -55,8 +85,12 @@ module ActiveModelSerializers
end
def resource_object_for(options)
cache_check(serializer) do
serializer.attributes(options[:fields])
cached_serializer = CachedSerializer.new(serializer)
cached_attributes(cached_serializer) do
cached_serializer.cache_check(self) do
serializer.attributes(options[:fields])
end
end
end
end

View File

@ -23,14 +23,16 @@ module ActiveModelSerializers
end
def fragment_cached?
@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
@klass._cache && (@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except)
end
def cache_key
return @cache_key if defined?(@cache_key)
parts = []
parts << object_cache_key
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
parts.join('/')
@cache_key = parts.join('/')
end
def object_cache_key
@ -38,6 +40,38 @@ module ActiveModelSerializers
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key
end
# find all cache_key for the collection_serializer
# @param collection_serializer
# @param include_tree
# @return [Array] all cache_key of collection_serializer
def self.object_cache_keys(serializers, include_tree)
cache_keys = []
serializers.each do |serializer|
cache_keys << object_cache_key(serializer)
serializer.associations(include_tree).each do |association|
if association.serializer.respond_to?(:each)
association.serializer.each do |sub_serializer|
cache_keys << object_cache_key(sub_serializer)
end
else
cache_keys << object_cache_key(association.serializer)
end
end
end
cache_keys.compact.uniq
end
# @return [String, nil] the cache_key of the serializer or nil
def self.object_cache_key(serializer)
return unless serializer.present? && serializer.object.present?
cached_serializer = new(serializer)
cached_serializer.cached? ? cached_serializer.cache_key : nil
end
end
end
end

View File

@ -93,6 +93,10 @@ module ActiveModelSerializers
cached.constantize.cache(klass._cache_options)
# Preserve the type setting in the cached/non-cached serializer classes
cached.constantize.type(klass._type)
non_cached.constantize.type(klass._type)
cached.constantize.fragmented(serializer)
non_cached.constantize.fragmented(serializer)

View File

@ -5,7 +5,9 @@ module ActiveModelSerializers
autoload :PaginationLinks
autoload :FragmentCache
autoload :Link
require 'active_model/serializer/adapter/json_api/meta'
autoload :Deserialization
require 'active_model/serializer/adapter/json_api/api_objects'
# TODO: if we like this abstraction and other API objects to it,
# then extract to its own file and require it.
@ -96,7 +98,7 @@ module ActiveModelSerializers
end
def process_resource(serializer, primary)
resource_identifier = resource_identifier_for(serializer)
resource_identifier = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::ResourceIdentifier.new(serializer).as_json
return false unless @resource_identifiers.add?(resource_identifier)
resource_object = resource_object_for(serializer)
@ -126,37 +128,13 @@ module ActiveModelSerializers
process_relationships(serializer, include_tree)
end
def resource_identifier_type_for(serializer)
return serializer._type if serializer._type
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
serializer.object.class.model_name.singular
else
serializer.object.class.model_name.plural
end
end
def resource_identifier_id_for(serializer)
if serializer.respond_to?(:id)
serializer.id
else
serializer.object.id
end
end
def resource_identifier_for(serializer)
type = resource_identifier_type_for(serializer)
id = resource_identifier_id_for(serializer)
{ id: id.to_s, type: type }
end
def attributes_for(serializer, fields)
serializer.attributes(fields).except(:id)
end
def resource_object_for(serializer)
resource_object = cache_check(serializer) do
resource_object = resource_identifier_for(serializer)
resource_object = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::ResourceIdentifier.new(serializer).as_json
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields)
@ -164,33 +142,29 @@ module ActiveModelSerializers
resource_object
end
relationships = relationships_for(serializer)
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
relationships = relationships_for(serializer, requested_associations)
resource_object[:relationships] = relationships if relationships.any?
links = links_for(serializer)
resource_object[:links] = links if links.any?
meta = meta_for(serializer)
resource_object[:meta] = meta unless meta.nil?
resource_object
end
def relationship_value_for(serializer, options = {})
if serializer.respond_to?(:each)
serializer.map { |s| resource_identifier_for(s) }
else
if options[:virtual_value]
options[:virtual_value]
elsif serializer && serializer.object
resource_identifier_for(serializer)
end
end
end
def relationships_for(serializer)
resource_type = resource_identifier_type_for(serializer)
requested_associations = fieldset.fields_for(resource_type) || '*'
def relationships_for(serializer, requested_associations)
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
serializer.associations(include_tree).each_with_object({}) do |association, hash|
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
hash[association.key] = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::Relationship.new(
serializer,
association.serializer,
association.options,
association.links,
association.meta
).as_json
end
end
@ -203,6 +177,10 @@ module ActiveModelSerializers
def pagination_links_for(serializer, options)
JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
end
def meta_for(serializer)
ActiveModel::Serializer::Adapter::JsonApi::Meta.new(serializer).as_json
end
end
end
end

View File

@ -27,8 +27,9 @@ module ActiveModelSerializers
def as_json
return @value if @value
hash = { href: @href }
hash.merge!(meta: @meta) if @meta
hash = {}
hash[:href] = @href if @href
hash[:meta] = @meta if @meta
hash
end

View File

@ -45,6 +45,16 @@ module ActionController
render json: @profiles, meta: { total: 10 }
end
def render_array_using_implicit_serializer_and_links
with_adapter ActiveModelSerializers::Adapter::JsonApi do
@profiles = [
Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
]
render json: @profiles, links: { self: 'http://example.com/api/profiles/1' }
end
end
def render_object_with_cache_enabled
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@author = Author.new(id: 1, name: 'Joao Moura.')
@ -254,6 +264,29 @@ module ActionController
assert_equal expected.to_json, @response.body
end
def test_render_array_using_implicit_serializer_and_links
get :render_array_using_implicit_serializer_and_links
expected = {
data: [
{
id: assigns(:profiles).first.id.to_s,
type: 'profiles',
attributes: {
name: 'Name 1',
description: 'Description 1'
}
}
],
links: {
self: 'http://example.com/api/profiles/1'
}
}
assert_equal 'application/json', @response.content_type
assert_equal expected.to_json, @response.body
end
def test_render_with_cache_enable
expected = {
id: 1,

View File

@ -2,6 +2,14 @@ require 'test_helper'
module ActiveModelSerializers
module Adapter
class FragmentCacheTest < ActiveSupport::TestCase
TypedRoleSerializer = Class.new(ActiveModel::Serializer) do
type 'my-roles'
cache only: [:name], skip_digest: true
attributes :id, :name, :description
belongs_to :author
end
def setup
super
@spam = Spam::UnrelatedLink.new(id: 'spam-id-1')
@ -30,7 +38,11 @@ module ActiveModelSerializers
}
assert_equal(@spam_hash.fetch, expected_result)
end
def test_fragment_fetch_with_type_override
serialization = serializable(Role.new(name: 'Another Author'), serializer: TypedRoleSerializer, adapter: :json_api).serializable_hash
assert_equal(TypedRoleSerializer._type, serialization.fetch(:data).fetch(:type))
end
end
end
end

View File

@ -0,0 +1,168 @@
require 'test_helper'
module ActiveModel
class Serializer
module Adapter
class JsonApi
module ApiObjects
class RelationshipTest < ActiveSupport::TestCase
def setup
@blog = Blog.new(id: 1)
@author = Author.new(id: 1, name: 'Steve K.', blog: @blog)
@serializer = BlogSerializer.new(@blog)
ActionController::Base.cache_store.clear
end
def test_relationship_with_data
expected = {
data: {
id: '1',
type: 'blogs'
}
}
test_relationship(expected, options: { include_data: true })
end
def test_relationship_with_nil_model
@serializer = BlogSerializer.new(nil)
expected = { data: nil }
test_relationship(expected, options: { include_data: true })
end
def test_relationship_with_nil_serializer
@serializer = nil
expected = { data: nil }
test_relationship(expected, options: { include_data: true })
end
def test_relationship_with_data_array
posts = [Post.new(id: 1), Post.new(id: 2)]
@serializer = ActiveModel::Serializer::CollectionSerializer.new(posts)
@author.posts = posts
@author.blog = nil
expected = {
data: [
{
id: '1',
type: 'posts'
},
{
id: '2',
type: 'posts'
}
]
}
test_relationship(expected, options: { include_data: true })
end
def test_relationship_data_not_included
test_relationship({}, options: { include_data: false })
end
def test_relationship_simple_link
links = { self: 'a link' }
test_relationship({ links: { self: 'a link' } }, links: links)
end
def test_relationship_many_links
links = {
self: 'a link',
related: 'another link'
}
expected = {
links: {
self: 'a link',
related: 'another link'
}
}
test_relationship(expected, links: links)
end
def test_relationship_block_link
links = { self: proc { "#{object.id}" } }
expected = { links: { self: "#{@blog.id}" } }
test_relationship(expected, links: links)
end
def test_relationship_block_link_with_meta
links = {
self: proc do
href "#{object.id}"
meta(id: object.id)
end
}
expected = {
links: {
self: {
href: "#{@blog.id}",
meta: { id: @blog.id }
}
}
}
test_relationship(expected, links: links)
end
def test_relationship_simple_meta
meta = { id: '1' }
expected = { meta: meta }
test_relationship(expected, meta: meta)
end
def test_relationship_block_meta
meta = proc do
{ id: object.id }
end
expected = {
meta: {
id: @blog.id
}
}
test_relationship(expected, meta: meta)
end
def test_relationship_with_everything
links = {
self: 'a link',
related: proc do
href "#{object.id}"
meta object.id
end
}
meta = proc do
{ id: object.id }
end
expected = {
data: {
id: '1',
type: 'blogs'
},
links: {
self: 'a link',
related: {
href: '1', meta: 1
}
},
meta: {
id: @blog.id
}
}
test_relationship(expected, meta: meta, options: { include_data: true }, links: links)
end
private
def test_relationship(expected, params = {})
options = params.fetch(:options, {})
links = params.fetch(:links, {})
meta = params[:meta]
parent_serializer = AuthorSerializer.new(@author)
relationship = Relationship.new(parent_serializer, @serializer, options, links, meta)
assert_equal(expected, relationship.as_json)
end
end
end
end
end
end
end

View File

@ -0,0 +1,88 @@
require 'test_helper'
module ActiveModel
class Serializer
module Adapter
class JsonApi
module ApiObjects
class ResourceIdentifierTest < ActiveSupport::TestCase
class WithDefinedTypeSerializer < Serializer
type 'with_defined_type'
end
class WithDefinedIdSerializer < Serializer
def id
'special_id'
end
end
class FragmentedSerializer < Serializer; end
def setup
@model = Author.new(id: 1, name: 'Steve K.')
ActionController::Base.cache_store.clear
end
def test_defined_type
test_type(WithDefinedTypeSerializer, 'with_defined_type')
end
def test_singular_type
test_type_inflection(AuthorSerializer, 'author', :singular)
end
def test_plural_type
test_type_inflection(AuthorSerializer, 'authors', :plural)
end
def test_id_defined_on_object
test_id(AuthorSerializer, @model.id.to_s)
end
def test_id_defined_on_serializer
test_id(WithDefinedIdSerializer, 'special_id')
end
def test_id_defined_on_fragmented
FragmentedSerializer.fragmented(WithDefinedIdSerializer.new(@author))
test_id(FragmentedSerializer, 'special_id')
end
private
def test_type_inflection(serializer_class, expected_type, inflection)
original_inflection = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = inflection
test_type(serializer_class, expected_type)
ActiveModelSerializers.config.jsonapi_resource_type = original_inflection
end
def test_type(serializer_class, expected_type)
serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer)
expected = {
id: @model.id.to_s,
type: expected_type
}
assert_equal(expected, resource_identifier.as_json)
end
def test_id(serializer_class, id)
serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer)
inflection = ActiveModelSerializers.config.jsonapi_resource_type
type = @model.class.model_name.send(inflection)
expected = {
id: id,
type: type
}
assert_equal(expected, resource_identifier.as_json)
end
end
end
end
end
end
end

View File

@ -20,7 +20,7 @@ module ActiveModelSerializers
def setup
@post = Post.new(id: 1337, comments: [], author: nil)
@author = LinkAuthor.new(id: 1337)
@author = LinkAuthor.new(id: 1337, posts: [@post])
end
def test_toplevel_links
@ -46,6 +46,24 @@ module ActiveModelSerializers
assert_equal(expected, hash[:links])
end
def test_nil_toplevel_links
hash = ActiveModel::SerializableResource.new(
@post,
adapter: :json_api,
links: nil
).serializable_hash
refute hash.key?(:links), 'No links key to be output'
end
def test_nil_toplevel_links_json_adapter
hash = ActiveModel::SerializableResource.new(
@post,
adapter: :json,
links: nil
).serializable_hash
refute hash.key?(:links), 'No links key to be output'
end
def test_resource_links
hash = serializable(@author, adapter: :json_api).serializable_hash
expected = {

View File

@ -0,0 +1,192 @@
require 'test_helper'
module ActiveModel
class Serializer
module Adapter
class JsonApi
class RelationshipTest < ActiveSupport::TestCase
RelationshipAuthor = Class.new(::Model)
class RelationshipAuthorSerializer < ActiveModel::Serializer
has_one :bio do
link :self, '//example.com/link_author/relationships/bio'
end
has_one :profile do
link :related do
"//example.com/profiles/#{object.profile.id}"
end
end
has_many :locations do
link :related do
ids = object.locations.map(&:id).join(',')
href "//example.com/locations/#{ids}"
end
end
has_many :posts do
link :related do
ids = object.posts.map(&:id).join(',')
href "//example.com/posts/#{ids}"
meta ids: ids
end
end
has_many :comments do
link :self do
meta ids: [1]
end
end
has_many :roles do
meta count: object.posts.count
end
has_one :blog do
link :self, '//example.com/link_author/relationships/blog'
include_data false
end
belongs_to :reviewer do
meta name: 'Dan Brown'
include_data true
end
has_many :likes do
link :related do
ids = object.likes.map(&:id).join(',')
href "//example.com/likes/#{ids}"
meta ids: ids
end
meta liked: object.likes.any?
end
end
def setup
@post = Post.new(id: 1337, comments: [], author: nil)
@blog = Blog.new(id: 1337, name: 'extra')
@bio = Bio.new(id: 1337)
@like = Like.new(id: 1337)
@role = Role.new(id: 1337)
@profile = Profile.new(id: 1337)
@location = Location.new(id: 1337)
@reviewer = Author.new(id: 1337)
@comment = Comment.new(id: 1337)
@author = RelationshipAuthor.new(
id: 1337,
posts: [@post],
blog: @blog,
reviewer: @reviewer,
bio: @bio,
likes: [@like],
roles: [@role],
locations: [@location],
profile: @profile,
comments: [@comment]
)
end
def test_relationship_simple_link
expected = {
data: {
id: '1337',
type: 'bios'
},
links: {
self: '//example.com/link_author/relationships/bio'
}
}
assert_relationship(:bio, expected)
end
def test_relationship_block_link
expected = {
data: { id: '1337', type: 'profiles' },
links: { related: '//example.com/profiles/1337' }
}
assert_relationship(:profile, expected)
end
def test_relationship_block_link_href
expected = {
data: [{ id: '1337', type: 'locations' }],
links: {
related: { href: '//example.com/locations/1337' }
}
}
assert_relationship(:locations, expected)
end
def test_relationship_block_link_href_and_meta
expected = {
data: [{ id: '1337', type: 'posts' }],
links: {
related: {
href: '//example.com/posts/1337',
meta: { ids: '1337' }
}
}
}
assert_relationship(:posts, expected)
end
def test_relationship_block_link_meta
expected = {
data: [{ id: '1337', type: 'comments' }],
links: {
self: {
meta: { ids: [1] }
}
}
}
assert_relationship(:comments, expected)
end
def test_relationship_meta
expected = {
data: [{ id: '1337', type: 'roles' }],
meta: { count: 1 }
}
assert_relationship(:roles, expected)
end
def test_relationship_not_including_data
expected = {
links: { self: '//example.com/link_author/relationships/blog' }
}
assert_relationship(:blog, expected)
end
def test_relationship_including_data_explicit
expected = {
data: { id: '1337', type: 'authors' },
meta: { name: 'Dan Brown' }
}
assert_relationship(:reviewer, expected)
end
def test_relationship_with_everything
expected = {
data: [{ id: '1337', type: 'likes' }],
links: {
related: {
href: '//example.com/likes/1337',
meta: { ids: '1337' }
}
},
meta: { liked: true }
}
assert_relationship(:likes, expected)
end
private
def assert_relationship(relationship_name, expected)
hash = serializable(@author, adapter: :json_api).serializable_hash
assert_equal(expected, hash[:data][:relationships][relationship_name])
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'test_helper'
module ActiveModel
class Serializer
module Adapter
class JsonApi
class ResourceMetaTest < Minitest::Test
class MetaHashPostSerializer < ActiveModel::Serializer
attributes :id
meta stuff: 'value'
end
class MetaBlockPostSerializer < ActiveModel::Serializer
attributes :id
meta do
{ comments_count: object.comments.count }
end
end
def setup
@post = Post.new(id: 1337, comments: [], author: nil)
end
def test_meta_hash_object_resource
hash = ActiveModel::SerializableResource.new(
@post,
serializer: MetaHashPostSerializer,
adapter: :json_api
).serializable_hash
expected = {
stuff: 'value'
}
assert_equal(expected, hash[:data][:meta])
end
def test_meta_block_object_resource
hash = ActiveModel::SerializableResource.new(
@post,
serializer: MetaBlockPostSerializer,
adapter: :json_api
).serializable_hash
expected = {
comments_count: @post.comments.count
}
assert_equal(expected, hash[:data][:meta])
end
def test_meta_object_resource_in_array
post2 = Post.new(id: 1339, comments: [Comment.new])
posts = [@post, post2]
hash = ActiveModel::SerializableResource.new(
posts,
each_serializer: MetaBlockPostSerializer,
adapter: :json_api
).serializable_hash
expected = {
:data => [
{ :id => '1337', :type => 'posts', :meta => { :comments_count => 0 } },
{ :id => '1339', :type => 'posts', :meta => { :comments_count => 1 } }
]
}
assert_equal(expected, hash)
end
end
end
end
end
end

View File

@ -1,69 +0,0 @@
require 'test_helper'
module ActiveModelSerializers
module Adapter
class JsonApi
class ResourceTypeConfigTest < ActiveSupport::TestCase
class ProfileTypeSerializer < ActiveModel::Serializer
attributes :name
type 'profile'
end
def setup
@author = Author.new(id: 1, name: 'Steve K.')
@author.bio = nil
@author.roles = []
@blog = Blog.new(id: 23, name: 'AMS Blog')
@post = Post.new(id: 42, title: 'New Post', body: 'Body')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post.comments = [@comment]
@post.blog = @blog
@anonymous_post.comments = []
@anonymous_post.blog = nil
@comment.post = @post
@comment.author = nil
@post.author = @author
@anonymous_post.author = nil
@blog = Blog.new(id: 1, name: 'My Blog!!')
@blog.writer = @author
@blog.articles = [@post, @anonymous_post]
@author.posts = []
end
def with_jsonapi_resource_type type
old_type = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = type
yield
ensure
ActiveModelSerializers.config.jsonapi_resource_type = old_type
end
def test_config_plural
with_jsonapi_resource_type :plural do
hash = serializable(@comment, adapter: :json_api).serializable_hash
assert_equal('comments', hash[:data][:type])
end
end
def test_config_singular
with_jsonapi_resource_type :singular do
hash = serializable(@comment, adapter: :json_api).serializable_hash
assert_equal('comment', hash[:data][:type])
end
end
def test_explicit_type_value
hash = serializable(@author, serializer: ProfileTypeSerializer, adapter: :json_api).serializable_hash
assert_equal('profile', hash.fetch(:data).fetch(:type))
end
private
def serializable(resource, options = {})
ActiveModel::SerializableResource.new(resource, options)
end
end
end
end
end

View File

@ -0,0 +1,61 @@
require 'test_helper'
module ActiveModel
class Serializer
module Adapter
class JsonApi
class TypeTest < ActiveSupport::TestCase
class StringTypeSerializer < ActiveModel::Serializer
attribute :name
type 'profile'
end
class SymbolTypeSerializer < ActiveModel::Serializer
attribute :name
type :profile
end
setup do
@author = Author.new(id: 1, name: 'Steve K.')
end
def test_config_plural
with_jsonapi_resource_type :plural do
assert_type(@author, 'authors')
end
end
def test_config_singular
with_jsonapi_resource_type :singular do
assert_type(@author, 'author')
end
end
def test_explicit_string_type_value
assert_type(@author, 'profile', serializer: StringTypeSerializer)
end
def test_explicit_symbol_type_value
assert_type(@author, 'profile', serializer: SymbolTypeSerializer)
end
private
def assert_type(resource, expected_type, opts = {})
opts = opts.reverse_merge(adapter: :json_api)
hash = serializable(resource, opts).serializable_hash
assert_equal(expected_type, hash.fetch(:data).fetch(:type))
end
def with_jsonapi_resource_type inflection
old_inflection = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = inflection
yield
ensure
ActiveModelSerializers.config.jsonapi_resource_type = old_inflection
end
end
end
end
end
end

View File

@ -23,5 +23,13 @@ module ActiveModel
options = nil
assert_equal @adapter.as_json(options), @serializable_resource.as_json(options)
end
def test_use_adapter_with_adapter_option
assert ActiveModel::SerializableResource.new(@resource, { adapter: 'json' }).use_adapter?
end
def test_use_adapter_with_adapter_option_as_false
refute ActiveModel::SerializableResource.new(@resource, { adapter: false }).use_adapter?
end
end
end

View File

@ -32,13 +32,13 @@ module ActiveModel
case key
when :posts
assert_equal({}, options)
assert_equal({ include_data: true }, options)
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
when :bio
assert_equal({}, options)
assert_equal({ include_data: true }, options)
assert_nil serializer
when :roles
assert_equal({}, options)
assert_equal({ include_data: true }, options)
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
else
flunk "Unknown association: #{key}"
@ -80,7 +80,7 @@ module ActiveModel
flunk "Unknown association: #{key}"
end
assert_equal({}, association.options)
assert_equal({ include_data: true }, association.options)
end
end

View File

@ -1,209 +1,238 @@
require 'test_helper'
require 'tmpdir'
require 'tempfile'
module ActiveModel
class Serializer
class CacheTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
module ActiveModelSerializers
class CacheTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
def setup
ActionController::Base.cache_store.clear
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post = Post.new(title: 'New Post', body: 'Body')
@bio = Bio.new(id: 1, content: 'AMS Contributor')
@author = Author.new(name: 'Joao M. D. Moura')
@blog = Blog.new(id: 999, name: 'Custom blog', writer: @author, articles: [])
@role = Role.new(name: 'Great Author')
@location = Location.new(lat: '-23.550520', lng: '-46.633309')
@place = Place.new(name: 'Amazing Place')
@author.posts = [@post]
@author.roles = [@role]
@role.author = @author
@author.bio = @bio
@bio.author = @author
@post.comments = [@comment]
@post.author = @author
@comment.post = @post
@comment.author = @author
@post.blog = @blog
@location.place = @place
def setup
ActionController::Base.cache_store.clear
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post = Post.new(title: 'New Post', body: 'Body')
@bio = Bio.new(id: 1, content: 'AMS Contributor')
@author = Author.new(name: 'Joao M. D. Moura')
@blog = Blog.new(id: 999, name: 'Custom blog', writer: @author, articles: [])
@role = Role.new(name: 'Great Author')
@location = Location.new(lat: '-23.550520', lng: '-46.633309')
@place = Place.new(name: 'Amazing Place')
@author.posts = [@post]
@author.roles = [@role]
@role.author = @author
@author.bio = @bio
@bio.author = @author
@post.comments = [@comment]
@post.author = @author
@comment.post = @post
@comment.author = @author
@post.blog = @blog
@location.place = @place
@location_serializer = LocationSerializer.new(@location)
@bio_serializer = BioSerializer.new(@bio)
@role_serializer = RoleSerializer.new(@role)
@post_serializer = PostSerializer.new(@post)
@author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment)
@blog_serializer = BlogSerializer.new(@blog)
@location_serializer = LocationSerializer.new(@location)
@bio_serializer = BioSerializer.new(@bio)
@role_serializer = RoleSerializer.new(@role)
@post_serializer = PostSerializer.new(@post)
@author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment)
@blog_serializer = BlogSerializer.new(@blog)
end
def test_inherited_cache_configuration
inherited_serializer = Class.new(PostSerializer)
assert_equal PostSerializer._cache_key, inherited_serializer._cache_key
assert_equal PostSerializer._cache_options, inherited_serializer._cache_options
end
def test_override_cache_configuration
inherited_serializer = Class.new(PostSerializer) do
cache key: 'new-key'
end
def test_inherited_cache_configuration
inherited_serializer = Class.new(PostSerializer)
assert_equal PostSerializer._cache_key, 'post'
assert_equal inherited_serializer._cache_key, 'new-key'
end
assert_equal PostSerializer._cache_key, inherited_serializer._cache_key
assert_equal PostSerializer._cache_options, inherited_serializer._cache_options
def test_cache_definition
assert_equal(ActionController::Base.cache_store, @post_serializer.class._cache)
assert_equal(ActionController::Base.cache_store, @author_serializer.class._cache)
assert_equal(ActionController::Base.cache_store, @comment_serializer.class._cache)
end
def test_cache_key_definition
assert_equal('post', @post_serializer.class._cache_key)
assert_equal('writer', @author_serializer.class._cache_key)
assert_equal(nil, @comment_serializer.class._cache_key)
end
def test_cache_key_interpolation_with_updated_at
render_object_with_cache(@author)
assert_equal(nil, ActionController::Base.cache_store.fetch(@author.cache_key))
assert_equal(@author_serializer.attributes.to_json, ActionController::Base.cache_store.fetch("#{@author_serializer.class._cache_key}/#{@author_serializer.object.id}-#{@author_serializer.object.updated_at.strftime("%Y%m%d%H%M%S%9N")}").to_json)
end
def test_default_cache_key_fallback
render_object_with_cache(@comment)
assert_equal(@comment_serializer.attributes.to_json, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
end
def test_cache_options_definition
assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options)
assert_equal(nil, @blog_serializer.class._cache_options)
assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options)
end
def test_fragment_cache_definition
assert_equal([:name], @role_serializer.class._cache_only)
assert_equal([:content], @bio_serializer.class._cache_except)
end
def test_associations_separately_cache
ActionController::Base.cache_store.clear
assert_equal(nil, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(nil, ActionController::Base.cache_store.fetch(@comment.cache_key))
Timecop.freeze(Time.now) do
render_object_with_cache(@post)
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
end
end
def test_override_cache_configuration
inherited_serializer = Class.new(PostSerializer) do
cache key: 'new-key'
end
def test_associations_cache_when_updated
# Clean the Cache
ActionController::Base.cache_store.clear
assert_equal PostSerializer._cache_key, 'post'
assert_equal inherited_serializer._cache_key, 'new-key'
Timecop.freeze(Time.now) do
# Generate a new Cache of Post object and each objects related to it.
render_object_with_cache(@post)
# Check if it cached the objects separately
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
# Simulating update on comments relationship with Post
new_comment = Comment.new(id: 2, body: 'ZOMG A NEW COMMENT')
new_comment_serializer = CommentSerializer.new(new_comment)
@post.comments = [new_comment]
# Ask for the serialized object
render_object_with_cache(@post)
# Check if the the new comment was cached
assert_equal(new_comment_serializer.attributes, ActionController::Base.cache_store.fetch(new_comment.cache_key))
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
end
end
def test_cache_definition
assert_equal(ActionController::Base.cache_store, @post_serializer.class._cache)
assert_equal(ActionController::Base.cache_store, @author_serializer.class._cache)
assert_equal(ActionController::Base.cache_store, @comment_serializer.class._cache)
end
def test_fragment_fetch_with_virtual_associations
expected_result = {
id: @location.id,
lat: @location.lat,
lng: @location.lng,
place: 'Nowhere'
}
def test_cache_key_definition
assert_equal('post', @post_serializer.class._cache_key)
assert_equal('writer', @author_serializer.class._cache_key)
assert_equal(nil, @comment_serializer.class._cache_key)
end
hash = render_object_with_cache(@location)
def test_cache_key_interpolation_with_updated_at
render_object_with_cache(@author)
assert_equal(nil, ActionController::Base.cache_store.fetch(@author.cache_key))
assert_equal(@author_serializer.attributes.to_json, ActionController::Base.cache_store.fetch("#{@author_serializer.class._cache_key}/#{@author_serializer.object.id}-#{@author_serializer.object.updated_at.strftime("%Y%m%d%H%M%S%9N")}").to_json)
end
assert_equal(hash, expected_result)
assert_equal({ place: 'Nowhere' }, ActionController::Base.cache_store.fetch(@location.cache_key))
end
def test_default_cache_key_fallback
def test_uses_file_digest_in_cache_key
render_object_with_cache(@blog)
assert_equal(@blog_serializer.attributes, ActionController::Base.cache_store.fetch(@blog.cache_key_with_digest))
end
def test_cache_digest_definition
assert_equal(::Model::FILE_DIGEST, @post_serializer.class._cache_digest)
end
def test_object_cache_keys
serializer = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args('*')
actual = Adapter::CachedSerializer.object_cache_keys(serializer, include_tree)
assert_equal actual.size, 3
assert actual.any? { |key| key == 'comment/1' }
assert actual.any? { |key| key =~ %r{post/post-\d+} }
assert actual.any? { |key| key =~ %r{writer/author-\d+} }
end
def test_cached_attributes
serializer = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
Timecop.freeze(Time.now) do
render_object_with_cache(@comment)
assert_equal(@comment_serializer.attributes.to_json, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
attributes = Adapter::Attributes.new(serializer)
attributes.send(:cache_attributes)
cached_attributes = attributes.instance_variable_get(:@cached_attributes)
assert_equal cached_attributes[@comment.cache_key], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes
assert_equal cached_attributes[@comment.post.cache_key], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes
writer = @comment.post.blog.writer
writer_cache_key = "writer/#{writer.id}-#{writer.updated_at.strftime("%Y%m%d%H%M%S%9N")}"
assert_equal cached_attributes[writer_cache_key], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes
end
end
def test_cache_options_definition
assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options)
assert_equal(nil, @blog_serializer.class._cache_options)
assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options)
end
def test_serializer_file_path_on_nix
path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_fragment_cache_definition
assert_equal([:name], @role_serializer.class._cache_only)
assert_equal([:content], @bio_serializer.class._cache_except)
end
def test_serializer_file_path_on_windows
path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_associations_separately_cache
ActionController::Base.cache_store.clear
assert_equal(nil, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(nil, ActionController::Base.cache_store.fetch(@comment.cache_key))
def test_serializer_file_path_with_space
path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
Timecop.freeze(Time.now) do
render_object_with_cache(@post)
def test_serializer_file_path_with_submatch
# The submatch in the path ensures we're using a correctly greedy regexp.
path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
end
end
def test_digest_caller_file
contents = "puts 'AMS rocks'!"
dir = Dir.mktmpdir('space char')
file = Tempfile.new('some_ruby.rb', dir)
file.write(contents)
path = file.path
caller_line = "#{path}:1:in `<top (required)>'"
file.close
assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents)
ensure
file.unlink
FileUtils.remove_entry dir
end
def test_associations_cache_when_updated
# Clean the Cache
ActionController::Base.cache_store.clear
def test_warn_on_serializer_not_defined_in_file
called = false
serializer = Class.new(ActiveModel::Serializer)
assert_match(/_cache_digest/, (capture(:stderr) do
serializer.digest_caller_file('')
called = true
end))
assert called
end
Timecop.freeze(Time.now) do
# Generate a new Cache of Post object and each objects related to it.
render_object_with_cache(@post)
private
# Check if it cached the objects separately
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
# Simulating update on comments relationship with Post
new_comment = Comment.new(id: 2, body: 'ZOMG A NEW COMMENT')
new_comment_serializer = CommentSerializer.new(new_comment)
@post.comments = [new_comment]
# Ask for the serialized object
render_object_with_cache(@post)
# Check if the the new comment was cached
assert_equal(new_comment_serializer.attributes, ActionController::Base.cache_store.fetch(new_comment.cache_key))
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
end
end
def test_fragment_fetch_with_virtual_associations
expected_result = {
id: @location.id,
lat: @location.lat,
lng: @location.lng,
place: 'Nowhere'
}
hash = render_object_with_cache(@location)
assert_equal(hash, expected_result)
assert_equal({ place: 'Nowhere' }, ActionController::Base.cache_store.fetch(@location.cache_key))
end
def test_uses_file_digest_in_cache_key
render_object_with_cache(@blog)
assert_equal(@blog_serializer.attributes, ActionController::Base.cache_store.fetch(@blog.cache_key_with_digest))
end
def test_cache_digest_definition
assert_equal(::Model::FILE_DIGEST, @post_serializer.class._cache_digest)
end
def test_serializer_file_path_on_nix
path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_serializer_file_path_on_windows
path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_serializer_file_path_with_space
path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_serializer_file_path_with_submatch
# The submatch in the path ensures we're using a correctly greedy regexp.
path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb'
caller_line = "#{path}:1:in `<top (required)>'"
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
end
def test_digest_caller_file
contents = "puts 'AMS rocks'!"
dir = Dir.mktmpdir('space char')
file = Tempfile.new('some_ruby.rb', dir)
file.write(contents)
path = file.path
caller_line = "#{path}:1:in `<top (required)>'"
file.close
assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents)
ensure
file.unlink
FileUtils.remove_entry dir
end
def test_warn_on_serializer_not_defined_in_file
called = false
serializer = Class.new(ActiveModel::Serializer)
assert_match(/_cache_digest/, (capture(:stderr) do
serializer.digest_caller_file('')
called = true
end))
assert called
end
private
def render_object_with_cache(obj)
ActiveModel::SerializableResource.new(obj).serializable_hash
end
def render_object_with_cache(obj)
ActiveModel::SerializableResource.new(obj).serializable_hash
end
end
end

View File

@ -0,0 +1,80 @@
require 'test_helper'
module ActiveModelSerializers
module Adapter
class CachedSerializerTest < ActiveSupport::TestCase
def test_cached_false_without_cache_store
cached_serializer = build do |serializer|
serializer._cache = nil
end
refute cached_serializer.cached?
end
def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except
cached_serializer = build do |serializer|
serializer._cache = Object
end
assert cached_serializer.cached?
end
def test_cached_false_with_cache_store_and_with_cache_only
cached_serializer = build do |serializer|
serializer._cache = Object
serializer._cache_only = [:name]
end
refute cached_serializer.cached?
end
def test_cached_false_with_cache_store_and_with_cache_except
cached_serializer = build do |serializer|
serializer._cache = Object
serializer._cache_except = [:content]
end
refute cached_serializer.cached?
end
def test_fragment_cached_false_without_cache_store
cached_serializer = build do |serializer|
serializer._cache = nil
serializer._cache_only = [:name]
end
refute cached_serializer.fragment_cached?
end
def test_fragment_cached_true_with_cache_store_and_cache_only
cached_serializer = build do |serializer|
serializer._cache = Object
serializer._cache_only = [:name]
end
assert cached_serializer.fragment_cached?
end
def test_fragment_cached_true_with_cache_store_and_cache_except
cached_serializer = build do |serializer|
serializer._cache = Object
serializer._cache_except = [:content]
end
assert cached_serializer.fragment_cached?
end
def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only
cached_serializer = build do |serializer|
serializer._cache = Object
serializer._cache_except = [:content]
serializer._cache_only = [:name]
end
refute cached_serializer.fragment_cached?
end
private
def build
serializer = Class.new(ActiveModel::Serializer)
serializer._cache_key = nil
serializer._cache_options = nil
yield serializer if block_given?
serializer_instance = serializer.new(Object)
CachedSerializer.new(serializer_instance)
end
end
end
end

View File

@ -1,6 +0,0 @@
# https://github.com/colszowka/simplecov/pull/400
# https://github.com/ruby/ruby/blob/trunk/lib/English.rb
unless defined?(English)
# The exception object passed to +raise+.
alias $ERROR_INFO $! # rubocop:disable Style/SpecialGlobalVars
end