From 6e11d6234eb338dfe3f5aecce55487ce75f7a37e Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Tue, 29 Nov 2016 13:29:22 -0600 Subject: [PATCH 1/8] Merge pull request #1990 from mxie/mx-result-typo Fix typos and capitalization in Relationship Links docs [ci skip] --- docs/howto/add_relationship_links.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/howto/add_relationship_links.md b/docs/howto/add_relationship_links.md index b942acc75..a27f7e2da 100644 --- a/docs/howto/add_relationship_links.md +++ b/docs/howto/add_relationship_links.md @@ -37,7 +37,7 @@ class Api::V1::UserSerializer < ActiveModel::Serializer end ``` -This will resilt in (example is in jsonapi adapter): +This will result in (example is in JSONAPI adapter): ```json { "data": { @@ -69,7 +69,7 @@ class Api::V1::UserSerializer < ActiveModel::Serializer end ``` -This will resilt in (example is in jsonapi adapter): +This will result in (example is in JSONAPI adapter): ```json { "data": { From e036b71e0c276ffb15a3b1bb802b78719cc8d3da Mon Sep 17 00:00:00 2001 From: "L. Preston Sego III" Date: Sat, 3 Dec 2016 15:45:08 -0500 Subject: [PATCH 2/8] Merge pull request #1992 from ojiry/bump_ruby_versions Run tests by Ruby 2.2.6 and 2.3.3 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d18c084dd..0cd358e43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ sudo: false rvm: - 2.1 - - 2.2.3 - - 2.3.0 + - 2.2.6 + - 2.3.3 - ruby-head - jruby-9.0.4.0 - jruby-head From 4054f43309cffef2197caff58a2cd4d381c2d5ce Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 7 Dec 2016 07:07:26 -0600 Subject: [PATCH 3/8] Merge pull request #1994 from bf4/promote_architecture Promote important architecture description that answers a lot of questions we get Conflicts: docs/ARCHITECTURE.md --- README.md | 136 +++++++++++++++++++++++++++++++++++++- docs/ARCHITECTURE.md | 125 ----------------------------------- docs/README.md | 1 - docs/general/rendering.md | 19 +----- 4 files changed, 137 insertions(+), 144 deletions(-) delete mode 100644 docs/ARCHITECTURE.md diff --git a/README.md b/README.md index cfcf84124..3b751a836 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,141 @@ serializer = SomeSerializer.new(resource, serializer_options) serializer.attributes serializer.associations ``` -See [ARCHITECTURE.md](docs/ARCHITECTURE.md) for more information. + +## Architecture + +This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, +please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or +[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md). + +The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile). + +### ActiveModel::Serializer + +An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) +and exposes an `attributes` method, among a few others. +It allows you to specify which attributes and associations should be represented in the serializatation of the resource. +It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself. +It may be useful to think of it as a +[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters). + +#### ActiveModel::CollectionSerializer + +The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers +and, if there is no serializer, primitives. + +### ActiveModelSerializers::Adapter::Base + +The **`ActiveModelSerializeres::Adapter::Base`** describes the structure of the JSON document generated from a +serializer. For example, the `Attributes` example represents each serializer as its +unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON +API](http://jsonapi.org/) document. + +### ActiveModelSerializers::SerializableResource + +The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter +to an object that responds to `to_json`, and `as_json`. It is used in the controller to +encapsulate the serialization resource when rendered. However, it can also be used on its own +to serialize a resource outside of a controller, as well. + +### Primitive handling + +Definitions: A primitive is usually a String or Array. There is no serializer +defined for them; they will be serialized when the resource is converted to JSON (`as_json` or +`to_json`). (The below also applies for any object with no serializer.) + +- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all. + +Internally, if no serializer can be found in the controller, the resource is not decorated by +ActiveModelSerializers. + +- However, when a primitive value is an attribute or in a collection, it is not modified. + +When serializing a collection and the collection serializer (CollectionSerializer) cannot +identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128). +For example, when caught by `Reflection#build_association`, and the association value is set directly: + +```ruby +reflection_options[:virtual_value] = association_value.try(:as_json) || association_value +``` + +(which is called by the adapter as `serializer.associations(*)`.) + +### How options are parsed + +High-level overview: + +- For a **collection** + - `:serializer` specifies the collection serializer and + - `: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_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5). + The remaining options are serializer options. + +Details: + +1. **ActionController::Serialization** + 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)` + 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`). + The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5). +1. **ActiveModelSerializers::SerializableResource** + 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.) + - Where `serializer?` is `use_adapter? && !!(serializer)` + - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil); + False when explicit adapter is falsy (nil or false)' + - Where `serializer`: + 1. from explicit `:serializer` option, else + 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)` + 1. A side-effect of checking `serializer` is: + - The `:serializer` option is removed from the serializer_opts hash + - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option + 1. The serializer and adapter are created as + 1. `serializer_instance = serializer.new(resource, serializer_opts)` + 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)` +1. **ActiveModel::Serializer::CollectionSerializer#new** + 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts + is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16). +1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for + resource as defined by the serializer. + +(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)` +methods on the resource serialization by the Rails JSON renderer. They are, therefore, important +to know about, but not part of ActiveModelSerializers.) + +### What does a 'serializable resource' look like? + +- An `ActiveRecord::Base` object. +- Any Ruby object that passes the + [Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests) + [code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb). + +ActiveModelSerializers provides a +[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb), +which is a simple serializable PORO (Plain-Old Ruby Object). + +`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code. + +```ruby +class MyModel < ActiveModelSerializers::Model + attr_accessor :id, :name, :level +end +``` + +The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an +ActiveRecord::Base object or not. + +Outside of the controller the rules are **exactly** the same as for records. For example: + +```ruby +render json: MyModel.new(level: 'awesome'), adapter: :json +``` + +would be serialized the same as + +```ruby +ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json +``` ## Semantic Versioning diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md deleted file mode 100644 index 3d566e5cb..000000000 --- a/docs/ARCHITECTURE.md +++ /dev/null @@ -1,125 +0,0 @@ -[Back to Guides](README.md) - -This document focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, -please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or -[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md). - -The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile). - -# ARCHITECTURE - -An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) -and exposes an `attributes` method, among a few others. -It allows you to specify which attributes and associations should be represented in the serializatation of the resource. -It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself. -It may be useful to think of it as a -[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters). - -The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers -and, if there is no serializer, primitives. - -The **`ActiveModel::Adapter`** describes the structure of the JSON document generated from a -serializer. For example, the `Attributes` example represents each serializer as its -unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON -API](http://jsonapi.org/) document. - -The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter -to an object that responds to `to_json`, and `as_json`. It is used in the controller to -encapsulate the serialization resource when rendered. However, it can also be used on its own -to serialize a resource outside of a controller, as well. - -## Primitive handling - -Definitions: A primitive is usually a String or Array. There is no serializer -defined for them; they will be serialized when the resource is converted to JSON (`as_json` or -`to_json`). (The below also applies for any object with no serializer.) - -ActiveModelSerializers doesn't handle primitives passed to `render json:` at all. - -However, when a primitive value is an attribute or in a collection, -it is not modified. - -Internally, if no serializer can be found in the controller, the resource is not decorated by -ActiveModelSerializers. - -If the collection serializer (CollectionSerializer) cannot -identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128). -For example, when caught by `Reflection#build_association`, the association value is set directly: - -```ruby -reflection_options[:virtual_value] = association_value.try(:as_json) || association_value -``` - -(which is called by the adapter as `serializer.associations(*)`.) - -## How options are parsed - -High-level overview: - -- For a collection - - `:serializer` specifies the collection serializer and - - `: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_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5). - The remaining options are serializer options. - -Details: - -1. **ActionController::Serialization** - 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)` - 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`). - The `adapter_opts` keys are defined in `ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`. -1. **ActiveModelSerializers::SerializableResource** - 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.) - - Where `serializer?` is `use_adapter? && !!(serializer)` - - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil); - False when explicit adapter is falsy (nil or false)' - - Where `serializer`: - 1. from explicit `:serializer` option, else - 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)` - 1. A side-effect of checking `serializer` is: - - The `:serializer` option is removed from the serializer_opts hash - - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option - 1. The serializer and adapter are created as - 1. `serializer_instance = serializer.new(resource, serializer_opts)` - 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)` -1. **ActiveModel::Serializer::CollectionSerializer#new** - 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts - is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16). -1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for - resource as defined by the serializer. - -## What does a 'serializable resource' look like? - -- An `ActiveRecord::Base` object. -- Any Ruby object that passes the - [Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests) - [code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb). - -ActiveModelSerializers provides a -[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb), -which is a simple serializable PORO (Plain-Old Ruby Object). - -ActiveModelSerializers::Model may be used either as a template, or in production code. - -```ruby -class MyModel < ActiveModelSerializers::Model - attr_accessor :id, :name, :level -end -``` - -The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an -ActiveRecord::Base object or not. - -Outside of the controller the rules are **exactly** the same as for records. For example: - -```ruby -render json: MyModel.new(level: 'awesome'), adapter: :json -``` - -would be serialized the same as - -```ruby -ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json -``` diff --git a/docs/README.md b/docs/README.md index b7d8c1523..94460ec12 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,6 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10. - JSON API - [Schema](jsonapi/schema.md) - [Errors](jsonapi/errors.md) -- [ARCHITECTURE](ARCHITECTURE.md) ## How to diff --git a/docs/general/rendering.md b/docs/general/rendering.md index b75c31938..21120a5a0 100644 --- a/docs/general/rendering.md +++ b/docs/general/rendering.md @@ -48,26 +48,11 @@ render json: @posts, serializer: CollectionSerializer, each_serializer: PostPrev ## Serializing non-ActiveRecord objects -All serializable resources must pass the -[ActiveModel::Serializer::Lint::Tests](../../lib/active_model/serializer/lint.rb#L17). - -See the ActiveModelSerializers::Model for a base class that implements the full -API for a plain-old Ruby object (PORO). +See [README](../../README.md#what-does-a-serializable-resource-look-like) ## SerializableResource options -The `options` hash passed to `render` or `ActiveModelSerializers::SerializableResource.new(resource, options)` -are partitioned into `serializer_opts` and `adapter_opts`. `adapter_opts` are passed to new Adapters; -`serializer_opts` are passed to new Serializers. - -The `adapter_opts` are specified in [ActiveModelSerializers::SerializableResource::ADAPTER_OPTIONS](../../lib/active_model_serializers/serializable_resource.rb#L5). -The `serializer_opts` are the remaining options. - -(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)` -methods on the resource serialization by the Rails JSON renderer. They are, therefore, important -to know about, but not part of ActiveModelSerializers.) - -See [ARCHITECTURE](../ARCHITECTURE.md) for more information. +See [README](../../README.md#activemodelserializersserializableresource) ### adapter_opts From 49f2dca730ceb09b63b4f94a90488ab45debec7d Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Tue, 13 Dec 2016 09:23:29 -0600 Subject: [PATCH 4/8] Merge pull request #1999 from bf4/typos Fix typos [ci skip] --- lib/active_model/serializer/concerns/attributes.rb | 2 +- test/action_controller/json_api/errors_test.rb | 4 ++-- test/action_controller/serialization_test.rb | 2 +- test/active_model_serializers/json_pointer_test.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_model/serializer/concerns/attributes.rb b/lib/active_model/serializer/concerns/attributes.rb index d1968d77e..6ee2732fd 100644 --- a/lib/active_model/serializer/concerns/attributes.rb +++ b/lib/active_model/serializer/concerns/attributes.rb @@ -66,7 +66,7 @@ def _attributes end # @api private - # maps attribute value to explict key name + # maps attribute value to explicit key name # @see Serializer::attribute # @see FragmentCache#fragment_serializer def _attributes_keys diff --git a/test/action_controller/json_api/errors_test.rb b/test/action_controller/json_api/errors_test.rb index dd1249f20..6da3c9ada 100644 --- a/test/action_controller/json_api/errors_test.rb +++ b/test/action_controller/json_api/errors_test.rb @@ -14,10 +14,10 @@ def test_active_model_with_multiple_errors { source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' } ] }.to_json - assert_equal json_reponse_body.to_json, expected_errors_object + assert_equal json_response_body.to_json, expected_errors_object end - def json_reponse_body + def json_response_body JSON.load(@response.body) end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index b5900e1d2..2d782b1fd 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -456,7 +456,7 @@ def use_adapter? end end - def test_render_event_is_emmited + def test_render_event_is_emitted subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |name| @name = name end diff --git a/test/active_model_serializers/json_pointer_test.rb b/test/active_model_serializers/json_pointer_test.rb index 0c8cf58fc..60619ee6e 100644 --- a/test/active_model_serializers/json_pointer_test.rb +++ b/test/active_model_serializers/json_pointer_test.rb @@ -13,7 +13,7 @@ def test_primary_data_pointer assert_equal '/data', pointer end - def test_unkown_data_pointer + def test_unknown_data_pointer assert_raises(TypeError) do ActiveModelSerializers::JsonPointer.new(:unknown) end From dfa6caac27baba762858288f740df09b41bb9c35 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 14 Dec 2016 20:30:59 -0600 Subject: [PATCH 5/8] Merge pull request #2000 from berfarah/patch-1 Link to 0.10.3 tag instead of `master` branch --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b751a836..b70f351a0 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,14 @@ If you'd like to chat, we have a [community slack](http://amserializers.herokuap Thanks! ## Documentation + +If you're reading this at https://github.com/rails-api/active_model_serializers you are +reading documentation for our `master`, which may include features that have not +been released yet. Please see below for the documentation relevant to you. + - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master) - - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/active_model_serializers/0.10.2) +- [0.10.3 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.3) + - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/active_model_serializers/0.10.3) - [Guides](docs) - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable) From 2c0b15d22f300cd59e4957cfdf2a07ec470e2dda Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 23 Dec 2016 11:24:23 -0600 Subject: [PATCH 6/8] Merge pull request #2007 from bf4/check_ci Test was failing due to change in JSON exception message when parsing empty string --- test/active_model_serializers/test/schema_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/active_model_serializers/test/schema_test.rb b/test/active_model_serializers/test/schema_test.rb index 105ac575d..0fe497d78 100644 --- a/test/active_model_serializers/test/schema_test.rb +++ b/test/active_model_serializers/test/schema_test.rb @@ -115,7 +115,8 @@ def test_with_a_non_existent_file end def test_that_raises_with_a_invalid_json_body - message = 'A JSON text must at least contain two octets!' + # message changes from JSON gem 2.0.2 to 2.2.0 + message = /A JSON text must at least contain two octets!|unexpected token at ''/ get :invalid_json_body @@ -123,7 +124,7 @@ def test_that_raises_with_a_invalid_json_body assert_response_schema('custom/show.json') end - assert_equal(message, error.message) + assert_match(message, error.message) end end end From adf110f4df6def594d82dbdf7739f66a39c04415 Mon Sep 17 00:00:00 2001 From: "L. Preston Sego III" Date: Wed, 7 Dec 2016 10:49:38 -0500 Subject: [PATCH 7/8] Swap out KeyTransform for CaseTransform (#1993) * delete KeyTransform, use CaseTransform * added changelog Conflicts: CHANGELOG.md --- CHANGELOG.md | 2 + active_model_serializers.gemspec | 1 + lib/active_model_serializers/adapter/base.rb | 4 +- .../adapter/json_api/deserialization.rb | 2 +- lib/active_model_serializers/key_transform.rb | 74 ----- .../key_transform_test.rb | 297 ------------------ test/benchmark/bm_transform.rb | 10 +- 7 files changed, 11 insertions(+), 379 deletions(-) delete mode 100644 lib/active_model_serializers/key_transform.rb delete mode 100644 test/active_model_serializers/key_transform_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fe24b77ec..21b3ac1da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Fixes: Misc: +- [#1993](https://github.com/rails-api/active_model_serializers/pull/1993) Swap out KeyTransform for CaseTransform gem for the possibility of native extension use (@NullVoxPopuli) + ### [v0.10.3 (2016-11-21)](https://github.com/rails-api/active_model_serializers/compare/v0.10.2...v0.10.3) Fixes: diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec index 89327bc63..3581ce408 100644 --- a/active_model_serializers.gemspec +++ b/active_model_serializers.gemspec @@ -43,6 +43,7 @@ Gem::Specification.new do |spec| # 'thread_safe' spec.add_runtime_dependency 'jsonapi', '0.1.1.beta2' + spec.add_runtime_dependency 'case_transform', '>= 0.2' spec.add_development_dependency 'activerecord', rails_versions # arel diff --git a/lib/active_model_serializers/adapter/base.rb b/lib/active_model_serializers/adapter/base.rb index 7b60db70b..851583285 100644 --- a/lib/active_model_serializers/adapter/base.rb +++ b/lib/active_model_serializers/adapter/base.rb @@ -1,4 +1,4 @@ -require 'active_model_serializers/key_transform' +require 'case_transform' module ActiveModelSerializers module Adapter @@ -31,7 +31,7 @@ def self.transform(options) # @param options [Object] serializable resource options # @return [Symbol] the default transform for the adapter def self.transform_key_casing!(value, options) - KeyTransform.send(transform(options), value) + CaseTransform.send(transform(options), value) end def self.cache_key diff --git a/lib/active_model_serializers/adapter/json_api/deserialization.rb b/lib/active_model_serializers/adapter/json_api/deserialization.rb index 2e0e531dd..b79125ac4 100644 --- a/lib/active_model_serializers/adapter/json_api/deserialization.rb +++ b/lib/active_model_serializers/adapter/json_api/deserialization.rb @@ -205,7 +205,7 @@ def parse_relationships(relationships, options) # @api private def transform_keys(hash, options) transform = options[:key_transform] || :underscore - KeyTransform.send(transform, hash) + CaseTransform.send(transform, hash) end end end diff --git a/lib/active_model_serializers/key_transform.rb b/lib/active_model_serializers/key_transform.rb deleted file mode 100644 index d0e648e59..000000000 --- a/lib/active_model_serializers/key_transform.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'active_support/core_ext/hash/keys' - -module ActiveModelSerializers - module KeyTransform - module_function - - # Transforms values to UpperCamelCase or PascalCase. - # - # @example: - # "some_key" => "SomeKey", - # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} - def camel(value) - case value - when Array then value.map { |item| camel(item) } - when Hash then value.deep_transform_keys! { |key| camel(key) } - when Symbol then camel(value.to_s).to_sym - when String then value.underscore.camelize - else value - end - end - - # Transforms values to camelCase. - # - # @example: - # "some_key" => "someKey", - # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} - def camel_lower(value) - case value - when Array then value.map { |item| camel_lower(item) } - when Hash then value.deep_transform_keys! { |key| camel_lower(key) } - when Symbol then camel_lower(value.to_s).to_sym - when String then value.underscore.camelize(:lower) - else value - end - end - - # Transforms values to dashed-case. - # This is the default case for the JsonApi adapter. - # - # @example: - # "some_key" => "some-key", - # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize} - def dash(value) - case value - when Array then value.map { |item| dash(item) } - when Hash then value.deep_transform_keys! { |key| dash(key) } - when Symbol then dash(value.to_s).to_sym - when String then value.underscore.dasherize - else value - end - end - - # Transforms values to underscore_case. - # This is the default case for deserialization in the JsonApi adapter. - # - # @example: - # "some-key" => "some_key", - # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore} - def underscore(value) - case value - when Array then value.map { |item| underscore(item) } - when Hash then value.deep_transform_keys! { |key| underscore(key) } - when Symbol then underscore(value.to_s).to_sym - when String then value.underscore - else value - end - end - - # Returns the value unaltered - def unaltered(value) - value - end - end -end diff --git a/test/active_model_serializers/key_transform_test.rb b/test/active_model_serializers/key_transform_test.rb deleted file mode 100644 index b4ff4d311..000000000 --- a/test/active_model_serializers/key_transform_test.rb +++ /dev/null @@ -1,297 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class KeyTransformTest < ActiveSupport::TestCase - def test_camel - obj = Object.new - scenarios = [ - { - value: { :"some-key" => 'value' }, - expected: { SomeKey: 'value' } - }, - { - value: { someKey: 'value' }, - expected: { SomeKey: 'value' } - }, - { - value: { some_key: 'value' }, - expected: { SomeKey: 'value' } - }, - { - value: { 'some-key' => 'value' }, - expected: { 'SomeKey' => 'value' } - }, - { - value: { 'someKey' => 'value' }, - expected: { 'SomeKey' => 'value' } - }, - { - value: { 'some_key' => 'value' }, - expected: { 'SomeKey' => 'value' } - }, - { - value: :"some-value", - expected: :SomeValue - }, - { - value: :some_value, - expected: :SomeValue - }, - { - value: :someValue, - expected: :SomeValue - }, - { - value: 'some-value', - expected: 'SomeValue' - }, - { - value: 'someValue', - expected: 'SomeValue' - }, - { - value: 'some_value', - expected: 'SomeValue' - }, - { - value: obj, - expected: obj - }, - { - value: nil, - expected: nil - }, - { - value: [ - { some_value: 'value' } - ], - expected: [ - { SomeValue: 'value' } - ] - } - ] - scenarios.each do |s| - result = ActiveModelSerializers::KeyTransform.camel(s[:value]) - assert_equal s[:expected], result - end - end - - def test_camel_lower - obj = Object.new - scenarios = [ - { - value: { :"some-key" => 'value' }, - expected: { someKey: 'value' } - }, - { - value: { SomeKey: 'value' }, - expected: { someKey: 'value' } - }, - { - value: { some_key: 'value' }, - expected: { someKey: 'value' } - }, - { - value: { 'some-key' => 'value' }, - expected: { 'someKey' => 'value' } - }, - { - value: { 'SomeKey' => 'value' }, - expected: { 'someKey' => 'value' } - }, - { - value: { 'some_key' => 'value' }, - expected: { 'someKey' => 'value' } - }, - { - value: :"some-value", - expected: :someValue - }, - { - value: :SomeValue, - expected: :someValue - }, - { - value: :some_value, - expected: :someValue - }, - { - value: 'some-value', - expected: 'someValue' - }, - { - value: 'SomeValue', - expected: 'someValue' - }, - { - value: 'some_value', - expected: 'someValue' - }, - { - value: obj, - expected: obj - }, - { - value: nil, - expected: nil - }, - { - value: [ - { some_value: 'value' } - ], - expected: [ - { someValue: 'value' } - ] - } - ] - scenarios.each do |s| - result = ActiveModelSerializers::KeyTransform.camel_lower(s[:value]) - assert_equal s[:expected], result - end - end - - def test_dash - obj = Object.new - scenarios = [ - { - value: { some_key: 'value' }, - expected: { :"some-key" => 'value' } - }, - { - value: { 'some_key' => 'value' }, - expected: { 'some-key' => 'value' } - }, - { - value: { SomeKey: 'value' }, - expected: { :"some-key" => 'value' } - }, - { - value: { 'SomeKey' => 'value' }, - expected: { 'some-key' => 'value' } - }, - { - value: { someKey: 'value' }, - expected: { :"some-key" => 'value' } - }, - { - value: { 'someKey' => 'value' }, - expected: { 'some-key' => 'value' } - }, - { - value: :some_value, - expected: :"some-value" - }, - { - value: :SomeValue, - expected: :"some-value" - }, - { - value: 'SomeValue', - expected: 'some-value' - }, - { - value: :someValue, - expected: :"some-value" - }, - { - value: 'someValue', - expected: 'some-value' - }, - { - value: obj, - expected: obj - }, - { - value: nil, - expected: nil - }, - { - value: [ - { 'some_value' => 'value' } - ], - expected: [ - { 'some-value' => 'value' } - ] - } - ] - scenarios.each do |s| - result = ActiveModelSerializers::KeyTransform.dash(s[:value]) - assert_equal s[:expected], result - end - end - - def test_underscore - obj = Object.new - scenarios = [ - { - value: { :"some-key" => 'value' }, - expected: { some_key: 'value' } - }, - { - value: { 'some-key' => 'value' }, - expected: { 'some_key' => 'value' } - }, - { - value: { SomeKey: 'value' }, - expected: { some_key: 'value' } - }, - { - value: { 'SomeKey' => 'value' }, - expected: { 'some_key' => 'value' } - }, - { - value: { someKey: 'value' }, - expected: { some_key: 'value' } - }, - { - value: { 'someKey' => 'value' }, - expected: { 'some_key' => 'value' } - }, - { - value: :"some-value", - expected: :some_value - }, - { - value: :SomeValue, - expected: :some_value - }, - { - value: :someValue, - expected: :some_value - }, - { - value: 'some-value', - expected: 'some_value' - }, - { - value: 'SomeValue', - expected: 'some_value' - }, - { - value: 'someValue', - expected: 'some_value' - }, - { - value: obj, - expected: obj - }, - { - value: nil, - expected: nil - }, - { - value: [ - { 'some-value' => 'value' } - ], - expected: [ - { 'some_value' => 'value' } - ] - } - ] - scenarios.each do |s| - result = ActiveModelSerializers::KeyTransform.underscore(s[:value]) - assert_equal s[:expected], result - end - end - end -end diff --git a/test/benchmark/bm_transform.rb b/test/benchmark/bm_transform.rb index 8af5298e9..97c655c01 100644 --- a/test/benchmark/bm_transform.rb +++ b/test/benchmark/bm_transform.rb @@ -25,21 +25,21 @@ serialization = adapter.as_json Benchmark.ams('camel', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::KeyTransform.camel(serialization) + CaseTransform.camel(serialization) end Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::KeyTransform.camel_lower(serialization) + CaseTransform.camel_lower(serialization) end Benchmark.ams('dash', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::KeyTransform.dash(serialization) + CaseTransform.dash(serialization) end Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::KeyTransform.unaltered(serialization) + CaseTransform.unaltered(serialization) end Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::KeyTransform.underscore(serialization) + CaseTransform.underscore(serialization) end From 97b587b14c0bba7198852813aea8d547122555ba Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 23 Dec 2016 11:24:38 -0600 Subject: [PATCH 8/8] Merge pull request #2005 from kofronpi/support-ruby-2.4 Update jsonapi runtime dependency to 0.1.1.beta6 --- active_model_serializers.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec index 3581ce408..a1fc0107a 100644 --- a/active_model_serializers.gemspec +++ b/active_model_serializers.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |spec| # 'minitest' # 'thread_safe' - spec.add_runtime_dependency 'jsonapi', '0.1.1.beta2' + spec.add_runtime_dependency 'jsonapi', '0.1.1.beta6' spec.add_runtime_dependency 'case_transform', '>= 0.2' spec.add_development_dependency 'activerecord', rails_versions