Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict serializable_hash to accepted options #1647

Merged
merged 7 commits into from
Apr 11, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Breaking changes:
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear)

Features:
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4)
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear)
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
take precedence over `serialization_scope` in the controller.
Expand Down
2 changes: 1 addition & 1 deletion lib/active_model_serializers/adapter/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Json < Base
def serializable_hash(options = nil)
options ||= {}
serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
self.class.transform_key_casing!(serialized_hash, options)
self.class.transform_key_casing!(serialized_hash, instance_options)
end
end
end
Expand Down
15 changes: 7 additions & 8 deletions lib/active_model_serializers/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,13 @@ def self.default_key_transform

# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
def serializable_hash(options = nil)
options ||= {}
def serializable_hash(*)
document = if serializer.success?
success_document(options)
success_document(instance_options)
else
failure_document(options)
failure_document(instance_options)
end
self.class.transform_key_casing!(document, options)
self.class.transform_key_casing!(document, instance_options)
end

# {http://jsonapi.org/format/#document-top-level Primary data}
Expand Down Expand Up @@ -128,7 +127,7 @@ def success_document(options)

if is_collection && serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
hash[:links].update(pagination_links_for(serializer))
end

hash
Expand Down Expand Up @@ -499,8 +498,8 @@ def links_for(serializer)
# end
# prs:
# https://github.com/rails-api/active_model_serializers/pull/1041
def pagination_links_for(serializer, options)
PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
def pagination_links_for(serializer)
PaginationLinks.new(serializer.object, instance_options).as_json
end

# {http://jsonapi.org/format/#document-meta Docment Meta}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@ class PaginationLinks

attr_reader :collection, :context

def initialize(collection, context)
def initialize(collection, adapter_options)
@collection = collection
@context = context
@adapter_options = adapter_options
@context = adapter_options.fetch(:serialization_context)
end

def serializable_hash(options = {})
def as_json
per_page = collection.try(:per_page) || collection.try(:limit_value) || collection.size
pages_from.each_with_object({}) do |(key, value), hash|
params = query_parameters.merge(page: { number: value, size: per_page }).to_query

hash[key] = "#{url(options)}?#{params}"
hash[key] = "#{url(adapter_options)}?#{params}"
end
end

protected

attr_reader :adapter_options

private

def pages_from
Expand Down
3 changes: 1 addition & 2 deletions lib/active_model_serializers/adapter/null.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
module ActiveModelSerializers
module Adapter
class Null < Base
# Since options param is not being used, underscored naming of the param
def serializable_hash(_options = nil)
def serializable_hash(*)
{}
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/active_model_serializers/serializable_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module ActiveModelSerializers
class SerializableResource
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context])
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@remear this is where I added serialization context to the adapter options.

Now pagination links call from jsonapi adapter is PaginationLinks.new(serializer.object, instance_options).as_json and PaginationLinks calls @context = adapter_options.fetch(:serialization_context)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add :key_transform here and your failing test will pass 😉

include ActiveModelSerializers::Logging

delegate :serializable_hash, :as_json, :to_json, to: :adapter
Expand Down
28 changes: 14 additions & 14 deletions test/adapter/json/transform_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,34 @@ def mock_request(key_transform = nil)
context = Minitest::Mock.new
context.expect(:request_url, URI)
context.expect(:query_parameters, {})
@options = {}
@options[:key_transform] = key_transform if key_transform
@options[:serialization_context] = context
options = {}
options[:key_transform] = key_transform if key_transform
options[:serialization_context] = context
serializer = CustomBlogSerializer.new(@blog)
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options)
end

Post = Class.new(::Model)
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :publish_at
end

def setup
setup do
ActionController::Base.cache_store.clear
@blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat')
serializer = CustomBlogSerializer.new(@blog)
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
end

def test_transform_default
mock_request
assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
}, @adapter.serializable_hash)
end

def test_transform_global_config
mock_request
result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options)
@adapter.serializable_hash
end
assert_equal({
blog: { id: 1, specialAttribute: 'neat', articles: nil }
Expand All @@ -45,7 +45,7 @@ def test_transform_global_config
def test_transform_serialization_ctx_overrides_global_config
mock_request(:camel)
result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options)
@adapter.serializable_hash
end
assert_equal({
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
Expand All @@ -56,36 +56,36 @@ def test_transform_undefined
mock_request(:blam)
result = nil
assert_raises NoMethodError do
result = @adapter.serializable_hash(@options)
result = @adapter.serializable_hash
end
end

def test_transform_dash
mock_request(:dash)
assert_equal({
blog: { id: 1, :"special-attribute" => 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
}, @adapter.serializable_hash)
end

def test_transform_unaltered
mock_request(:unaltered)
assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
}, @adapter.serializable_hash)
end

def test_transform_camel
mock_request(:camel)
assert_equal({
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
}, @adapter.serializable_hash(@options))
}, @adapter.serializable_hash)
end

def test_transform_camel_lower
mock_request(:camel_lower)
assert_equal({
blog: { id: 1, specialAttribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
}, @adapter.serializable_hash)
end
end
end
Expand Down
37 changes: 16 additions & 21 deletions test/adapter/json_api/pagination_links_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ def mock_request(query_parameters = {}, original_url = URI)
context = Minitest::Mock.new
context.expect(:request_url, original_url)
context.expect(:query_parameters, query_parameters)
@options = {}
@options[:serialization_context] = context
context.expect(:key_transform, nil)
end

def load_adapter(paginated_collection, options = {})
options = options.merge(adapter: :json_api)
ActiveModelSerializers::SerializableResource.new(paginated_collection, options)
def load_adapter(paginated_collection, mock_request = nil)
render_options = { adapter: :json_api }
render_options[:serialization_context] = mock_request if mock_request
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this! 👍

serializable(paginated_collection, render_options)
end

def using_kaminari(page = 2)
Expand Down Expand Up @@ -102,43 +102,38 @@ def expected_response_with_last_page_pagination_links
end

def test_pagination_links_using_kaminari
adapter = load_adapter(using_kaminari)
adapter = load_adapter(using_kaminari, mock_request)

mock_request
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
assert_equal expected_response_with_pagination_links, adapter.serializable_hash
end

def test_pagination_links_using_will_paginate
adapter = load_adapter(using_will_paginate)
adapter = load_adapter(using_will_paginate, mock_request)

mock_request
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
assert_equal expected_response_with_pagination_links, adapter.serializable_hash
end

def test_pagination_links_with_additional_params
adapter = load_adapter(using_will_paginate)
adapter = load_adapter(using_will_paginate, mock_request({ test: 'test' }))

mock_request({ test: 'test' })
assert_equal expected_response_with_pagination_links_and_additional_params,
adapter.serializable_hash(@options)
adapter.serializable_hash
end

def test_last_page_pagination_links_using_kaminari
adapter = load_adapter(using_kaminari(3))
adapter = load_adapter(using_kaminari(3), mock_request)

mock_request
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash(@options)
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash
end

def test_last_page_pagination_links_using_will_paginate
adapter = load_adapter(using_will_paginate(3))
adapter = load_adapter(using_will_paginate(3), mock_request)

mock_request
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash(@options)
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash
end

def test_not_showing_pagination_links
adapter = load_adapter(@array)
adapter = load_adapter(@array, mock_request)

assert_equal expected_response_without_pagination_links, adapter.serializable_hash
end
Expand Down
Loading