README.md in active_model_serializers-0.9.13 vs README.md in active_model_serializers-0.10.0.rc1

- old
+ new

@@ -1,956 +1,326 @@ -[![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png?branch=0-9-stable)](https://travis-ci.org/rails-api/active_model_serializers) -[![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers) - # ActiveModel::Serializers -## Purpose +[![Build Status](https://travis-ci.org/rails-api/active_model_serializers.svg)](https://travis-ci.org/rails-api/active_model_serializers) -`ActiveModel::Serializers` encapsulates the JSON serialization of objects. -Objects that respond to read\_attribute\_for\_serialization -(including `ActiveModel` and `ActiveRecord` objects) are supported. +ActiveModel::Serializers brings convention over configuration to your JSON generation. -Serializers know about both a model and the `current_user`, so you can -customize serialization based upon whether a user is authorized to see the -content. +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. -In short, **serializers replace hash-driven development with object-oriented -development.** +# RELEASE CANDIDATE, PLEASE READ -# Installing +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`. -The easiest way to install `ActiveModel::Serializers` is to add it to your -`Gemfile`: +`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) -```ruby -gem "active_model_serializers" -``` +## Example -Then, install it on the command line: +Given two models, a `Post(title: string, body: text)` and a +`Comment(name:string, body:text, post_id:integer)`, you will have two +serializers: -``` -$ bundle install -``` - -#### Ruby 1.8 is no longer supported! - -If you must use a ruby 1.8 version (MRI 1.8.7, REE, Rubinius 1.8, or JRuby 1.8), you need to use version 0.8.x. -Versions after 0.9.0 do not support ruby 1.8. To specify version 0.8, include this in your Gemfile: - ```ruby -gem "active_model_serializers", "~> 0.8.0" -``` - - -# Creating a Serializer - -The easiest way to create a new serializer is to generate a new resource, which -will generate a serializer at the same time: - -``` -$ rails g resource post title:string body:string -``` - -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: - -``` -$ rails g serializer post -``` - -### Support for POROs - -The PORO should include ActiveModel::SerializerSupport. That's all you need to -do to have your POROs supported. - -For Rails versions before Rails 4 ActiveModel::Serializers expects objects to -implement `read_attribute_for_serialization`. - -# render :json - -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`. - -This also works with `respond_with`, which uses `to_json` under the hood. Also -note that any options passed to `render :json` will be passed to your -serializer and available as `@serialization_options` inside. - -To specify a custom serializer for an object, you can specify the -serializer when you render the object: - -```ruby -render json: @post, serializer: FancyPostSerializer -``` - -### Use serialization outside of ActionController::Base - -When controller does not inherit from ActionController::Base, -include Serialization module manually: - -```ruby -class ApplicationController < ActionController::API - include ActionController::Serialization -end -``` - -## Arrays - -In your controllers, when you use `render :json` for an array of objects, AMS will -use `ActiveModel::ArraySerializer` (included in this project) as the base serializer, -and the individual `Serializer` for the objects contained in that array. - -```ruby class PostSerializer < ActiveModel::Serializer + cache key: 'posts', expires_in: 3.hours attributes :title, :body -end -class PostsController < ApplicationController - def index - @posts = Post.all - render json: @posts - end -end -``` + has_many :comments -Given the example above, the index action will return - -```json -{ - "posts": - [ - { "title": "Post 1", "body": "Hello!" }, - { "title": "Post 2", "body": "Goodbye!" } - ] -} -``` - -By default, the root element is the name of the controller. For example, `PostsController` -generates a root element "posts". To change it: - -```ruby -render json: @posts, root: "some_posts" -``` - -You may disable the root element for arrays at the top level, which will result in -more concise json. See the next section for ways on how to do this. Disabling the -root element of the array with any of those methods will produce - -```json -[ - { "title": "Post 1", "body": "Hello!" }, - { "title": "Post 2", "body": "Goodbye!" } -] -``` - -To specify a custom serializer for the items within an array: - -```ruby -render json: @posts, each_serializer: FancyPostSerializer -``` - -## Render independently - -By default the setting of serializer is in controller as described above which is the -recommended way. However, there may be cases you need to render the json object elsewhere -say in a helper or a view when controller is only for main object. - -Then you can render the serialized JSON independently. - -```ruby -def current_user_as_json_helper - CurrentUserSerializer.new(current_user).to_json + url :post end ``` -You can also render an array of objects using ArraySerializer. +and ```ruby -def users_array_as_json_helper(users) - ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json -end -``` +class CommentSerializer < ActiveModel::Serializer + attributes :name, :body + belongs_to :post -## Disabling the root element - -You have 4 options to disable the root element, each with a slightly different scope: - -#### 1. Disable root globally for all, or per class - -In an initializer: - -```ruby -# Disable for all serializers (except ArraySerializer) -ActiveModel::Serializer.root = false - -# Disable for ArraySerializer -ActiveModel::ArraySerializer.root = false -``` - -#### 2. Disable root per render call in your controller - -```ruby -render json: @posts, root: false -``` - -#### 3. Subclass the serializer, and specify using it - -```ruby -class CustomArraySerializer < ActiveModel::ArraySerializer - self.root = false + url [:post, :comment] end - -# controller: -render json: @posts, serializer: CustomArraySerializer ``` -#### 4. Define default_serializer_options in your controller +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: -If you define `default_serializer_options` method in your controller, -all serializers in actions of this controller and it's children will use them. -One of the options may be `root: false` - ```ruby -def default_serializer_options - { - root: false - } -end +ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi ``` -## Changing the Key Format +or -You can specify that serializers use the lower-camel key format at the config, class or instance level. - ```ruby - -ActiveModel::Serializer.setup do |config| - config.key_format = :lower_camel -end - -class BlogLowerCamelSerializer < ActiveModel::Serializer - format_keys :lower_camel -end - -BlogSerializer.new(object, key_format: :lower_camel) +ActiveModel::Serializer.config.adapter = :json_api ``` -## Changing the default association key type +You won't need to implement an adapter unless you wish to use a new format or +media type with AMS. -You can specify that serializers use unsuffixed names as association keys by default. +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 -ActiveModel::Serializer.setup do |config| - config.default_key_type = :name -end -``` - -This will build association keys like `comments` or `author` instead of `comment_ids` or `author_id`. - -## Getting the old version - -If you find that your project is already relying on the old rails to_json -change `render :json` to `render json: @your_object.to_json`. - -# Attributes and Associations - -Once you have a serializer, you can specify which attributes and associations -you would like to include in the serialized form. - -```ruby class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body + attributes :id, :body + + # look up :subject on the model, but use +title+ in the JSON + attribute :subject, :key => :title has_many :comments end ``` -## Attributes +In your controllers, when you use `render :json`, Rails will now first search +for a serializer for the object and use it if available. -For specified attributes, a serializer will look up the attribute on the -object you passed to `render :json`. It uses -`read_attribute_for_serialization`, which `ActiveRecord` objects implement as a -regular attribute lookup. - -Before looking up the attribute on the object, a serializer will check for the -presence of a method with the name of the attribute. This allows serializers to -include properties beyond the simple attributes of the model. For example: - ```ruby -class PersonSerializer < ActiveModel::Serializer - attributes :first_name, :last_name, :full_name +class PostsController < ApplicationController + def show + @post = Post.find(params[:id]) - def full_name - "#{object.first_name} #{object.last_name}" + render json: @post end end ``` -Within a serializer's methods, you can access the object being -serialized as `object`. +In this case, Rails will look for a serializer named `PostSerializer`, and if +it exists, use it to serialize the `Post`. -Since this shadows any attribute named `object`, you can include them through `object.object`. For example: +### Specify a serializer -```ruby -class VersionSerializer < ActiveModel::Serializer - attributes :version_object +If you wish to use a serializer other than the default, you can explicitly pass it to the renderer. - def version_object - object.object - end -end -``` +#### 1. For a resource: -You can also access the `scope` method, which provides an -authorization context to your serializer. By default, the context -is the current user of your application, but this -[can be customized](#customizing-scope). - -Serializers provide a method named `filter`, which should return an array -used to determine what attributes and associations should be included in the output. -This is typically used to customize output based on `current_user`. For example: - ```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body, :author - - def filter(keys) - if scope.admin? - keys - else - keys - [:author] - end - end -end + render json: @post, serializer: PostPreviewSerializer ``` -And it's also safe to mutate keys argument by doing keys.delete(:author) -in case you want to avoid creating two extra arrays. Note that if you do an -in-place modification, you still need to return the modified array. +#### 2. For an array resource: -### Alias Attribute -If you would like the key in the outputted JSON to be different from its name -in ActiveRecord, you can declare the attribute with the different name -and redefine that method: - ```ruby -class PostSerializer < ActiveModel::Serializer - # look up subject on the model, but use title in the JSON - def title - object.subject - end +# Use the default `ArraySerializer`, which will use `each_serializer` to +# serialize each element +render json: @posts, each_serializer: PostPreviewSerializer - attributes :id, :body, :title - has_many :comments -end +# Or, you can explicitly provide the collection serializer as well +render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer ``` -If you would like to add meta information to the outputted JSON, use the `:meta` -option: +### Meta +If you want a `meta` attribute in your response, specify it in the `render` +call: + ```ruby -render json: @posts, serializer: CustomArraySerializer, meta: {total: 10} +render json: @post, meta: { total: 10 } ``` -The above usage of `:meta` will produce the following: +The key can be customized using `meta_key` option. -```json -{ - "meta": { "total": 10 }, - "posts": [ - { "title": "Post 1", "body": "Hello!" }, - { "title": "Post 2", "body": "Goodbye!" } - ] -} -``` - -If you would like to change the meta key name you can use the `:meta_key` option: - ```ruby -render json: @posts, serializer: CustomArraySerializer, meta_object: { total: 10 }, meta_key: :meta_object +render json: @post, meta: { total: 10 }, meta_key: "custom_meta" ``` -The above usage of `:meta_key` will produce the following: +`meta` will only be included in your response if there's a root. For instance, +it won't be included in array responses. -```json -{ - "meta_object": { "total": 10 }, - "posts": [ - { "title": "Post 1", "body": "Hello!" }, - { "title": "Post 2", "body": "Goodbye!" } - ] -} -``` +### Root key -When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to -invalid JSON. If you do not have a root key, the meta information will be ignored. +If you want to define a custom root for your response, specify it in the `render` +call: -If you would like direct, low-level control of attribute serialization, you can -completely override the `attributes` method to return the hash you need: - ```ruby -class PersonSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - - def attributes - hash = super - if scope.admin? - hash["ssn"] = object.ssn - hash["secret"] = object.mothers_maiden_name - end - hash - end -end +render json: @post, root: "articles" ``` -## Associations +### Overriding association methods -For specified associations, the serializer will look up the association and -then serialize each element of the association. For instance, a `has_many -:comments` association will create a new `CommentSerializer` for each comment -and use it to serialize the comment. +If you want to override any association, you can use: -By default, serializers simply look up the association on the original object. -You can customize this behavior by implementing a method with the name of the -association and returning a different Array. Often, you will do this to -customize the objects returned based on the current user (scope). - ```ruby class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body + attributes :id, :body + has_many :comments - # only let the user see comments he created. def comments - object.comments.where(created_by: scope) + object.comments.active end end ``` -As with attributes, you can change the JSON key that the serializer should -use for a particular association. +### Overriding attribute methods -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body +If you want to override any attribute, you can use: - # look up comments, but use +my_comments+ as the key in JSON - has_many :comments, root: :my_comments -end -``` - -Also, as with attributes, serializers will execute a filter method to -determine which associations should be included in the output. For -example: - ```ruby class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body + attributes :id, :body + has_many :comments - def filter(keys) - keys.delete :comments if object.comments_disabled? - keys + def body + object.body.downcase end end ``` -Or ... +### Built in Adapters -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body - has_one :author - has_many :comments +#### JSONAPI - def filter(keys) - keys.delete :author unless scope.admin? - keys.delete :comments if object.comments_disabled? - keys - end -end -``` +This adapter follows RC3 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. -You may also use the `:serializer` option to specify a custom serializer class and the `:polymorphic` option to specify an association that is polymorphic (STI), e.g.: - ```ruby - has_many :comments, serializer: CommentShortSerializer - has_one :reviewer, polymorphic: true + render @posts, include: ['authors', 'comments'] + # or + render @posts, include: 'authors,comments' ``` -Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer. +## Installation -## Embedding Associations +Add this line to your application's Gemfile: -By default, associations will be embedded inside the serialized object. So if -you have a post, the outputted JSON will look like: - -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comments": [ - { "id": 1, "body": "what a dumb post" } - ] - } -} ``` - -This is convenient for simple use-cases, but for more complex clients, it is -better to supply an Array of IDs for the association. This makes your API more -flexible from a performance standpoint and avoids wasteful duplication. - -To embed IDs instead of associations, simply use the `embed` class method: - -```ruby -class PostSerializer < ActiveModel::Serializer - embed :ids - - attributes :id, :title, :body - has_many :comments -end +gem 'active_model_serializers' ``` -Now, any associations will be supplied as an Array of IDs: +And then execute: -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comment_ids": [ 1, 2, 3 ] - } -} ``` +$ bundle +``` -You may also choose to embed the IDs by the association's name underneath a -`key` for the resource. For example, say we want to change `comment_ids` -to `comments` underneath a `links` key: +## Creating a Serializer -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body +The easiest way to create a new serializer is to generate a new resource, which +will generate a serializer at the same time: - has_many :comments, embed: :ids, key: :comments, embed_namespace: :links -end ``` - -The JSON will look like this: - -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "links": { - "comments": [ 1, 2, 3 ] - } - } -} +$ rails g resource post title:string body:string ``` -Alternatively, you can choose to embed only the ids or the associated objects per association: +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: -```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body - - has_many :comments, embed: :objects - has_many :tags, embed: :ids -end ``` - -The JSON will look like this: - -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comments": [ - { "id": 1, "body": "what a dumb post" } - ], - "tag_ids": [ 1, 2, 3 ] - } -} +$ rails g serializer post ``` -In addition to supplying an Array of IDs, you may want to side-load the data -alongside the main object. This makes it easier to process the entire package -of data without having to recursively scan the tree looking for embedded -information. It also ensures that associations that are shared between several -objects (like tags), are only delivered once for the entire payload. +The generated seralizer will contain basic `attributes` and +`has_many`/`has_one`/`belongs_to` declarations, based on the model. For example: -You can specify that the data be included like this: - ```ruby class PostSerializer < ActiveModel::Serializer - embed :ids, include: true + attributes :title, :body - attributes :id, :title, :body has_many :comments -end -``` + has_one :author -Assuming that the comments also `has_many :tags`, you will get a JSON like -this: - -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comment_ids": [ 1, 2 ] - }, - "comments": [ - { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] }, - { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] }, - ], - "tags": [ - { "id": 1, "name": "short" }, - { "id": 2, "name": "whiny" }, - { "id": 3, "name": "happy" } - ] -} -``` - -If you would like to namespace association JSON underneath a certain key in -the root document (say, `linked`), you can specify an `embed_in_root_key`: - -```ruby -class PostSerializer < ActiveModel::Serializer - embed :ids, include: true, embed_in_root_key: :linked - - attributes: :id, :title, :body - has_many :comments, :tags + url :post end ``` -The above would yield the following JSON document: +and -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comment_ids": [ 1, 2 ] - }, - "linked": { - "comments": [ - { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] }, - { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] }, - ], - "tags": [ - { "id": 1, "name": "short" }, - { "id": 2, "name": "whiny" }, - { "id": 3, "name": "happy" } - ] - } -} -``` - -When side-loading data, your serializer cannot have the `{ root: false }` option, -as this would lead to invalid JSON. If you do not have a root key, the `include` -instruction will be ignored - -You can also specify a different root for the embedded objects than the key -used to reference them: - ```ruby -class PostSerializer < ActiveModel::Serializer - embed :ids, include: true +class CommentSerializer < ActiveModel::Serializer + attributes :name, :body - attributes :id, :title, :body - has_many :comments, key: :comment_ids, root: :comment_objects + belongs_to :post_id + + url [:post, :comment] end ``` -This would generate JSON that would look like this: +The attribute names are a **whitelist** of attributes to be serialized. -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comment_ids": [ 1 ] - }, - "comment_objects": [ - { "id": 1, "body": "what a dumb post" } - ] -} -``` +The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between +resources. By default, when you serialize a `Post`, you will get its `Comment`s +as well. -You can also specify a different attribute to use rather than the ID of the -objects: +You may also use the `:serializer` option to specify a custom serializer class, for example: ```ruby -class PostSerializer < ActiveModel::Serializer - embed :ids, include: true - - attributes :id, :title, :body - has_many :comments, key: :external_id -end + has_many :comments, serializer: CommentPreviewSerializer ``` -This would generate JSON that would look like this: +The `url` declaration describes which named routes to use while generating URLs +for your JSON. Not every adapter will require URLs. -```json -{ - "post": { - "id": 1, - "title": "New post", - "body": "A body!", - "comment_ids": [ "COMM001" ] - }, - "comments": [ - { "id": 1, "external_id": "COMM001", "body": "what a dumb post" } - ] -} -``` +## Caching -**NOTE**: The `embed :ids` mechanism is primary useful for clients that process -data in bulk and load it into a local store. For these clients, the ability to -easily see all of the data per type, rather than having to recursively scan the -data looking for information, is extremely useful. +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}"```. -If you are mostly working with the data in simple scenarios and manually making -Ajax requests, you probably just want to use the default embedded behavior. +The cache support is optimized to use the cached object in multiple request. An object cached on an ```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.** -## Embedding Polymorphic Associations +**[NOTE] The cache is automatically expired after update an object but it's not deleted.** -Because we need both the id and the type to be able to identify a polymorphic associated model, these are serialized in a slightly different format than common ones. - -When embedding entire objects: - ```ruby -class PostSerializer < ActiveModel::Serializer - attributes :id, :title - has_many :attachments, polymorphic: true -end +cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}``` ``` -```json -{ - "post": { - "id": 1, - "title": "New post", - "attachments": [ - { - "type": "image", - "image": { - "id": 3, - "name": "logo", - "url": "http://images.com/logo.jpg" - } - }, - { - "type": "video", - "video": { - "id": 12, - "uid": "XCSSMDFWW", - "source": "youtube" - } - } - ] - } -} -``` +Take the example bellow: -When embedding ids: - ```ruby class PostSerializer < ActiveModel::Serializer - embed :ids + cache key: 'post', expires_in: 3.hours + attributes :title, :body - attributes :id, :title - has_many :attachments, polymorphic: true -end -``` + has_many :comments -```json -{ - "post": { - "id": 1, - "title": "New post", - "attachment_ids": [ - { - "type": "image", - "id": 12 - }, - { - "type": "video", - "id": 3 - } - ] - } -} -``` - - -## Customizing Scope - -In a serializer, `current_user` is the current authorization scope which the controller -provides to the serializer when you call `render :json`. By default, this is -`current_user`, but can be customized in your controller by calling -`serialization_scope`: - -```ruby -class ApplicationController < ActionController::Base - serialization_scope :current_admin + url :post end ``` -The above example will also change the scope from `current_user` to -`current_admin`. +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. -Please note that, until now, `serialization_scope` doesn't accept a second -object with options for specifying which actions should or should not take a -given scope in consideration. +### Fragmenting Caching -To be clear, it's not possible, yet, to do something like this: +If there is some API endpoint that shouldn't be fully cached, you can still optmise it, using Fragment Cache on the attributes and relationships that you want to cache. -```ruby -class SomeController < ApplicationController - serialization_scope :current_admin, except: [:index, :show] -end -``` +You can define the attribute by using ```only``` or ```except``` option on cache method. -So, in order to have a fine grained control of what each action should take in -consideration for its scope, you may use something like this: +**[NOTE] Cache serializers will be used at their relationships** -```ruby -class CitiesController < ApplicationController - serialization_scope nil +Example: - def index - @cities = City.all - - render json: @cities, each_serializer: CitySerializer - end - - def show - @city = City.find(params[:id]) - - render json: @city, scope: current_admin - end -end -``` - -Assuming that the `current_admin` method needs to make a query in the database -for the current user, the advantage of this approach is that, by setting -`serialization_scope` to `nil`, the `index` action no longer will need to make -that query, only the `show` action will. - -## Testing - -In order to test a Serializer, you can just call `.new` on it, passing the object to serialize: - -### MiniTest - ```ruby -class TestPostSerializer < Minitest::Test - def setup - @serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text') - end - - def test_special_json_for_api - assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json - end -``` - -### RSpec - -```ruby -describe PostSerializer do - it "creates special JSON for the API" do - serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text') - expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}') - end -end -``` - -## Caching - -NOTE: This functionality was removed from AMS and it's in the TODO list. -We need to re-think and re-design the caching strategy for the next -version of AMS. - -To cache a serializer, call `cached` and define a `cache_key` method: - -```ruby class PostSerializer < ActiveModel::Serializer - cached # enables caching for this serializer - + cache key: 'post', expires_in: 3.hours, only: [:title] attributes :title, :body - def cache_key - [object, scope] - end -end -``` + has_many :comments -The caching interface uses `Rails.cache` under the hood. - -# ApplicationSerializer - -By default, new serializers descend from ActiveModel::Serializer. However, if you wish to share behaviour across your serializers you can create an ApplicationSerializer at ```app/serializers/application_serializer.rb```: - -```ruby -class ApplicationSerializer < ActiveModel::Serializer + url :post end ``` -Any newly generated serializers will automatically descend from ApplicationSerializer. +## 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). -now generates: +If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers). -```ruby -class PostSerializer < ApplicationSerializer - attributes :id -end -``` +Thanks! -# Design and Implementation Guidelines +# Contributing -## Keep it Simple - -`ActiveModel::Serializers` is capable of producing complex JSON views/large object -trees, and it may be tempting to design in this way so that your client can make -fewer requests to get data and so that related querying can be optimized. -However, keeping things simple in your serializers and controllers may -significantly reduce complexity and maintenance over the long-term development -of your application. Please consider reducing the complexity of the JSON views -you provide via the serializers as you build out your application, so that -controllers/services can be more easily reused without a lot of complexity -later. - -## Performance - -As you develop your controllers or other code that utilizes serializers, try to -avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you -are using ActiveRecord, you might want to use query includes or joins as needed -to make the data available that the serializer(s) need. +See [CONTRIBUTING.md](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md)