From 1b2324bd021b7e9adc5be11c123dd90286f3541b Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Thu, 10 Dec 2015 15:12:06 -0600 Subject: [PATCH] [DOCS] Refactor, update, create documentation [ci skip] --- CHANGELOG.md | 10 +- CONTRIBUTING.md | 35 +- README.md | 403 ++++-------------- docs/ARCHITECTURE.md | 2 + docs/README.md | 26 +- docs/general/adapters.md | 98 ++++- docs/general/caching.md | 52 +++ docs/general/configuration_options.md | 9 +- docs/general/getting_started.md | 38 +- docs/general/instrumentation.md | 3 + docs/general/logging.md | 6 +- docs/general/rendering.md | 136 ++++++ docs/general/serializers.md | 213 +++++++++ docs/howto/add_pagination_links.md | 10 +- docs/howto/add_root_key.md | 6 +- docs/howto/outside_controller_use.md | 34 +- docs/integrations/ember-and-json-api.md | 6 +- docs/jsonapi/schema.md | 2 + .../serializer/adapter/json_api.rb | 14 +- .../json_api/resource_type_config_test.rb | 6 +- test/serializers/adapter_for_test.rb | 16 +- test/serializers/associations_test.rb | 4 +- test/serializers/configuration_test.rb | 10 +- test/serializers/serializer_for_test.rb | 8 +- test/support/serialization_testing.rb | 12 +- 25 files changed, 697 insertions(+), 462 deletions(-) create mode 100644 docs/general/caching.md create mode 100644 docs/general/rendering.md create mode 100644 docs/general/serializers.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8f5547d..f5bd70db0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,12 @@ Breaking changes: - [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Remove Serializer#root_name (@beauby) - [#1138](https://github.com/rails-api/active_model_serializers/pull/1138) Introduce Adapter::Base (@bf4) * Adapters now inherit Adapter::Base. 'Adapter' is now a module, no longer a class. - * using a class as a namespace that you also inherit from is complicated and circular at time i.e. + * using a class as a namespace that you also inherit from is complicated and circular at times i.e. buggy (see https://github.com/rails-api/active_model_serializers/pull/1177) * The class methods on Adapter aren't necessarily related to the instance methods, they're more - Adapter functions - * named `Base` because it's a Rails-ism - * It helps to isolate and highlight what the Adapter interface actually is + Adapter functions. + * named `Base` because it's a Rails-ism. + * It helps to isolate and highlight what the Adapter interface actually is. Features: @@ -42,6 +42,8 @@ Misc: - [#1166](https://github.com/rails-api/active_model_serializers/pull/1166) Prefer methods over instance variables (@bf4) - [#1168](https://github.com/rails-api/active_model_serializers/pull/1168) Fix appveyor failure cache not being expired (@bf4) - [#1161](https://github.com/rails-api/active_model_serializers/pull/1161) Remove duplicate test helper (@bf4) +- [#1371](https://github.com/rails-api/active_model_serializers/pull/1371) Refactor, update, create documentation (@bf4) + ### v0.10.0.rc3 (2015/09/16 15:19 +00:00) - [#1129](https://github.com/rails-api/active_model_serializers/pull/1129) Remove SerializableResource.serialize in favor of `.new` (@bf4) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e87f8176c..ca5bc7228 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,9 @@ http://www.commitstrip.com/en/2014/05/07/the-truth-behind-open-source-apps/](doc ## How can I help? +- [Filing an issue](CONTRIBUTING.md#filing-an-issue) +- [Writing code and comments](CONTRIBUTING#writing-code-and-comments) + ### Filing an issue Everyone is encouraged to open issues that are affecting them: @@ -20,7 +23,6 @@ bugs, ideas, documentation (`/docs`), performance problems – everything helps! - Check if your issue has already been reported. - If you find an existing issue report, feel free to add further information to that report. - #### Writing If possible, please include the following information when [reporting an @@ -82,22 +84,27 @@ And please don't forget to stay involved in the issue until it is closed! Thanks - We are actively working to identify tasks under the label [**Good for New Contributors**](https://github.com/rails-api/active_model_serializers/labels/Good%20for%20New%20Contributors). - Some bugs are expressly not good for new contributors, so don't expect 100% overlap between the two. - [Changelog Missing](https://github.com/rails-api/active_model_serializers/issues?q=label%3A%22Changelog+Missing%22+is%3Aclosed) is an easy way to help out. -- If you want to work on new feature development, look for the label [**Feature**](https://github.com/rails-api/active_model_serializers/labels/Feature). +- [Fix a bug](https://github.com/rails-api/active_model_serializers/labels/Ready%20for%20PR). + - Ready for PR - A well defined bug, needs someone to PR a fix. + - Bug - Anything that is broken. + - Regression - A bug that did not exist in previous versions and isn't a new feature (applied in tandem with Bug). + - Performance - A performance related issue. We could track this as a bug, but usually these would have slightly lower priority than standard bugs. + +- [Develop new features](https://github.com/rails-api/active_model_serializers/labels/Feature). + +- [Improve code quality](https://codeclimate.com/github/rails-api/active_model_serializers/code?sort=smell_count&sort_direction=desc). + +- [Improve amount of code exercised by tests](https://codeclimate.com/github/rails-api/active_model_serializers/coverage?sort=covered_percent&sort_direction=asc). - We are also encouraging comments to substantial changes (larger than bugfixes and simple features) under an "RFC" (Request for Comments) process before we start active development. Look for the [**RFC**](https://github.com/rails-api/active_model_serializers/labels/RFC) label. -## Issue Labeling - -ActiveModelSerializers uses a subset of [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels) for Github Issues. You can [see our labels here](https://github.com/rails-api/active_model_serializers/labels). - -## Submitting a pull request (PR) +#### Submitting a pull request (PR) 1. The vast majority of development is happening under the `master` branch. This is where we would suggest you start. @@ -147,13 +154,13 @@ Remember to [squash your commits](CONTRIBUTING.md#about-pull-requests-prs) and r - by force pushing to it - A maintainer can make any changes themselves and manually merge the code in. -### Commit Messages +#### Commit Messages - [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - [http://stopwritingramblingcommitmessages.com/](http://stopwritingramblingcommitmessages.com/) - [ThoughtBot style guide](https://github.com/thoughtbot/guides/tree/master/style#git) -### About Pull Requests (PR's) +#### About Pull Requests (PR's) - [Using Pull Requests](https://help.github.com/articles/using-pull-requests) - [Github pull requests made easy](http://www.element84.com/github-pull-requests-made-easy.html) @@ -161,9 +168,13 @@ Remember to [squash your commits](CONTRIBUTING.md#about-pull-requests-prs) and r - [Level up your Git](http://rakeroutes.com/blog/deliberate-git/) - [All Your Open Source Code Are Belong To Us](http://www.benjaminfleischer.com/2013/07/30/all-your-open-source-code-are-belong-to-us/) -### Running tests +## Issue Labeling + +ActiveModelSerializers uses a subset of [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels) for Github Issues. You can [see our labels here](https://github.com/rails-api/active_model_serializers/labels). + +## Running tests -#### Running with Rake +### Running with Rake The easiest way to run the unit tests is through Rake. The default task runs the entire test suite for all classes. For more information, checkout the diff --git a/README.md b/README.md index 684b39c1c..0fb63fbc0 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,55 @@ # ActiveModelSerializers [![Build Status](https://travis-ci.org/rails-api/active_model_serializers.svg?branch=master)](https://travis-ci.org/rails-api/active_model_serializers) - - +(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) -_Windows Build Status -_ [![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) +## Documentation -ActiveModelSerializers brings convention over configuration to your JSON generation. - -AMS does this through two components: **serializers** and **adapters**. -Serializers describe _which_ attributes and relationships should be serialized. -Adapters describe _how_ attributes and relationships should be serialized. - -By default AMS will use the **Attributes Adapter**. But we strongly advise you to use **JsonApi Adapter** that follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format). -Check how to change the adapter in the sections bellow. - -# Documentation - -Master - -- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers) -- [Guides](https://github.com/rails-api/active_model_serializers/tree/master/docs) - -# RELEASE CANDIDATE, PLEASE READ - -This is the master branch of AMS. It will become the `0.10.0` release when it's -ready. Currently this is a release candidate. This is **not** backward -compatible with `0.9.0` or `0.8.0`. - -`0.10.x` will be based on the `0.8.0` code, but with a more flexible -architecture. We'd love your help. [Learn how you can help here.](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md) - -## Example - -Given two models, a `Post(title: string, body: text)` and a -`Comment(name: string, body: text, post_id: integer)`, you will have two -serializers: - -```ruby -class PostSerializer < ActiveModel::Serializer - cache key: 'posts', expires_in: 3.hours - attributes :title, :body - - has_many :comments -end -``` - -and - -```ruby -class CommentSerializer < ActiveModel::Serializer - attributes :name, :body - - belongs_to :post -end -``` - -Generally speaking, you as a user of AMS will write (or generate) these -serializer classes. If you want to use a different adapter, such as a JsonApi, you can -change this in an initializer: - -```ruby -ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi -``` - -or - -```ruby -ActiveModel::Serializer.config.adapter = :json_api -``` - -You won't need to implement an adapter unless you wish to use a new format or -media type with AMS. - -If you want to have a root key on your responses you should use the Json adapter, instead of the default Attributes: - -```ruby -ActiveModel::Serializer.config.adapter = :json -``` - -If you would like the key in the outputted JSON to be different from its name in ActiveRecord, you can use the :key option to customize it: - -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :body - - # look up :subject on the model, but use +title+ in the JSON - attribute :subject, :key => :title - has_many :comments -end -``` - -In your controllers, when you use `render :json`, Rails will now first search -for a serializer for the object and use it if available. - -```ruby -class PostsController < ApplicationController - def show - @post = Post.find(params[:id]) - - render json: @post - end -end -``` - -In this case, Rails will look for a serializer named `PostSerializer`, and if -it exists, use it to serialize the `Post`. - -### Specify a serializer - -If you wish to use a serializer other than the default, you can explicitly pass it to the renderer. - -#### 1. For a resource: - -```ruby - render json: @post, serializer: PostPreviewSerializer -``` - -#### 2. For an array resource: - -```ruby -# Use the default `CollectionSerializer`, which will use `each_serializer` to -# serialize each element -render json: @posts, each_serializer: PostPreviewSerializer - -# Or, you can explicitly provide the collection serializer as well -render json: @posts, serializer: CollectionSerializer, each_serializer: PostPreviewSerializer -``` - -### Meta - -If you want a `meta` attribute in your response, specify it in the `render` -call: - -```ruby -render json: @post, meta: { total: 10 } -``` - -The key can be customized using `meta_key` option. - -```ruby -render json: @post, meta: { total: 10 }, meta_key: "custom_meta" -``` - -`meta` will only be included in your response if you are using an Adapter that supports `root`, -as JsonAPI and Json adapters, the default adapter (Attributes) doesn't have `root`. - -### Using a serializer without `render` - -At times, you might want to use a serializer without rendering it to the view. For those cases, you can create an instance of `ActiveModel::SerializableResource` with -the resource you want to be serialized and call `.serializable_hash`. - -```ruby -def create - @message = current_user.messages.create!(message_params) - MessageCreationWorker.perform(serialized_message) - head 204 -end - -def serialized_message - ActiveModel::SerializableResource.new(@message).serializable_hash -end -``` - -### Overriding association methods - -If you want to override any association, you can use: - -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :body - - has_many :comments - - def comments - object.comments.active - end -end -``` - -### Overriding attribute methods - -If you want to override any attribute, you can use: - -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :body - - has_many :comments - - def body - object.body.downcase - end -end -``` - -### Built in Adapters - -#### Attributes +- [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/github/rails-api/active_model_serializers) + - [Guides](docs) +- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) +- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) -It's the default adapter, it generates a json response without a root key. -Doesn't follow any specific convention. +## About -#### JSON +ActiveModelSerializers brings convention over configuration to your JSON generation. -It also generates a json response but always with a root key. The root key **can't be overridden**, and will be automatically defined accordingly with the objects being serialized. -Doesn't follow any specific convention. +ActiveModelSerializers works through two components: **serializers** and **adapters**. -#### JSON API +Serializers describe _which_ attributes and relationships should be serialized. -This adapter follows 1.0 of the format specified in -[jsonapi.org/format](http://jsonapi.org/format). It will include the associated -resources in the `"included"` member when the resource names are included in the -`include` option. Including nested associated resources is also supported. +Adapters describe _how_ attributes and relationships should be serialized. -```ruby - render @posts, include: ['author', 'comments', 'comments.author'] - # or - render @posts, include: 'author,comments,comments.author' -``` +SerializableResource co-ordinates the resource, Adapter and Serializer to produce the +resource serialization. The serialization has the `#as_json`, `#to_json` and `#serializable_hash` +methods used by the Rails JSON Renderer. (SerializableResource actually delegates +these methods to the adapter.) -In addition, two types of wildcards may be used. `*` includes one level of associations, and `**` includes all recursively. These can be combined with other paths. +By default ActiveModelSerializers will use the **Attributes Adapter**. +But we strongly advise you to use **JsonApi Adapter**, which +follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format). +Check how to change the adapter in the sections below. -```ruby - render @posts, include: '**' # or '*' for a single layer -``` +## RELEASE CANDIDATE, PLEASE READ -The following would render posts and include the author, the author's comments, and every resource referenced by the author's comments (recursively). It could be combined, like above, with other paths in any combination desired. +This is the **master** branch of ActiveModelSerializers. -```ruby - render @posts, include: 'author.comments.**' -``` +It will become the `0.10.0` release when it's ready. Currently this is a release candidate. -The JSON API [specifies](http://jsonapi.org/format/#fetching-includes) that the user may supply a parameter specifying which related resources are to be included: +`0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`. -```ruby - render @posts, include: params[:include] -``` +`0.10.x` will be based on the `0.8.0` code, but with a more flexible +architecture. We'd love your help. [Learn how you can help here.](CONTRIBUTING.md) -This raises some security concerns since the user could pass in `include=**`, so filter the values for `include` appropriately if you decide to support this JSON API feature. +It is generally safe and recommended to use the master branch. ## Installation +Note: *ActiveModelSerializers is already included on Rails >= 5* + Add this line to your application's Gemfile: ``` @@ -254,145 +62,80 @@ And then execute: $ bundle ``` -## Creating a Serializer +## Getting Started -The easiest way to create a new serializer is to generate a new resource, which -will generate a serializer at the same time: +See [Getting Started](docs/general/getting_started.md) for the nuts and bolts. -``` -$ rails g resource post title:string body:string -``` +More information is available in the [Guides](docs) and +[High-level behavior](README.md#high-level-behavior). -This will generate a serializer in `app/serializers/post_serializer.rb` for -your new model. You can also generate a serializer for an existing model with -the serializer generator: +## Getting Help -``` -$ rails g serializer post -``` +If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new). -The generated serializer will contain basic `attributes` and -`has_many`/`has_one`/`belongs_to` declarations, based on the model. For example: +If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers). -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :title, :body +Thanks! - has_many :comments - has_one :author -end -``` +## High-level behavior -and +Given a [serializable model](lib/active_model/serializer/lint.rb): ```ruby -class CommentSerializer < ActiveModel::Serializer - attributes :name, :body - - belongs_to :post_id +# either +class SomeResource < ActiveRecord::Base + # columns: title, body +end +# or +class SomeResource < ActiveModelSerializers::Model + attr_accessor :title, :body end ``` -The attribute names are a **whitelist** of attributes to be serialized. - -The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between -resources. By default, when you serialize a `Post`, you will get its `Comments` -as well. - -You may also use the `:serializer` option to specify a custom serializer class, for example: +And initialized as: ```ruby - has_many :comments, serializer: CommentPreviewSerializer +resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration') ``` -And you can change the JSON key that the serializer should use for a particular association: +Given a serializer for the serializable model: ```ruby - has_many :comments, key: :reviews +class SomeSerializer < ActiveModel::Serializer + attribute :title, key: :name + attributes :body +end ``` -## Pagination - -Pagination links will be included in your response automatically as long -as the resource is paginated using [Kaminari](https://github.com/amatsuda/kaminari) or -[WillPaginate](https://github.com/mislav/will_paginate) and -if you are using the ```JsonApi``` adapter. - -Although the others adapters does not have this feature, it is possible to -implement pagination links to `JSON` adapter. For more information about it, -please see in our docs [How to add pagination -links](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md). - -## Caching - -To cache a serializer, call ```cache``` and pass its options. -The options are the same options of ```ActiveSupport::Cache::Store```, plus -a ```key``` option that will be the prefix of the object cache -on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```. - -The cache support is optimized to use the cached object in multiple request. An object cached on a ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically. - -**[NOTE] Every object is individually cached.** - -**[NOTE] The cache is automatically expired after an object is updated, but it's not deleted.** +The model can be serialized as: ```ruby -cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}``` +options = {} +serialization = SerializableResource.new(resource, options) +serialization.to_json +serialization.as_json ``` -Take the example bellow: +SerializableResource delegates to the adapter, which it builds as: ```ruby -class PostSerializer < ActiveModel::Serializer - cache key: 'post', expires_in: 3.hours - attributes :title, :body - - has_many :comments -end +adapter_options = {} +adapter = Adapter.create(serializer, adapter_options) +adapter.to_json +adapter.as_json +adapter.serializable_hash ``` -On this example every ```Post``` object will be cached with -the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want, -but in this case it will be automatically expired after 3 hours. - -### Fragment Caching - -If there is some API endpoint that shouldn't be fully cached, you can still optimise it, using Fragment Cache on the attributes and relationships that you want to cache. - -You can define the attribute by using ```only``` or ```except``` option on cache method. - -**[NOTE] Cache serializers will be used at their relationships** - -Example: +The adapter formats the serializer's attributes and associations (a.k.a. includes): ```ruby -class PostSerializer < ActiveModel::Serializer - cache key: 'post', expires_in: 3.hours, only: [:title] - attributes :title, :body - - has_many :comments -end +serializer_options = {} +serializer = SomeSerializer.new(resource, serializer_options) +serializer.attributes +serializer.associations ``` - -## Serializing non-ActiveRecord objects - -All serializable resources must pass the ActiveModel::Serializer::Lint::Tests. - -See the ActiveModelSerializers::Model for a base class that implements the full -API for a plain-old Ruby object (PORO). - -## Hooks - -To run a hook when ActiveModelSerializers is loaded, use `ActiveSupport.on_load(:active_model_serializers) do end` - -## Getting Help - -If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new). - -If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers). - -Thanks! +See [ARCHITECTURE.md](docs/ARCHITECTURE.md) for more information. # Contributing -See [CONTRIBUTING.md](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md) +See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 4d8106897..3e1d16f90 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,3 +1,5 @@ +[Back to Guides](README.md) + # ARCHITECTURE An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) diff --git a/docs/README.md b/docs/README.md index 51c8c85fb..49b471074 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,40 +1,34 @@ # Docs - ActiveModel::Serializer 0.10.x -This is the documentation of AMS, it's focused on the **0.10.x version.** +This is the documentation of ActiveModelSerializers, it's focused on the **0.10.x version.** ----- ## General - [Getting Started](general/getting_started.md) -- [Adapters](general/adapters.md) - [Configuration Options](general/configuration_options.md) +- [Serializers](general/serializers.md) +- [Adapters](general/adapters.md) +- [Rendering](general/rendering.md) +- [Caching](general/caching.md) - [Logging](general/logging.md) - [Instrumentation](general/instrumentation.md) +- [JSON API Schema](jsonapi/schema.md) +- [ARCHITECTURE](ARCHITECTURE.md) ## How to - [How to add root key](howto/add_root_key.md) - [How to add pagination links](howto/add_pagination_links.md) -- [Using AMS Outside Of Controllers](howto/outside_controller_use.md) +- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md) ## Integrations -| Integration | Supported AMS versions | Gem name and/or link + +| Integration | Supported ActiveModelSerializers versions | Gem name and/or link |----|-----|---- | Ember.js | 0.9.x | [active-model-adapter](https://github.com/ember-data/active-model-adapter) | Ember.js | 0.10.x + | [docs/integrations/ember-and-json-api.md](integrations/ember-and-json-api.md) | Grape | 0.10.x + | [#1258](https://github.com/rails-api/active_model_serializers/issues/1258) | | Grape | 0.9.x | https://github.com/jrhe/grape-active_model_serializers/ | | Sinatra | 0.9.x | https://github.com/SauloSilva/sinatra-active-model-serializers/ - -## Getting Help - -If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new). - -If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers). - -Thanks! - -## Contributing - -See [CONTRIBUTING.md](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md) diff --git a/docs/general/adapters.md b/docs/general/adapters.md index 8723398ca..daee3b8be 100644 --- a/docs/general/adapters.md +++ b/docs/general/adapters.md @@ -1,9 +1,40 @@ +[Back to Guides](../README.md) + # Adapters -AMS works through two components: **serializers** and **adapters**. -Serializers describe _which_ attributes and relationships should be serialized. -Adapters describe _how_ attributes and relationships should be serialized. -You can use one of the built-in adapters (```Attributes``` is the default one) or create one by yourself, but you won't need to implement an adapter unless you wish to use a new format or media type with AMS. +ActiveModelSerializers offers the ability to configure which adapter +to use both globally and/or when serializing (usually when rendering). + +The global adapter configuration is set on [`ActiveModelSerializers.config`](configuration_options.md). +It should be set only once, preferably at initialization. + +For example: + +```ruby +ActiveModelSerializers.config.adapter = ActiveModel::Serializer::Adapter::JsonApi +``` + +or + +```ruby +ActiveModelSerializers.config.adapter = :json_api +``` + +or + +```ruby +ActiveModelSerializers.config.adapter = :json +``` + +The local adapter option is in the format `adapter: adapter`, where `adapter` is +any of the same values as set globally. + +The configured adapter can be set as a symbol, class, or class name, as described in +[Advanced adapter configuration](adapters.md#advanced-adapter-configuration). + +The `Attributes` adapter does not include a root key. It is just the serialized attributes. + +Use either the `JSON` or `JSON API` adapters if you want the response document to have a root key. ## Built in Adapters @@ -14,49 +45,70 @@ Doesn't follow any specific convention. ### JSON -It also generates a json response but always with a root key. The root key **can't be overridden**, and will be automatically defined accordingly to the objects being serialized. +The response document always with a root key. + +The root key **can't be overridden**, and will be derived from the resource being serialized. + Doesn't follow any specific convention. ### JSON API -This adapter follows **version 1.0** of the format specified in -[jsonapi.org/format](http://jsonapi.org/format). It will include the associated -resources in the `"included"` member when the resource names are included in the -`include` option. +This adapter follows **version 1.0** of the [format specified](../jsonapi/schema.md) in +[jsonapi.org/format](http://jsonapi.org/format). -```ruby -render @posts, include: ['authors', 'comments'] -``` +#### Included -or +It will include the associated resources in the `"included"` member +when the resource names are included in the `include` option. +Including nested associated resources is also supported. ```ruby -render @posts, include: 'authors,comments' + render @posts, include: ['author', 'comments', 'comments.author'] + # or + render @posts, include: 'author,comments,comments.author' ``` -The format of the `include` option can be either a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes), an Array of Symbols and Hashes, or a mix of both. +In addition, two types of wildcards may be used: -## Choosing an adapter +- `*` includes one level of associations. +- `**` includes all recursively. -If you want to use a specify a default adapter, such as JsonApi, you can change this in an initializer: +These can be combined with other paths. ```ruby -ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi + render @posts, include: '**' # or '*' for a single layer ``` -or +The format of the `include` option can be either: + +- a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes). +- an Array of Symbols and Hashes. +- a mix of both. + +The following would render posts and include: + +- the author +- the author's comments, and +- every resource referenced by the author's comments (recursively). + +It could be combined, like above, with other paths in any combination desired. ```ruby -ActiveModel::Serializer.config.adapter = :json_api + render @posts, include: 'author.comments.**' ``` -If you want to have a root key for each resource in your responses, you should use the Json or -JsonApi adapters instead of the default Attributes: +##### Security Considerations + +Since the included options may come from the query params (i.e. user-controller): ```ruby -ActiveModel::Serializer.config.adapter = :json + render @posts, include: params[:include] ``` +The user could pass in `include=**`. + +We recommend filtering any user-supplied includes appropriately. + ## Advanced adapter configuration ### Registering an adapter diff --git a/docs/general/caching.md b/docs/general/caching.md new file mode 100644 index 000000000..7d02568a3 --- /dev/null +++ b/docs/general/caching.md @@ -0,0 +1,52 @@ +[Back to Guides](../README.md) + +# Caching + +To cache a serializer, call ```cache``` and pass its options. +The options are the same options of ```ActiveSupport::Cache::Store```, plus +a ```key``` option that will be the prefix of the object cache +on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```. + +The cache support is optimized to use the cached object in multiple request. An object cached on a ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically. + +**[NOTE] Every object is individually cached.** + +**[NOTE] The cache is automatically expired after an object is updated, but it's not deleted.** + +```ruby +cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}``` +``` + +Take the example bellow: + +```ruby +class PostSerializer < ActiveModel::Serializer + cache key: 'post', expires_in: 3.hours + attributes :title, :body + + has_many :comments +end +``` + +On this example every ```Post``` object will be cached with +the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want, +but in this case it will be automatically expired after 3 hours. + +## Fragment Caching + +If there is some API endpoint that shouldn't be fully cached, you can still optimise it, using Fragment Cache on the attributes and relationships that you want to cache. + +You can define the attribute by using ```only``` or ```except``` option on cache method. + +**[NOTE] Cache serializers will be used at their relationships** + +Example: + +```ruby +class PostSerializer < ActiveModel::Serializer + cache key: 'post', expires_in: 3.hours, only: [:title] + attributes :title, :body + + has_many :comments +end +``` diff --git a/docs/general/configuration_options.md b/docs/general/configuration_options.md index 46465f062..81d03bb86 100644 --- a/docs/general/configuration_options.md +++ b/docs/general/configuration_options.md @@ -1,6 +1,9 @@ +[Back to Guides](../README.md) + # Configuration Options -The following configuration options can be set on `ActiveModel::Serializer.config` inside an initializer. +The following configuration options can be set on `ActiveModelSerializers.config`, +preferably inside an initializer. ## General @@ -17,3 +20,7 @@ The following configuration options can be set on `ActiveModel::Serializer.confi Default: `'1.0'`. - `jsonapi_toplevel_meta`: Optional metadata. Not included if empty. Default: `{}`. + +## Hooks + +To run a hook when ActiveModelSerializers is loaded, use `ActiveSupport.on_load(:active_model_serializers) do end` diff --git a/docs/general/getting_started.md b/docs/general/getting_started.md index 011f90ce7..91e74c09d 100644 --- a/docs/general/getting_started.md +++ b/docs/general/getting_started.md @@ -1,20 +1,6 @@ -# Getting Started - -## Installation - -### ActiveModel::Serializer is already included on Rails >= 5 - -Add this line to your application's Gemfile: - -``` -gem 'active_model_serializers' -``` - -And then execute: +[Back to Guides](../README.md) -``` -$ bundle -``` +# Getting Started ## Creating a Serializer @@ -33,7 +19,7 @@ the serializer generator: $ rails g serializer post ``` -The generated seralizer will contain basic `attributes` and +The generated serializer will contain basic `attributes` and `has_many`/`has_one`/`belongs_to` declarations, based on the model. For example: ```ruby @@ -42,7 +28,6 @@ class PostSerializer < ActiveModel::Serializer has_many :comments has_one :author - end ``` @@ -53,13 +38,20 @@ class CommentSerializer < ActiveModel::Serializer attributes :name, :body belongs_to :post_id - end ``` +The attribute names are a **whitelist** of attributes to be serialized. + +The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between +resources. By default, when you serialize a `Post`, you will get its `Comments` +as well. + +For more information, see [Serializers](docs/general/serializers.md). + ### Namespaced Models -When serializing a model inside a namespace, such as `Api::V1::Post`, AMS will expect the corresponding serializer to be inside the same namespace (namely `Api::V1::PostSerializer`). +When serializing a model inside a namespace, such as `Api::V1::Post`, ActiveModelSerializers will expect the corresponding serializer to be inside the same namespace (namely `Api::V1::PostSerializer`). ### Model Associations and Nested Serializers @@ -69,7 +61,7 @@ class PostSerializer < ActiveModel::Serializer has_many :comments end ``` -AMS will look for `PostSerializer::CommentSerializer` in priority, and fall back to `::CommentSerializer` in case the former does not exist. This allows for more control over the way a model gets serialized as an association of an other model. +ActiveModelSerializers will look for `PostSerializer::CommentSerializer` in priority, and fall back to `::CommentSerializer` in case the former does not exist. This allows for more control over the way a model gets serialized as an association of an other model. For example, in the following situation: @@ -86,11 +78,11 @@ class PostSerializer < ActiveModel::Serializer end ``` -AMS will use `PostSerializer::CommentSerializer` (thus including only the `:body_short` attribute) when serializing a `Comment` as part of a `Post`, but use `::CommentSerializer` when serializing a `Comment` directly (thus including `:body, :date, :nb_likes`). +ActiveModelSerializers will use `PostSerializer::CommentSerializer` (thus including only the `:body_short` attribute) when serializing a `Comment` as part of a `Post`, but use `::CommentSerializer` when serializing a `Comment` directly (thus including `:body, :date, :nb_likes`). ## Rails Integration -AMS will automatically integrate with you Rails app, you won't need to update your controller, this is a example of how it will look like: +ActiveModelSerializers will automatically integrate with you Rails app, you won't need to update your controller, this is a example of how it will look like: ```ruby class PostsController < ApplicationController diff --git a/docs/general/instrumentation.md b/docs/general/instrumentation.md index 160a9e765..ba6a1ffd8 100644 --- a/docs/general/instrumentation.md +++ b/docs/general/instrumentation.md @@ -1,3 +1,5 @@ +[Back to Guides](../README.md) + # Instrumentation ActiveModelSerializers uses the @@ -30,6 +32,7 @@ ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |*ar # event.payload # whatever end +``` ## [LogSubscriber](http://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html) diff --git a/docs/general/logging.md b/docs/general/logging.md index 010ae01a1..a37e5d9fb 100644 --- a/docs/general/logging.md +++ b/docs/general/logging.md @@ -1,11 +1,13 @@ +[Back to Guides](../README.md) + # Logging -If we are using ActiveModel::Serializers on Rails app by default the `Rails.logger` will be used. +If we are using ActiveModelSerializers on a Rails app by default the `Rails.logger` will be used. On a non Rails enviroment by default the `ActiveSupport::TaggedLogging` will be used. -If we need to customize the logger we can define this in an initializer: +You may customize the logger we by in an initializer, for example: ```ruby ActiveModelSerializers.logger = Logger.new(STDOUT) diff --git a/docs/general/rendering.md b/docs/general/rendering.md new file mode 100644 index 000000000..4350a7192 --- /dev/null +++ b/docs/general/rendering.md @@ -0,0 +1,136 @@ +[Back to Guides](../README.md) + +# Rendering + +### Implicit Serializer + +In your controllers, when you use `render :json`, Rails will now first search +for a serializer for the object and use it if available. + +```ruby +class PostsController < ApplicationController + def show + @post = Post.find(params[:id]) + + render json: @post + end +end +``` + +In this case, Rails will look for a serializer named `PostSerializer`, and if +it exists, use it to serialize the `Post`. + +### Explicit Serializer + +If you wish to use a serializer other than the default, you can explicitly pass it to the renderer. + +#### 1. For a resource: + +```ruby + render json: @post, serializer: PostPreviewSerializer +``` + +#### 2. For a resource collection: + +Specify the serializer for each resource with `each_serializer` + +```ruby +render json: @posts, each_serializer: PostPreviewSerializer +``` + +The default serializer for collections is `CollectionSerializer`. + +Specify the collection serializer with the `serializer` option. + +```ruby +render json: @posts, serializer: CollectionSerializer, each_serializer: PostPreviewSerializer +``` + +## 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). + +## SerializableResource options + +The `options` hash passed to `render` or `ActiveModel::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 [ActiveModel::SerializableResource::ADAPTER_OPTIONS](../../lib/active_model/serializable_resource.rb#L4). +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. + +### adapter_opts + +#### fields + +PR please :) + +#### adapter + +PR please :) + +#### meta + +If you want a `meta` attribute in your response, specify it in the `render` +call: + +```ruby +render json: @post, meta: { total: 10 } +``` + +The key can be customized using `meta_key` option. + +```ruby +render json: @post, meta: { total: 10 }, meta_key: "custom_meta" +``` + +`meta` will only be included in your response if you are using an Adapter that supports `root`, +as JsonAPI and Json adapters, the default adapter (Attributes) doesn't have `root`. + +#### meta_key + +PR please :) + +#### links + +PR please :) + +### serializer_opts + +#### include + +PR please :) + +#### root + +PR please :) + +#### serializer + +PR please :) + +#### scope + +PR please :) + +#### scope_name + +PR please :) + +## Using a serializer without `render` + +See [Usage outside of a controller](../howto/outside_controller_use.md#serializing-before-controller-render). + +## Pagination + +See [How to add pagination links](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md). diff --git a/docs/general/serializers.md b/docs/general/serializers.md new file mode 100644 index 000000000..67a14c2a6 --- /dev/null +++ b/docs/general/serializers.md @@ -0,0 +1,213 @@ +[Back to Guides](../README.md) + +# Serializers + +Given a serializer class: + +```ruby +class SomeSerializer < ActiveModel::Serializer +end +``` + +The following methods may be defined in it: + +### Attributes + +#### ::attributes + +Serialization of the resource `title` and `body` + +| In Serializer | #attributes | +|---------------------------- |-------------| +| `attributes :title, :body` | `{ title: 'Some Title', body: 'Some Body' }` +| `attributes :title, :body`
`def body "Special #{object.body}" end` | `{ title: 'Some Title', body: 'Special Some Body' }` + + +#### ::attribute + +Serialization of the resource `title` + +| In Serializer | #attributes | +|---------------------------- |-------------| +| `attribute :title` | `{ title: 'Some Title' } ` +| `attribute :title, key: :name` | `{ name: 'Some Title' } ` +| `attribute :title { 'A Different Title'}` | `{ title: 'A Different Title' } ` +| `attribute :title`
`def title 'A Different Title' end` | `{ title: 'A Different Title' }` + +### Associations + +#### ::has_one + +e.g. + +```ruby +has_one :bio +has_one :blog, key: :site +has_one :maker, virtual_value: { id: 1 } +``` + +#### ::has_many + +e.g. + +```ruby +has_many :comments +has_many :comments, key: :reviews +has_many :comments, serializer: CommentPreviewSerializer +has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }] +has_many :comments, key: :last_comments do + last(1) +end +``` + +#### ::belongs_to + +e.g. + +```ruby +belongs_to :author, serializer: AuthorPreviewSerializer +belongs_to :author, key: :writer +belongs_to :post +belongs_to :blog +def blog + Blog.new(id: 999, name: 'Custom blog') +end +``` + +### Caching + +#### ::cache + +e.g. + +```ruby +cache key: 'post', expires_in: 0.1, skip_digest: true +cache expires_in: 1.day, skip_digest: true +cache key: 'writer', skip_digest: true +cache only: [:name], skip_digest: true +cache except: [:content], skip_digest: true +cache key: 'blog' +cache only: [:id] +``` + +#### #cache_key + +e.g. + +```ruby +# Uses a custom non-time-based cache key +def cache_key + "#{self.class.name.downcase}/#{self.id}" +end +``` + +### Other + +#### ::type + +e.g. + +```ruby +class UserProfileSerializer < ActiveModel::Serializer + type 'profile' +end +``` + +#### ::link + +e.g. + +```ruby +link :other, 'https://example.com/resource' +link :self do + href "https://example.com/link_author/#{object.id}" +end +``` + +#### #object + +The object being serialized. + +#### #root + +PR please :) + +#### #scope + +PR please :) + +#### #read_attribute_for_serialization(key) + +The serialized value for a given key. e.g. `read_attribute_for_serialization(:title) #=> 'Hello World'` + +#### #links + +PR please :) + +#### #json_key + +PR please :) + +## Examples + +Given two models, a `Post(title: string, body: text)` and a +`Comment(name: string, body: text, post_id: integer)`, you will have two +serializers: + +```ruby +class PostSerializer < ActiveModel::Serializer + cache key: 'posts', expires_in: 3.hours + attributes :title, :body + + has_many :comments +end +``` + +and + +```ruby +class CommentSerializer < ActiveModel::Serializer + attributes :name, :body + + belongs_to :post +end +``` + +Generally speaking, you, as a user of ActiveModelSerializers, will write (or generate) these +serializer classes. + +## More Info + +For more information, see [the Serializer class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer.rb) + +## Overriding association methods + +If you want to override any association, you can use: + +```ruby +class PostSerializer < ActiveModel::Serializer + attributes :id, :body + + has_many :comments + + def comments + object.comments.active + end +end +``` + +## Overriding attribute methods + +If you want to override any attribute, you can use: + +```ruby +class PostSerializer < ActiveModel::Serializer + attributes :id, :body + + has_many :comments + + def body + object.body.downcase + end +end +``` diff --git a/docs/howto/add_pagination_links.md b/docs/howto/add_pagination_links.md index 0e784ec92..64b903fb6 100644 --- a/docs/howto/add_pagination_links.md +++ b/docs/howto/add_pagination_links.md @@ -1,3 +1,5 @@ +[Back to Guides](../README.md) + # How to add pagination links ### JSON API adapter @@ -8,6 +10,10 @@ the resource is paginated and if you are using the ```JsonApi``` adapter. If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). +Although the others adapters does not have this feature, it is possible to +implement pagination links to `JSON` adapter. For more information about it, +please see in our docs + ###### Kaminari examples ```ruby @@ -33,7 +39,7 @@ render json: @posts ``` ```ruby -ActiveModel::Serializer.config.adapter = :json_api +ActiveModelSerializers.config.adapter = :json_api ``` ex: @@ -61,7 +67,7 @@ ex: } ``` -AMS pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). +ActiveModelSerializers pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). ### JSON adapter diff --git a/docs/howto/add_root_key.md b/docs/howto/add_root_key.md index 51f0f1e1f..3ee032e7a 100644 --- a/docs/howto/add_root_key.md +++ b/docs/howto/add_root_key.md @@ -1,6 +1,6 @@ # How to add root key -Add the root key to your API is quite simple with AMS. The **Adapter** is what determines the format of your JSON response. The default adapter is the ```Attributes``` which doesn't have the root key, so your response is something similar to: +Add the root key to your API is quite simple with ActiveModelSerializers. The **Adapter** is what determines the format of your JSON response. The default adapter is the ```Attributes``` which doesn't have the root key, so your response is something similar to: ```json { @@ -13,10 +13,10 @@ Add the root key to your API is quite simple with AMS. The **Adapter** is what d In order to add the root key you need to use the ```JSON``` Adapter, you can change this in an initializer: ```ruby -ActiveModel::Serializer.config.adapter = :json +ActiveModelSerializers.config.adapter = :json ``` -You can also specify a class as adapter, as long as it complies with the AMS adapters interface. +You can also specify a class as adapter, as long as it complies with the ActiveModelSerializers adapters interface. It will add the root key to all your serialized endpoints. ex: diff --git a/docs/howto/outside_controller_use.md b/docs/howto/outside_controller_use.md index e74258c08..9ca46d496 100644 --- a/docs/howto/outside_controller_use.md +++ b/docs/howto/outside_controller_use.md @@ -1,8 +1,10 @@ -## Using AMS Outside Of A Controller +[Back to Guides](../README.md) -### Serializing a resource +## Using ActiveModelSerializers Outside Of A Controller -In AMS versions 0.10 or later, serializing resources outside of the controller context is fairly simple: +### Serializing a resource + +In ActiveModelSerializers versions 0.10 or later, serializing resources outside of the controller context is fairly simple: ```ruby # Create our resource @@ -16,14 +18,14 @@ serializable_resource = ActiveModel::SerializableResource.new(post, options) # Convert your resource into json model_json = serializable_resource.as_json -``` +``` -### Retrieving a Resource's Active Model Serializer +### Looking up the Serializer for a Resource If you want to retrieve a serializer for a specific resource, you can do the following: ```ruby -# Create our resource +# Create our resource post = Post.create(title: "Another Example", body: "So much fun.") # Optional options parameters @@ -33,10 +35,24 @@ options = {} serializer = ActiveModel::Serializer.serializer_for(post, options) ``` -You could also retrieve the serializer via: +You could also retrieve the serializer via: ```ruby -ActiveModel::SerializableResource.new(post, options).serializer +ActiveModel::SerializableResource.new(post, options).serializer ``` -Both approaches will return an instance, if any, of the resource's serializer. \ No newline at end of file +Both approaches will return an instance, if any, of the resource's serializer. + +## Serializing before controller render + +At times, you might want to use a serializer without rendering it to the view. For those cases, you can create an instance of `ActiveModel::SerializableResource` with +the resource you want to be serialized and call `.as_json`. + +```ruby +def create + message = current_user.messages.create!(message_params) + message_json = ActiveModel::SerializableResource.new(message).as_json + MessageCreationWorker.perform(message_json) + head 204 +end +``` diff --git a/docs/integrations/ember-and-json-api.md b/docs/integrations/ember-and-json-api.md index cf2e52ae4..bd4fa4c0e 100644 --- a/docs/integrations/ember-and-json-api.md +++ b/docs/integrations/ember-and-json-api.md @@ -1,3 +1,5 @@ +[Back to Guides](../README.md) + # Integrating with Ember and JSON API - [Preparation](./ember-and-json-api.md#preparation) @@ -10,7 +12,7 @@ Note: This guide assumes that `ember-cli` is used for your ember app. -The JSON API specification calls for hyphens for multi-word separators. AMS uses underscores. +The JSON API specification calls for hyphens for multi-word separators. ActiveModelSerializers uses underscores. To solve this, in Ember, both the adapter and the serializer will need some modifications: ### Server-Side Changes @@ -86,7 +88,7 @@ export default DS.JSONAPISerializer.extend({ ## Including Nested Resources Previously, `store.find` and `store.findRecord` did not allow specification of any query params. -The AMS default for the `include` parameter is to be `nil` meaning that if any associations are defined in your serializer, only the `id` and `type` will be in the `relationships` structure of the JSON API response. +The ActiveModelSerializers default for the `include` parameter is to be `nil` meaning that if any associations are defined in your serializer, only the `id` and `type` will be in the `relationships` structure of the JSON API response. For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API) With the above modifications, you can execute code as below in order to include nested resources while doing a find query. diff --git a/docs/jsonapi/schema.md b/docs/jsonapi/schema.md index 1a42585b3..72b149484 100644 --- a/docs/jsonapi/schema.md +++ b/docs/jsonapi/schema.md @@ -1,3 +1,5 @@ +[Back to Guides](../README.md) + [![JSON API 1.0](https://img.shields.io/badge/JSON%20API-1.0-lightgrey.svg)](http://jsonapi.org/) ## JSON API Requests diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index b34b5ab41..1c7f7226b 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -11,11 +11,11 @@ class JsonApi < Base # then extract to its own file and require it. module ApiObjects module JsonApi - ActiveModel::Serializer.config.jsonapi_version = '1.0' - ActiveModel::Serializer.config.jsonapi_toplevel_meta = {} + ActiveModelSerializers.config.jsonapi_version = '1.0' + ActiveModelSerializers.config.jsonapi_toplevel_meta = {} # Make JSON API top-level jsonapi member opt-in # ref: http://jsonapi.org/format/#document-top-level - ActiveModel::Serializer.config.jsonapi_include_toplevel_object = false + ActiveModelSerializers.config.jsonapi_include_toplevel_object = false module_function @@ -24,15 +24,15 @@ def add!(hash) end def include_object? - ActiveModel::Serializer.config.jsonapi_include_toplevel_object + ActiveModelSerializers.config.jsonapi_include_toplevel_object end # TODO: see if we can cache this def object object = { jsonapi: { - version: ActiveModel::Serializer.config.jsonapi_version, - meta: ActiveModel::Serializer.config.jsonapi_toplevel_meta + version: ActiveModelSerializers.config.jsonapi_version, + meta: ActiveModelSerializers.config.jsonapi_toplevel_meta } } object[:jsonapi].reject! { |_, v| v.blank? } @@ -114,7 +114,7 @@ def serializable_hash_for_single_resource def resource_identifier_type_for(serializer) return serializer._type if serializer._type - if ActiveModel::Serializer.config.jsonapi_resource_type == :singular + if ActiveModelSerializers.config.jsonapi_resource_type == :singular serializer.object.class.model_name.singular else serializer.object.class.model_name.plural diff --git a/test/adapter/json_api/resource_type_config_test.rb b/test/adapter/json_api/resource_type_config_test.rb index 39462b994..f67ecca73 100644 --- a/test/adapter/json_api/resource_type_config_test.rb +++ b/test/adapter/json_api/resource_type_config_test.rb @@ -33,11 +33,11 @@ def setup end def with_jsonapi_resource_type type - old_type = ActiveModel::Serializer.config.jsonapi_resource_type - ActiveModel::Serializer.config.jsonapi_resource_type = type + old_type = ActiveModelSerializers.config.jsonapi_resource_type + ActiveModelSerializers.config.jsonapi_resource_type = type yield ensure - ActiveModel::Serializer.config.jsonapi_resource_type = old_type + ActiveModelSerializers.config.jsonapi_resource_type = old_type end def test_config_plural diff --git a/test/serializers/adapter_for_test.rb b/test/serializers/adapter_for_test.rb index ecb837770..b4a51a397 100644 --- a/test/serializers/adapter_for_test.rb +++ b/test/serializers/adapter_for_test.rb @@ -4,11 +4,11 @@ class AdapterForTest < Minitest::Test UnknownAdapterError = ::ActiveModel::Serializer::Adapter::UnknownAdapterError def setup - @previous_adapter = ActiveModel::Serializer.config.adapter + @previous_adapter = ActiveModelSerializers.config.adapter end def teardown - ActiveModel::Serializer.config.adapter = @previous_adapter + ActiveModelSerializers.config.adapter = @previous_adapter end def test_returns_default_adapter @@ -17,23 +17,23 @@ def test_returns_default_adapter end def test_overwrite_adapter_with_symbol - ActiveModel::Serializer.config.adapter = :null + ActiveModelSerializers.config.adapter = :null adapter = ActiveModel::Serializer.adapter assert_equal ActiveModel::Serializer::Adapter::Null, adapter ensure - ActiveModel::Serializer.config.adapter = @previous_adapter + ActiveModelSerializers.config.adapter = @previous_adapter end def test_overwrite_adapter_with_class - ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::Null + ActiveModelSerializers.config.adapter = ActiveModel::Serializer::Adapter::Null adapter = ActiveModel::Serializer.adapter assert_equal ActiveModel::Serializer::Adapter::Null, adapter end def test_raises_exception_if_invalid_symbol_given - ActiveModel::Serializer.config.adapter = :unknown + ActiveModelSerializers.config.adapter = :unknown assert_raises UnknownAdapterError do ActiveModel::Serializer.adapter @@ -41,7 +41,7 @@ def test_raises_exception_if_invalid_symbol_given end def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter - ActiveModel::Serializer.config.adapter = 42 + ActiveModelSerializers.config.adapter = 42 assert_raises UnknownAdapterError do ActiveModel::Serializer.adapter @@ -110,7 +110,7 @@ def test_lookup_adapter_for_unknown_name end def test_adapter - assert_equal ActiveModel::Serializer.config.adapter, :attributes + assert_equal ActiveModelSerializers.config.adapter, :attributes assert_equal ActiveModel::Serializer.adapter, ActiveModel::Serializer::Adapter::Attributes end diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index ecb671f2a..d3117f077 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -33,13 +33,13 @@ def test_has_many_and_has_one case key when :posts assert_equal({}, options) - assert_kind_of(ActiveModel::Serializer.config.collection_serializer, serializer) + assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) when :bio assert_equal({}, options) assert_nil serializer when :roles assert_equal({}, options) - assert_kind_of(ActiveModel::Serializer.config.collection_serializer, serializer) + assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) else flunk "Unknown association: #{key}" end diff --git a/test/serializers/configuration_test.rb b/test/serializers/configuration_test.rb index d9667b9e1..6ae2ea679 100644 --- a/test/serializers/configuration_test.rb +++ b/test/serializers/configuration_test.rb @@ -4,15 +4,15 @@ module ActiveModel class Serializer class ConfigurationTest < Minitest::Test def test_collection_serializer - assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModel::Serializer.config.collection_serializer + assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.collection_serializer end def test_array_serializer - assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModel::Serializer.config.array_serializer + assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.array_serializer end def test_setting_array_serializer_sets_collection_serializer - config = ActiveModel::Serializer.config + config = ActiveModelSerializers.config old_config = config.dup begin assert_equal ActiveModel::Serializer::CollectionSerializer, config.collection_serializer @@ -20,12 +20,12 @@ def test_setting_array_serializer_sets_collection_serializer assert_equal config.array_serializer, :foo assert_equal config.collection_serializer, :foo ensure - ActiveModel::Serializer.config.replace(old_config) + ActiveModelSerializers.config.replace(old_config) end end def test_default_adapter - assert_equal :attributes, ActiveModel::Serializer.config.adapter + assert_equal :attributes, ActiveModelSerializers.config.adapter end end end diff --git a/test/serializers/serializer_for_test.rb b/test/serializers/serializer_for_test.rb index 53b2f07de..bee10957a 100644 --- a/test/serializers/serializer_for_test.rb +++ b/test/serializers/serializer_for_test.rb @@ -6,21 +6,21 @@ class SerializerForTest < Minitest::Test class CollectionSerializerTest < Minitest::Test def setup @array = [1, 2, 3] - @previous_collection_serializer = ActiveModel::Serializer.config.collection_serializer + @previous_collection_serializer = ActiveModelSerializers.config.collection_serializer end def teardown - ActiveModel::Serializer.config.collection_serializer = @previous_collection_serializer + ActiveModelSerializers.config.collection_serializer = @previous_collection_serializer end def test_serializer_for_array serializer = ActiveModel::Serializer.serializer_for(@array) - assert_equal ActiveModel::Serializer.config.collection_serializer, serializer + assert_equal ActiveModelSerializers.config.collection_serializer, serializer end def test_overwritten_serializer_for_array new_collection_serializer = Class.new - ActiveModel::Serializer.config.collection_serializer = new_collection_serializer + ActiveModelSerializers.config.collection_serializer = new_collection_serializer serializer = ActiveModel::Serializer.serializer_for(@array) assert_equal new_collection_serializer, serializer end diff --git a/test/support/serialization_testing.rb b/test/support/serialization_testing.rb index e12720974..f5a8f6faa 100644 --- a/test/support/serialization_testing.rb +++ b/test/support/serialization_testing.rb @@ -1,6 +1,6 @@ module SerializationTesting def config - ActiveModel::Serializer.config + ActiveModelSerializers.config end private @@ -15,20 +15,20 @@ def generate_cached_serializer(obj) # to pass in the +adapter+ option to ActiveModel::SerializableResource. # e.g ActiveModel::SerializableResource.new(resource, adapter: :json_api) def with_adapter(adapter) - old_adapter = ActiveModel::Serializer.config.adapter - ActiveModel::Serializer.config.adapter = adapter + old_adapter = ActiveModelSerializers.config.adapter + ActiveModelSerializers.config.adapter = adapter yield ensure - ActiveModel::Serializer.config.adapter = old_adapter + ActiveModelSerializers.config.adapter = old_adapter end alias_method :with_configured_adapter, :with_adapter def with_config(hash) old_config = config.dup - ActiveModel::Serializer.config.update(hash) + ActiveModelSerializers.config.update(hash) yield ensure - ActiveModel::Serializer.config.replace(old_config) + ActiveModelSerializers.config.replace(old_config) end def serializable(resource, options = {})