pax_global_header00006660000000000000000000000064131016561650014516gustar00rootroot0000000000000052 comment=2eb57af5619725c16792fedb20ce8e849a7ee838 active_model_serializers-0.9.7/000077500000000000000000000000001310165616500166025ustar00rootroot00000000000000active_model_serializers-0.9.7/.gitignore000066400000000000000000000002661310165616500205760ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc *.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp *.swp .ruby-version vendor/bundle active_model_serializers-0.9.7/.travis.yml000066400000000000000000000021531310165616500207140ustar00rootroot00000000000000language: ruby sudo: false rvm: - 1.9.3 # EOL - 2.0.0 # EOL - 2.1 - ruby-head - jruby-9.1.5.0 # is precompiled per http://rubies.travis-ci.org/ jdk: - oraclejdk8 before_install: - gem update --system - rvm @global do gem uninstall bundler -a -x - rvm @global do gem install bundler -v 1.13.7 install: bundle install --path=vendor/bundle --retry=3 --jobs=3 cache: directories: - vendor/bundle env: global: - "JRUBY_OPTS='--dev -J-Xmx1024M --debug'" matrix: - "RAILS_VERSION=4.0" - "RAILS_VERSION=4.1" - "RAILS_VERSION=4.2" - "RAILS_VERSION=5.0" - "RAILS_VERSION=master" matrix: exclude: - rvm: 1.9.3 env: RAILS_VERSION=master - rvm: 2.0.0 env: RAILS_VERSION=master - rvm: 2.1 env: RAILS_VERSION=master - rvm: jruby-9.1.5.0 env: RAILS_VERSION=master - rvm: 1.9.3 env: RAILS_VERSION=5.0 - rvm: 2.0.0 env: RAILS_VERSION=5.0 - rvm: 2.1 env: RAILS_VERSION=5.0 - rvm: jruby-9.1.5.0 env: RAILS_VERSION=5.0 allow_failures: - rvm: ruby-head - env: "RAILS_VERSION=master" fast_finish: true active_model_serializers-0.9.7/CHANGELOG.md000066400000000000000000000260271310165616500204220ustar00rootroot00000000000000## 0.09.x ### [0-9-stable](https://github.com/rails-api/active_model_serializers/compare/v0.9.7...0-9-stable) ### [v0.9.7 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.9.6...v0.9.7) - [#2080](https://github.com/rails-api/active_model_serializers/pull/2080) remove `{ payload: nil }` from `!serialize.active_model_serializers` ActiveSupport::Notification. `payload` never had a value. Changes, for example `{ serializer: 'ActiveModel::DefaultSerializer', payload: nil }` to be `{ serializer: 'ActiveModel::DefaultSerializer' }` (@yosiat) ### [v0.9.6 (2017-01-10)](https://github.com/rails-api/active_model_serializers/compare/v0.9.5...v0.9.6) - [#2008](https://github.com/rails-api/active_model_serializers/pull/2008) Fix warning on Thor. (@kirs) ### [v0.9.5 (2016-03-30)](https://github.com/rails-api/active_model_serializers/compare/v0.9.4...v0.9.5) - [#1607](https://github.com/rails-api/active_model_serializers/pull/1607) Merge multiple nested associations. (@Hirtenknogger) ### [v0.9.4 (2016-01-05)](https://github.com/rails-api/active_model_serializers/compare/v0.9.3...v0.9.4) - [#752](https://github.com/rails-api/active_model_serializers/pull/752) Tiny improvement of README 0-9-stable (@basiam) - [#749](https://github.com/rails-api/active_model_serializers/pull/749) remove trailing whitespace (@shwoodard) - [#717](https://github.com/rails-api/active_model_serializers/pull/717) fixed issue with rendering Hash which appears in rails 4.2.0.beta4 (@kurko, @greshny) - [#790](https://github.com/rails-api/active_model_serializers/pull/790) pass context to ArraySerializer (@lanej) - [#797](https://github.com/rails-api/active_model_serializers/pull/797) Fix and test for #490 (@afn) - [#813](https://github.com/rails-api/active_model_serializers/pull/813) Allow to define custom serializer for given class (@jtomaszewski) - [#841](https://github.com/rails-api/active_model_serializers/pull/841) Fix issue with embedding multiple associations under the same root key (@antstorm) - [#748](https://github.com/rails-api/active_model_serializers/pull/748) Propagate serialization_options across associations (@raphaelpereira) ### [v0.9.3 (2015/01/21 20:29 +00:00)](https://github.com/rails-api/active_model_serializers/compare/v0.9.2...v0.9.3) Features: - [#774](https://github.com/rails-api/active_model_serializers/pull/774) Fix nested include attributes (@nhocki) - [#771](https://github.com/rails-api/active_model_serializers/pull/771) Make linked resource type names consistent with root names (@sweatypitts) - [#696](https://github.com/rails-api/active_model_serializers/pull/696) Explicitly set serializer for associations (@ggordon) - [#700](https://github.com/rails-api/active_model_serializers/pull/700) sparse fieldsets (@arenoir) - [#768](https://github.com/rails-api/active_model_serializers/pull/768) Adds support for `meta` and `meta_key` attribute (@kurko) ### [v0.9.2](https://github.com/rails-api/active_model_serializers/compare/v0.9.1...v0.9.2) ### [v0.9.1 (2014/12/04 11:54 +00:00)](https://github.com/rails-api/active_model_serializers/compare/v0.9.0...v0.9.1) - [#707](https://github.com/rails-api/active_model_serializers/pull/707) A Friendly Note on Which AMS Version to Use (@jherdman) - [#730](https://github.com/rails-api/active_model_serializers/pull/730) Fixes nested has_many links in JSONAPI (@kurko) - [#718](https://github.com/rails-api/active_model_serializers/pull/718) Allow overriding the adapter with render option (@ggordon) - [#720](https://github.com/rails-api/active_model_serializers/pull/720) Rename attribute with :key (0.8.x compatibility) (@ggordon) - [#728](https://github.com/rails-api/active_model_serializers/pull/728) Use type as key for linked resources (@kurko) - [#729](https://github.com/rails-api/active_model_serializers/pull/729) Use the new beta build env on Travis (@joshk) - [#703](https://github.com/rails-api/active_model_serializers/pull/703) Support serializer and each_serializer options in renderer (@ggordon, @mieko) - [#727](https://github.com/rails-api/active_model_serializers/pull/727) Includes links inside of linked resources (@kurko) - [#726](https://github.com/rails-api/active_model_serializers/pull/726) Bugfix: include nested has_many associations (@kurko) - [#722](https://github.com/rails-api/active_model_serializers/pull/722) Fix infinite recursion (@ggordon) - [#1](https://github.com/rails-api/active_model_serializers/pull/1) Allow for the implicit use of ArraySerializer when :each_serializer is specified (@mieko) - [#692](https://github.com/rails-api/active_model_serializers/pull/692) Include 'linked' member for json-api collections (@ggordon) - [#714](https://github.com/rails-api/active_model_serializers/pull/714) Define as_json instead of to_json (@guilleiguaran) - [#710](https://github.com/rails-api/active_model_serializers/pull/710) JSON-API: Don't include linked section if associations are empty (@guilleiguaran) - [#711](https://github.com/rails-api/active_model_serializers/pull/711) Fixes rbx gems bundling on TravisCI (@kurko) - [#709](https://github.com/rails-api/active_model_serializers/pull/709) Add type key when association name is different than object type (@guilleiguaran) - [#708](https://github.com/rails-api/active_model_serializers/pull/708) Handle correctly null associations (@guilleiguaran) - [#691](https://github.com/rails-api/active_model_serializers/pull/691) Fix embed option for associations (@jacob-s-son) - [#689](https://github.com/rails-api/active_model_serializers/pull/689) Fix support for custom root in JSON-API adapter (@guilleiguaran) - [#685](https://github.com/rails-api/active_model_serializers/pull/685) Serialize ids as strings in JSON-API adapter (@guilleiguaran) - [#684](https://github.com/rails-api/active_model_serializers/pull/684) Refactor adapters to implement support for array serialization (@guilleiguaran) - [#682](https://github.com/rails-api/active_model_serializers/pull/682) Include root by default in JSON-API serializers (@guilleiguaran) - [#625](https://github.com/rails-api/active_model_serializers/pull/625) Add DSL for urls (@JordanFaust) - [#677](https://github.com/rails-api/active_model_serializers/pull/677) Add support for embed: :ids option for in associations (@guilleiguaran) - [#681](https://github.com/rails-api/active_model_serializers/pull/681) Check superclasses for Serializers (@quainjn) - [#680](https://github.com/rails-api/active_model_serializers/pull/680) Add support for root keys (@NullVoxPopuli) - [#675](https://github.com/rails-api/active_model_serializers/pull/675) Support Rails 4.2.0 (@tricknotes) - [#667](https://github.com/rails-api/active_model_serializers/pull/667) Require only activemodel instead of full rails (@guilleiguaran) - [#653](https://github.com/rails-api/active_model_serializers/pull/653) Add "_test" suffix to JsonApi::HasManyTest filename. (@alexgenco) - [#631](https://github.com/rails-api/active_model_serializers/pull/631) Update build badge URL (@craiglittle) ### [v0.9.0](https://github.com/rails-api/active_model_serializers/compare/v0.9.0.alpha1...v0.9.0) ### [0.9.0.alpha1 - January 7, 2014](https://github.com/rails-api/active_model_serializers/compare/d72b66d4c...v0.9.0.alpha1) ### 0.9.0.pre * The following methods were removed - Model#active\_model\_serializer - Serializer#include! - Serializer#include? - Serializer#attr\_disabled= - Serializer#cache - Serializer#perform\_caching - Serializer#schema (needs more discussion) - Serializer#attribute - Serializer#include\_#{name}? (filter method added) - Serializer#attributes (took a hash) * The following things were added - Serializer#filter method - CONFIG object * Remove support for ruby 1.8 versions. * Require rails >= 3.2. * Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces. * Added a "prefix" option in case you want to use a different version of serializer. * Serializers default namespace can be set in `default_serializer_options` and inherited by associations. # VERSION 0.9.0.pre * The following methods were removed - Model#active\_model\_serializer - Serializer#include! - Serializer#include? - Serializer#attr\_disabled= - Serializer#cache - Serializer#perform\_caching - Serializer#schema (needs more discussion) - Serializer#attribute - Serializer#include\_#{name}? (filter method added) - Serializer#attributes (took a hash) * The following things were added - Serializer#filter method - CONFIG object * Remove support for ruby 1.8 versions. * Require rails >= 3.2. * Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces. * Added a "prefix" option in case you want to use a different version of serializer. * Serializers default namespace can be set in `default_serializer_options` and inherited by associations. # VERSION 0.8.1 * Fix bug whereby a serializer using 'options' would blow up. # VERSION 0.8.0 * Attributes can now have optional types. * A new DefaultSerializer ensures that POROs behave the same way as ActiveModels. * If you wish to override ActiveRecord::Base#to\_Json, you can now require 'active\_record/serializer\_override'. We don't recommend you do this, but many users do, so we've left it optional. * Fixed a bug where ActionController wouldn't always have MimeResponds. * An optional caching feature allows you to cache JSON & hashes that AMS uses. Adding 'cached true' to your Serializers will turn on this cache. * URL helpers used inside of Engines now work properly. * Serializers now can filter attributes with `only` and `except`: ``` UserSerializer.new(user, only: [:first_name, :last_name]) UserSerializer.new(user, except: :first_name) ``` * Basic Mongoid support. We now include our mixins in the right place. * On Ruby 1.8, we now generate an `id` method that properly serializes `id` columns. See issue #127 for more. * Add an alias for `scope` method to be the name of the context. By default this is `current_user`. The name is automatically set when using `serialization_scope` in the controller. * Pass through serialization options (such as `:include`) when a model has no serializer defined. # VERSION 0.7.0 * ```embed_key``` option to allow embedding by attributes other than IDs * Fix rendering nil with custom serializer * Fix global ```self.root = false``` * Add support for specifying the serializer for an association as a String * Able to specify keys on the attributes method * Serializer Reloading via ActiveSupport::DescendantsTracker * Reduce double map to once; Fixes datamapper eager loading. # VERSION 0.6.0 * Serialize sets properly * Add root option to ArraySerializer * Support polymorphic associations * Support :each_serializer in ArraySerializer * Add `scope` method to easily access the scope in the serializer * Fix regression with Rails 3.2.6; add Rails 4 support * Allow serialization_scope to be disabled with serialization_scope nil * Array serializer should support pure ruby objects besides serializers # VERSION 0.5.0 * First tagged version * Changes generators to always generate an ApplicationSerializer active_model_serializers-0.9.7/CONTRIBUTING.md000066400000000000000000000011231310165616500210300ustar00rootroot00000000000000Contributing to AMS =================== First of all, **thank you**! Now, for the details: Please file issues on the [GitHub Issues list](https://github.com/rails-api/active_model_serializers/issues). Please discuss new features or ask for feedback about a new feature [on rails-api-core](https://groups.google.com/forum/#!forum/rails-api-core). If you want a feature implemented, the best way to get it done is to submit a pull request that implements it. Tests and docs would be nice. Please include a CHANGELOG with all entries that change behavior. :heart: :sparkling_heart: :heart: active_model_serializers-0.9.7/DESIGN.textile000066400000000000000000000461031310165616500211570ustar00rootroot00000000000000This was the original design document for serializers. It is useful mostly for historical purposes as the public API has changed. h2. Rails Serializers This guide describes how to use Active Model serializers to build non-trivial JSON services in Rails. By reading this guide, you will learn: * When to use the built-in Active Model serialization * When to use a custom serializer for your models * How to use serializers to encapsulate authorization concerns * How to create serializer templates to describe the application-wide structure of your serialized JSON * How to build resources not backed by a single database table for use with JSON services This guide covers an intermediate topic and assumes familiarity with Rails conventions. It is suitable for applications that expose a JSON API that may return different results based on the authorization status of the user. h3. Serialization By default, Active Record objects can serialize themselves into JSON by using the `to_json` method. This method takes a series of additional parameter to control which properties and associations Rails should include in the serialized output. When building a web application that uses JavaScript to retrieve JSON data from the server, this mechanism has historically been the primary way that Rails developers prepared their responses. This works great for simple cases, as the logic for serializing an Active Record object is neatly encapsulated in Active Record itself. However, this solution quickly falls apart in the face of serialization requirements based on authorization. For instance, a web service may choose to expose additional information about a resource only if the user is entitled to access it. In addition, a JavaScript front-end may want information that is not neatly described in terms of serializing a single Active Record object, or in a different format than. In addition, neither the controller nor the model seems like the correct place for logic that describes how to serialize an model object *for the current user*. Serializers solve these problems by encapsulating serialization in an object designed for this purpose. If the default +to_json+ semantics, with at most a few configuration options serve your needs, by all means continue to use the built-in +to_json+. If you find yourself doing hash-driven-development in your controllers, juggling authorization logic and other concerns, serializers are for you! h3. The Most Basic Serializer A basic serializer is a simple Ruby object named after the model class it is serializing.
class PostSerializer
  def initialize(post, scope)
    @post, @scope = post, scope
  end

  def as_json
    { post: { title: @post.name, body: @post.body } }
  end
end
A serializer is initialized with two parameters: the model object it should serialize and an authorization scope. By default, the authorization scope is the current user (+current_user+) but you can use a different object if you want. The serializer also implements an +as_json+ method, which returns a Hash that will be sent to the JSON encoder. Rails will transparently use your serializer when you use +render :json+ in your controller.
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render json: @post
  end
end
Because +respond_with+ uses +render :json+ under the hood for JSON requests, Rails will automatically use your serializer when you use +respond_with+ as well. h4. +serializable_hash+ In general, you will want to implement +serializable_hash+ and +as_json+ to allow serializers to embed associated content directly. The easiest way to implement these two methods is to have +as_json+ call +serializable_hash+ and insert the root.
class PostSerializer
  def initialize(post, scope)
    @post, @scope = post, scope
  end

  def serializable_hash
    { title: @post.name, body: @post.body }
  end

  def as_json
    { post: serializable_hash }
  end
end
h4. Authorization Let's update our serializer to include the email address of the author of the post, but only if the current user has superuser access.
class PostSerializer
  def initialize(post, scope)
    @post, @scope = post, scope
  end

  def as_json
    { post: serializable_hash }
  end

  def serializable_hash
    hash = post
    hash.merge!(super_data) if super?
    hash
  end

private
  def post
    { title: @post.name, body: @post.body }
  end

  def super_data
    { email: @post.email }
  end

  def super?
    @scope.superuser?
  end
end
h4. Testing One benefit of encapsulating our objects this way is that it becomes extremely straight-forward to test the serialization logic in isolation.
require "ostruct"

class PostSerializerTest < ActiveSupport::TestCase
  # For now, we use a very simple authorization structure. These tests will need
  # refactoring if we change that.
  plebe = OpenStruct.new(super?: false)
  god   = OpenStruct.new(super?: true)

  post  = OpenStruct.new(title: "Welcome to my blog!", body: "Blah blah blah", email: "tenderlove@gmail.com")

  test "a regular user sees just the title and body" do
    json = PostSerializer.new(post, plebe).to_json
    hash = JSON.parse(json)

    assert_equal post.title, hash.delete("title")
    assert_equal post.body, hash.delete("body")
    assert_empty hash
  end

  test "a superuser sees the title, body and email" do
    json = PostSerializer.new(post, god).to_json
    hash = JSON.parse(json)

    assert_equal post.title, hash.delete("title")
    assert_equal post.body, hash.delete("body")
    assert_equal post.email, hash.delete("email")
    assert_empty hash
  end
end
It's important to note that serializer objects define a clear interface specifically for serializing an existing object. In this case, the serializer expects to receive a post object with +name+, +body+ and +email+ attributes and an authorization scope with a +super?+ method. By defining a clear interface, it's must easier to ensure that your authorization logic is behaving correctly. In this case, the serializer doesn't need to concern itself with how the authorization scope decides whether to set the +super?+ flag, just whether it is set. In general, you should document these requirements in your serializer files and programatically via tests. The documentation library +YARD+ provides excellent tools for describing this kind of requirement:
class PostSerializer
  # @param [~body, ~title, ~email] post the post to serialize
  # @param [~super] scope the authorization scope for this serializer
  def initialize(post, scope)
    @post, @scope = post, scope
  end

  # ...
end
h3. Attribute Sugar To simplify this process for a number of common cases, Rails provides a default superclass named +ActiveModel::Serializer+ that you can use to implement your serializers. For example, you will sometimes want to simply include a number of existing attributes from the source model into the outputted JSON. In the above example, the +title+ and +body+ attributes were always included in the JSON. Let's see how to use +ActiveModel::Serializer+ to simplify our post serializer.
class PostSerializer < ActiveModel::Serializer
  attributes :title, :body

  def serializable_hash
    hash = attributes
    hash.merge!(super_data) if super?
    hash
  end

private
  def super_data
    { email: @post.email }
  end

  def super?
    @scope.superuser?
  end
end
First, we specified the list of included attributes at the top of the class. This will create an instance method called +attributes+ that extracts those attributes from the post model. NOTE: Internally, +ActiveModel::Serializer+ uses +read_attribute_for_serialization+, which defaults to +read_attribute+, which defaults to +send+. So if you're rolling your own models for use with the serializer, you can use simple Ruby accessors for your attributes if you like. Next, we use the attributes method in our +serializable_hash+ method, which allowed us to eliminate the +post+ method we hand-rolled earlier. We could also eliminate the +as_json+ method, as +ActiveModel::Serializer+ provides a default +as_json+ method for us that calls our +serializable_hash+ method and inserts a root. But we can go a step further!
class PostSerializer < ActiveModel::Serializer
  attributes :title, :body

private
  def attributes
    hash = super
    hash.merge!(email: post.email) if super?
    hash
  end

  def super?
    @scope.superuser?
  end
end
The superclass provides a default +initialize+ method as well as a default +serializable_hash+ method, which uses +attributes+. We can call +super+ to get the hash based on the attributes we declared, and then add in any additional attributes we want to use. NOTE: +ActiveModel::Serializer+ will create an accessor matching the name of the current class for the resource you pass in. In this case, because we have defined a PostSerializer, we can access the resource with the +post+ accessor. h3. Associations In most JSON APIs, you will want to include associated objects with your serialized object. In this case, let's include the comments with the current post.
class PostSerializer < ActiveModel::Serializer
  attributes :title, :body
  has_many :comments

private
  def attributes
    hash = super
    hash.merge!(email: post.email) if super?
    hash
  end

  def super?
    @scope.superuser?
  end
end
The default +serializable_hash+ method will include the comments as embedded objects inside the post.
{
  post: {
    title: "Hello Blog!",
    body: "This is my first post. Isn't it fabulous!",
    comments: [
      {
        title: "Awesome",
        body: "Your first post is great"
      }
    ]
  }
}
Rails uses the same logic to generate embedded serializations as it does when you use +render :json+. In this case, because you didn't define a +CommentSerializer+, Rails used the default +as_json+ on your comment object. If you define a serializer, Rails will automatically instantiate it with the existing authorization scope.
class CommentSerializer
  def initialize(comment, scope)
    @comment, @scope = comment, scope
  end

  def serializable_hash
    { title: @comment.title }
  end

  def as_json
    { comment: serializable_hash }
  end
end
If we define the above comment serializer, the outputted JSON will change to:
{
  post: {
    title: "Hello Blog!",
    body: "This is my first post. Isn't it fabulous!",
    comments: [{ title: "Awesome" }]
  }
}
Let's imagine that our comment system allows an administrator to kill a comment, and we only want to allow users to see the comments they're entitled to see. By default, +has_many :comments+ will simply use the +comments+ accessor on the post object. We can override the +comments+ accessor to limit the comments used to just the comments we want to allow for the current user.
class PostSerializer < ActiveModel::Serializer
  attributes :title. :body
  has_many :comments

private
  def attributes
    hash = super
    hash.merge!(email: post.email) if super?
    hash
  end

  def comments
    post.comments_for(scope)
  end

  def super?
    @scope.superuser?
  end
end
+ActiveModel::Serializer+ will still embed the comments, but this time it will use just the comments for the current user. NOTE: The logic for deciding which comments a user should see still belongs in the model layer. In general, you should encapsulate concerns that require making direct Active Record queries in scopes or public methods on your models. h4. Modifying Associations You can also rename associations if required. Say for example you have an association that makes sense to be named one thing in your code, but another when data is serialized. You can use the option to specify a different name for an association. Here is an example:
class UserSerializer < ActiveModel::Serializer
  has_many :followed_posts, key: :posts
  has_one :owned_account, key: :account
end
Using the :key without a :serializer option will use implicit detection to determine a serializer. In this example, you'd have to define two classes: PostSerializer and AccountSerializer. You can also add the :serializer option to set it explicitly:
class UserSerializer < ActiveModel::Serializer
  has_many :followed_posts, key: :posts, serializer: CustomPostSerializer
  has_one :owne_account, key: :account, serializer: PrivateAccountSerializer
end
h3. Customizing Associations Not all front-ends expect embedded documents in the same form. In these cases, you can override the default +serializable_hash+, and use conveniences provided by +ActiveModel::Serializer+ to avoid having to build up the hash manually. For example, let's say our front-end expects the posts and comments in the following format:
{
  post: {
    id: 1
    title: "Hello Blog!",
    body: "This is my first post. Isn't it fabulous!",
    comments: [1,2]
  },
  comments: [
    {
      id: 1
      title: "Awesome",
      body: "Your first post is great"
    },
    {
      id: 2
      title: "Not so awesome",
      body: "Why is it so short!"
    }
  ]
}
We could achieve this with a custom +as_json+ method. We will also need to define a serializer for comments.
class CommentSerializer < ActiveModel::Serializer
  attributes :id, :title, :body

  # define any logic for dealing with authorization-based attributes here
end

class PostSerializer < ActiveModel::Serializer
  attributes :title, :body
  has_many :comments

  def as_json
    { post: serializable_hash }.merge!(associations)
  end

  def serializable_hash
    post_hash = attributes
    post_hash.merge!(association_ids)
    post_hash
  end

private
  def attributes
    hash = super
    hash.merge!(email: post.email) if super?
    hash
  end

  def comments
    post.comments_for(scope)
  end

  def super?
    @scope.superuser?
  end
end
Here, we used two convenience methods: +associations+ and +association_ids+. The first, +associations+, creates a hash of all of the define associations, using their defined serializers. The second, +association_ids+, generates a hash whose key is the association name and whose value is an Array of the association's keys. The +association_ids+ helper will use the overridden version of the association, so in this case, +association_ids+ will only include the ids of the comments provided by the +comments+ method. h3. Special Association Serializers So far, associations defined in serializers use either the +as_json+ method on the model or the defined serializer for the association type. Sometimes, you may want to serialize associated models differently when they are requested as part of another resource than when they are requested on their own. For instance, we might want to provide the full comment when it is requested directly, but only its title when requested as part of the post. To achieve this, you can define a serializer for associated objects nested inside the main serializer.
class PostSerializer < ActiveModel::Serializer
  class CommentSerializer < ActiveModel::Serializer
    attributes :id, :title
  end

  # same as before
  # ...
end
In other words, if a +PostSerializer+ is trying to serialize comments, it will first look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+ and finally +comment.as_json+. h3. Overriding the Defaults h4. Authorization Scope By default, the authorization scope for serializers is +:current_user+. This means that when you call +render json: @post+, the controller will automatically call its +current_user+ method and pass that along to the serializer's initializer. If you want to change that behavior, simply use the +serialization_scope+ class method.
class PostsController < ApplicationController
  serialization_scope :current_app
end
You can also implement an instance method called (no surprise) +serialization_scope+, which allows you to define a dynamic authorization scope based on the current request. WARNING: If you use different objects as authorization scopes, make sure that they all implement whatever interface you use in your serializers to control what the outputted JSON looks like. h3. Using Serializers Outside of a Request The serialization API encapsulates the concern of generating a JSON representation of a particular model for a particular user. As a result, you should be able to easily use serializers, whether you define them yourself or whether you use +ActiveModel::Serializer+ outside a request. For instance, if you want to generate the JSON representation of a post for a user outside of a request:
user = get_user # some logic to get the user in question
PostSerializer.new(post, user).to_json # reliably generate JSON output
If you want to generate JSON for an anonymous user, you should be able to use whatever technique you use in your application to generate anonymous users outside of a request. Typically, that means creating a new user and not saving it to the database:
user = User.new # create a new anonymous user
PostSerializer.new(post, user).to_json
In general, the better you encapsulate your authorization logic, the more easily you will be able to use the serializer outside of the context of a request. For instance, if you use an authorization library like Cancan, which uses a uniform +user.can?(action, model)+, the authorization interface can very easily be replaced by a plain Ruby object for testing or usage outside the context of a request. h3. Collections So far, we've talked about serializing individual model objects. By default, Rails will serialize collections, including when using the +associations+ helper, by looping over each element of the collection, calling +serializable_hash+ on the element, and then grouping them by their type (using the plural version of their class name as the root). For example, an Array of post objects would serialize as:
{
  posts: [
    {
      title: "FIRST POST!",
      body: "It's my first pooooost"
    },
    { title: "Second post!",
      body: "Zomg I made it to my second post"
    }
  ]
}
If you want to change the behavior of serialized Arrays, you need to create a custom Array serializer.
class ArraySerializer < ActiveModel::ArraySerializer
  def serializable_array
    serializers.map do |serializer|
      serializer.serializable_hash
    end
  end

  def as_json
    hash = { root => serializable_array }
    hash.merge!(associations)
    hash
  end
end
When generating embedded associations using the +associations+ helper inside a regular serializer, it will create a new ArraySerializer with the associated content and call its +serializable_array+ method. In this case, those embedded associations will not recursively include associations. When generating an Array using +render json: posts+, the controller will invoke the +as_json+ method, which will include its associations and its root. active_model_serializers-0.9.7/Gemfile000066400000000000000000000031221310165616500200730ustar00rootroot00000000000000source 'https://rubygems.org' gemspec version = ENV["RAILS_VERSION"] || "4.2" if version == 'master' gem 'rack', github: 'rack/rack' gem 'arel', github: 'rails/arel' gem 'rails', github: 'rails/rails' git 'https://github.com/rails/rails.git' do gem 'railties' gem 'activesupport' gem 'activemodel' gem 'actionpack' gem 'activerecord', group: :test # Rails 5 gem 'actionview' end # Rails 5 gem 'rails-controller-testing', github: 'rails/rails-controller-testing' else gem_version = "~> #{version}.0" gem 'rails', gem_version gem 'railties', gem_version gem 'activesupport', gem_version gem 'activemodel', gem_version gem 'actionpack', gem_version gem 'activerecord', gem_version, group: :test end if RUBY_VERSION < '2' gem 'mime-types', [ '>= 2.6.2', '< 3' ] end if RUBY_VERSION < '2.1' gem 'nokogiri', '< 1.7' end # https://github.com/bundler/bundler/blob/89a8778c19269561926cea172acdcda241d26d23/lib/bundler/dependency.rb#L30-L54 @windows_platforms = [:mswin, :mingw, :x64_mingw] # Windows does not include zoneinfo files, so bundle the tzinfo-data gem tzinfo_platforms = @windows_platforms tzinfo_platforms += [:jruby] if version >= '4.1' gem 'tzinfo-data', platforms: tzinfo_platforms group :bench do gem 'benchmark-ips', '>= 2.7.2' end group :test do gem 'sqlite3', platform: (@windows_platforms + [:ruby]) gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby gem 'simplecov', '~> 0.10', require: false, group: :development end group :development, :test do gem 'rubocop', '~> 0.34.0', require: false end active_model_serializers-0.9.7/MIT-LICENSE000066400000000000000000000020631310165616500202370ustar00rootroot00000000000000Copyright (c) 2011-2012 José Valim & Yehuda Katz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. active_model_serializers-0.9.7/README.md000066400000000000000000000567421310165616500200770ustar00rootroot00000000000000[![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 `ActiveModel::Serializers` encapsulates the JSON serialization of objects. Objects that respond to read\_attribute\_for\_serialization (including `ActiveModel` and `ActiveRecord` objects) are supported. 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. In short, **serializers replace hash-driven development with object-oriented development.** # Installing The easiest way to install `ActiveModel::Serializers` is to add it to your `Gemfile`: ```ruby gem "active_model_serializers" ``` Then, install it on the command line: ``` $ 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 `@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 attributes :title, :body end class PostsController < ApplicationController def index @posts = Post.all render json: @posts end end ``` 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 end ``` You can also render an array of objects using ArraySerializer. ```ruby def users_array_as_json_helper(users) ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json end ``` ## 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 end # controller: render json: @posts, serializer: CustomArraySerializer ``` #### 4. Define default_serializer_options in your controller 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 ``` ## Changing the Key Format 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) ``` ## Changing the default association key type You can specify that serializers use unsuffixed names as association keys by default. ```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 has_many :comments end ``` ## Attributes 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 def full_name "#{object.first_name} #{object.last_name}" end end ``` Within a serializer's methods, you can access the object being serialized as `object`. Since this shadows any attribute named `object`, you can include them through `object.object`. For example: ```ruby class VersionSerializer < ActiveModel::Serializer attributes :version_object def version_object object.object end end ``` 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 ``` 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. ### 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 attributes :id, :body, :title has_many :comments end ``` If you would like to add meta information to the outputted JSON, use the `:meta` option: ```ruby render json: @posts, serializer: CustomArraySerializer, meta: {total: 10} ``` The above usage of `:meta` will produce the following: ```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 ``` The above usage of `:meta_key` will produce the following: ```json { "meta_object": { "total": 10 }, "posts": [ { "title": "Post 1", "body": "Hello!" }, { "title": "Post 2", "body": "Goodbye!" } ] } ``` 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 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 ``` ## Associations 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. 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 has_many :comments # only let the user see comments he created. def comments object.comments.where(created_by: scope) end end ``` As with attributes, you can change the JSON key that the serializer should use for a particular association. ```ruby class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body # 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 has_many :comments def filter(keys) keys.delete :comments if object.comments_disabled? keys end end ``` Or ... ```ruby class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body has_one :author has_many :comments def filter(keys) keys.delete :author unless scope.admin? keys.delete :comments if object.comments_disabled? keys end end ``` 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 ``` Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer. ## Embedding Associations 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 ``` Now, any associations will be supplied as an Array of IDs: ```json { "post": { "id": 1, "title": "New post", "body": "A body!", "comment_ids": [ 1, 2, 3 ] } } ``` 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: ```ruby class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body 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 ] } } } ``` Alternatively, you can choose to embed only the ids or the associated objects per association: ```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 ] } } ``` 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. You can specify that the data be included like this: ```ruby class PostSerializer < ActiveModel::Serializer embed :ids, include: true attributes :id, :title, :body has_many :comments end ``` 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 end ``` The above would yield the following JSON document: ```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 attributes :id, :title, :body has_many :comments, key: :comment_ids, root: :comment_objects end ``` This would generate JSON that would look like this: ```json { "post": { "id": 1, "title": "New post", "body": "A body!", "comment_ids": [ 1 ] }, "comment_objects": [ { "id": 1, "body": "what a dumb post" } ] } ``` You can also specify a different attribute to use rather than the ID of the objects: ```ruby class PostSerializer < ActiveModel::Serializer embed :ids, include: true attributes :id, :title, :body has_many :comments, key: :external_id end ``` This would generate JSON that would look like this: ```json { "post": { "id": 1, "title": "New post", "body": "A body!", "comment_ids": [ "COMM001" ] }, "comments": [ { "id": 1, "external_id": "COMM001", "body": "what a dumb post" } ] } ``` **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. 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. ## Embedding Polymorphic Associations 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 ``` ```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" } } ] } } ``` When embedding ids: ```ruby class PostSerializer < ActiveModel::Serializer embed :ids attributes :id, :title has_many :attachments, polymorphic: true end ``` ```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 end ``` The above example will also change the scope from `current_user` to `current_admin`. 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. To be clear, it's not possible, yet, to do something like this: ```ruby class SomeController < ApplicationController serialization_scope :current_admin, except: [:index, :show] end ``` 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: ```ruby class CitiesController < ApplicationController serialization_scope nil 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 attributes :title, :body def cache_key [object, scope] end end ``` 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 end ``` Any newly generated serializers will automatically descend from ApplicationSerializer. ``` $ rails g serializer post ``` now generates: ```ruby class PostSerializer < ApplicationSerializer attributes :id end ``` # Design and Implementation Guidelines ## 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. active_model_serializers-0.9.7/Rakefile000066400000000000000000000007441310165616500202540ustar00rootroot00000000000000#!/usr/bin/env rake require "bundler/gem_tasks" require "rake/testtask" desc 'Run tests' test_task = Rake::TestTask.new(:test) do |t| t.libs << 'test' t.pattern = 'test/**/*_test.rb' t.verbose = true end task default: :test desc 'Run tests in isolated processes' namespace :test do task :isolated do Dir[test_task.pattern].each do |file| cmd = ['ruby'] test_task.libs.each { |l| cmd << '-I' << l } cmd << file sh cmd.join(' ') end end end active_model_serializers-0.9.7/active_model_serializers.gemspec000066400000000000000000000021651310165616500252220ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $:.unshift File.expand_path("../lib", __FILE__) require "active_model/serializer/version" Gem::Specification.new do |gem| gem.authors = ["José Valim", "Yehuda Katz", "Santiago Pastorino"] gem.email = ["jose.valim@gmail.com", "wycats@gmail.com", "santiago@wyeworks.com"] gem.description = %q{Making it easy to serialize models for client-side use} gem.summary = %q{Bringing consistency and object orientation to model serialization. Works great for client-side MVC frameworks!} gem.homepage = "https://github.com/rails-api/active_model_serializers" gem.license = 'MIT' gem.files = Dir['README.md', 'CHANGELOG.md', 'CONTRIBUTING.md', 'DESIGN.textile', 'MIT-LICENSE', 'lib/**/*', 'test/**/*'] gem.test_files = Dir['test/**/*'] gem.name = "active_model_serializers" gem.require_paths = ["lib"] gem.version = ActiveModel::Serializer::VERSION gem.required_ruby_version = ">= 1.9.3" gem.add_dependency "activemodel", ">= 3.2" gem.add_dependency "concurrent-ruby", "~> 1.0" gem.add_development_dependency "rails", ">= 3.2" end active_model_serializers-0.9.7/appveyor.yml000066400000000000000000000007551310165616500212010ustar00rootroot00000000000000version: '{build}' skip_tags: true environment: matrix: - ruby_version: "200" - ruby_version: "200-x64" - ruby_version: "21" - ruby_version: "21-x64" cache: - vendor/bundle install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - gem install bundler - bundler --version - bundle platform - bundle install --path=vendor/bundle --retry=3 --jobs=3 test_script: - bundle exec rake test build: off active_model_serializers-0.9.7/bin/000077500000000000000000000000001310165616500173525ustar00rootroot00000000000000active_model_serializers-0.9.7/bin/bench000077500000000000000000000117741310165616500203710ustar00rootroot00000000000000#!/usr/bin/env ruby # ActiveModelSerializers Benchmark driver # Adapted from # https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/driver.rb require 'bundler' Bundler.setup require 'json' require 'pathname' require 'optparse' require 'digest' require 'pathname' require 'shellwords' require 'logger' require 'English' class BenchmarkDriver ROOT = Pathname File.expand_path(File.join('..', '..'), __FILE__) BASE = ENV.fetch('BASE') { ROOT.join('test', 'benchmark') } ESCAPED_BASE = Shellwords.shellescape(BASE) def self.benchmark(options) new(options).run end def self.parse_argv_and_run(argv = ARGV, options = {}) options = { repeat_count: 1, pattern: [], env: 'CACHE_ON=on' }.merge!(options) OptionParser.new do |opts| opts.banner = 'Usage: bin/bench [options]' opts.on('-r', '--repeat-count [NUM]', 'Run benchmarks [NUM] times taking the best result') do |value| options[:repeat_count] = value.to_i end opts.on('-p', '--pattern ', 'Benchmark name pattern') do |value| options[:pattern] = value.split(',') end opts.on('-e', '--env ', 'ENV variables to pass in') do |value| options[:env] = value.split(',') end end.parse!(argv) benchmark(options) end attr_reader :commit_hash, :base # Based on logfmt: # https://www.brandur.org/logfmt # For more complete implementation see: # see https://github.com/arachnid-cb/logfmtr/blob/master/lib/logfmtr/base.rb # For usage see: # https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write/ # https://engineering.heroku.com/blogs/2014-09-05-hutils-explore-your-structured-data-logs/ # For Ruby parser see: # https://github.com/cyberdelia/logfmt-ruby def self.summary_logger(device = 'output.txt') require 'time' logger = Logger.new(device) logger.level = Logger::INFO logger.formatter = proc { |severity, datetime, progname, msg| msg = "'#{msg}'" "level=#{severity} time=#{datetime.utc.iso8601(6)} pid=#{Process.pid} progname=#{progname} msg=#{msg}#{$INPUT_RECORD_SEPARATOR}" } logger end def self.stdout_logger logger = Logger.new(STDOUT) logger.level = Logger::INFO logger.formatter = proc { |_, _, _, msg| "#{msg}#{$INPUT_RECORD_SEPARATOR}" } logger end def initialize(options) @writer = ENV['SUMMARIZE'] ? self.class.summary_logger : self.class.stdout_logger @repeat_count = options[:repeat_count] @pattern = options[:pattern] @commit_hash = options.fetch(:commit_hash) { `git rev-parse --short HEAD`.chomp } @base = options.fetch(:base) { ESCAPED_BASE } @env = Array(options[:env]).join(' ') @rubyopt = options[:rubyopt] # TODO: rename end def run files.each do |path| next if !@pattern.empty? && /#{@pattern.join('|')}/ !~ File.basename(path) run_single(Shellwords.shellescape(path)) end end private def files Dir[File.join(base, 'bm_*')] end def run_single(path) script = "RAILS_ENV=production #{@env} ruby #{@rubyopt} #{path}" environment = `ruby -v`.chomp.strip[/\d+\.\d+\.\d+\w+/] runs_output = measure(script) if runs_output.empty? results = { error: :no_results } return end results = {} results['commit_hash'] = commit_hash results['version'] = runs_output.first['version'] results['rails_version'] = runs_output.first['rails_version'] results['benchmark_run[environment]'] = environment results['runs'] = [] runs_output.each do |output| results['runs'] << { 'benchmark_type[category]' => output['label'], 'benchmark_run[result][iterations_per_second]' => output['iterations_per_second'].round(3), 'benchmark_run[result][total_allocated_objects_per_iteration]' => output['total_allocated_objects_per_iteration'] } end ensure results && report(results) end def report(results) @writer.info { 'Benchmark results:' } @writer.info { JSON.pretty_generate(results) } end def summarize(result) puts "#{result['label']} #{result['iterations_per_second']}/ips; #{result['total_allocated_objects_per_iteration']} objects" end # FIXME: ` provides the full output but it'll return failed output as well. def measure(script) results = Hash.new { |h, k| h[k] = [] } @repeat_count.times do output = sh(script) output.each_line do |line| next if line.nil? begin result = JSON.parse(line) rescue JSON::ParserError result = { error: line } # rubocop:disable Lint/UselessAssignment else summarize(result) results[result['label']] << result end end end results.map do |_, bm_runs| bm_runs.sort_by do |run| run['iterations_per_second'] end.last end end def sh(cmd) `#{cmd}` end end BenchmarkDriver.parse_argv_and_run if $PROGRAM_NAME == __FILE__ active_model_serializers-0.9.7/lib/000077500000000000000000000000001310165616500173505ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/action_controller/000077500000000000000000000000001310165616500230705ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/action_controller/serialization.rb000066400000000000000000000055061310165616500263000ustar00rootroot00000000000000require 'active_support/core_ext/class/attribute' module ActionController # Action Controller Serialization # # Overrides render :json to check if the given object implements +active_model_serializer+ # as a method. If so, use the returned serializer instead of calling +to_json+ on the object. # # This module also provides a serialization_scope method that allows you to configure the # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+ # to the current user: # # class ApplicationController < ActionController::Base # serialization_scope :current_user # end # # If you need more complex scope rules, you can simply override the serialization_scope: # # class ApplicationController < ActionController::Base # private # # def serialization_scope # current_user # end # end # module Serialization extend ActiveSupport::Concern include ActionController::Renderers class << self attr_accessor :enabled end self.enabled = true included do class_attribute :_serialization_scope self._serialization_scope = :current_user end module ClassMethods def serialization_scope(scope) self._serialization_scope = scope end end [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| define_method renderer_method do |resource, options| serializer = build_json_serializer(resource, options) if serializer super(serializer, options) else super(resource, options) end end end private def namespace_for_serializer @namespace_for_serializer ||= self.class.parent unless self.class.parent == Object end def default_serializer(resource) options = {}.tap do |o| o[:namespace] = namespace_for_serializer if namespace_for_serializer end ActiveModel::Serializer.serializer_for(resource, options) end def default_serializer_options {} end def serialization_scope _serialization_scope = self.class._serialization_scope send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope, true) end def build_json_serializer(resource, options = {}) options = default_serializer_options.merge(options) @namespace_for_serializer = options.fetch(:namespace, nil) if serializer = options.fetch(:serializer, default_serializer(resource)) options[:scope] = serialization_scope unless options.has_key?(:scope) if resource.respond_to?(:to_ary) options[:resource_name] = controller_name options[:namespace] = namespace_for_serializer if namespace_for_serializer end serializer.new(resource, options) end end end end active_model_serializers-0.9.7/lib/action_controller/serialization_test_case.rb000066400000000000000000000055711310165616500303340ustar00rootroot00000000000000module ActionController module SerializationAssertions extend ActiveSupport::Concern included do setup :setup_serialization_subscriptions teardown :teardown_serialization_subscriptions end def setup_serialization_subscriptions @serializers = Hash.new(0) ActiveSupport::Notifications.subscribe("!serialize.active_model_serializers") do |name, start, finish, id, payload| serializer = payload[:serializer] @serializers[serializer] += 1 end end def teardown_serialization_subscriptions ActiveSupport::Notifications.unsubscribe("!serialize.active_model_serializers") end def process(*args) @serializers = Hash.new(0) super end # Asserts that the request was rendered with the appropriate serializers. # # # assert that the "PostSerializer" serializer was rendered # assert_serializer "PostSerializer" # # # assert that the instance of PostSerializer was rendered # assert_serializer PostSerializer # # # assert that the "PostSerializer" serializer was rendered # assert_serializer :post_serializer # # # assert that the rendered serializer starts with "Post" # assert_serializer %r{\APost.+\Z} # # # assert that no serializer was rendered # assert_serializer nil # # def assert_serializer(options = {}, message = nil) # Force body to be read in case the template is being streamed. response.body rendered = @serializers msg = message || "expecting <#{options.inspect}> but rendering with <#{rendered.keys}>" matches_serializer = case options when lambda { |options| options.kind_of?(Class) && options < ActiveModel::Serializer } rendered.any? do |serializer, count| options.name == serializer end when Symbol options = options.to_s.camelize rendered.any? do |serializer, count| serializer == options end when String !options.empty? && rendered.any? do |serializer, count| serializer == options end when Regexp rendered.any? do |serializer, count| serializer.match(options) end when NilClass rendered.blank? else raise ArgumentError, "assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil" end assert matches_serializer, msg end end end active_model_serializers-0.9.7/lib/active_model/000077500000000000000000000000001310165616500220035ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/array_serializer.rb000066400000000000000000000045171310165616500257060ustar00rootroot00000000000000require 'active_model/default_serializer' require 'active_model/serializable' module ActiveModel class ArraySerializer include Serializable class << self attr_accessor :_root alias root _root= alias root= _root= end def initialize(object, options={}) @object = object @scope = options[:scope] @root = options.fetch(:root, self.class._root) @polymorphic = options.fetch(:polymorphic, false) @meta_key = options[:meta_key] || :meta @meta = options[@meta_key] @each_serializer = options[:each_serializer] @resource_name = options[:resource_name] @only = options[:only] ? Array(options[:only]) : nil @except = options[:except] ? Array(options[:except]) : nil @context = options[:context] @namespace = options[:namespace] @key_format = options[:key_format] || options[:each_serializer].try(:key_format) end attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context def json_key key = root.nil? ? @resource_name : root key_format == :lower_camel && key.present? ? key.camelize(:lower) : key end def serializer_for(item) serializer_class = @each_serializer || Serializer.serializer_for(item, namespace: @namespace) || DefaultSerializer serializer_class.new(item, scope: scope, key_format: key_format, context: @context, only: @only, except: @except, polymorphic: @polymorphic, namespace: @namespace) end def serializable_object(options={}) @object.map do |item| serializer_for(item).serializable_object_with_notification(options) end end alias_method :serializable_array, :serializable_object def embedded_in_root_associations @object.each_with_object({}) do |item, hash| serializer_for(item).embedded_in_root_associations.each_pair do |type, objects| next if !objects || objects.flatten.empty? if hash.has_key?(type) case hash[type] when Hash hash[type].deep_merge!(objects){ |key, old, new| (Array(old) + Array(new)).uniq } else hash[type].concat(objects).uniq! end else hash[type] = objects end end end end end end active_model_serializers-0.9.7/lib/active_model/default_serializer.rb000066400000000000000000000011441310165616500262050ustar00rootroot00000000000000require 'active_model/serializable' module ActiveModel # DefaultSerializer # # Provides a constant interface for all items class DefaultSerializer include ActiveModel::Serializable attr_reader :object def initialize(object, options={}) @object = object @wrap_in_array = options[:_wrap_in_array] end def as_json(options={}) instrument do return [] if @object.nil? && @wrap_in_array hash = @object.as_json @wrap_in_array ? [hash] : hash end end alias serializable_hash as_json alias serializable_object as_json end end active_model_serializers-0.9.7/lib/active_model/serializable.rb000066400000000000000000000025341310165616500250020ustar00rootroot00000000000000require 'active_model/serializable/utils' module ActiveModel module Serializable INSTRUMENTATION_KEY = '!serialize.active_model_serializers'.freeze def self.included(base) base.extend Utils end def as_json(options={}) instrument do if root = options.fetch(:root, json_key) hash = { root => serializable_object(options) } hash.merge!(serializable_data) hash else serializable_object(options) end end end def serializable_object_with_notification(options={}) instrument { serializable_object(options) } end def serializable_data embedded_in_root_associations.tap do |hash| if respond_to?(:meta) && meta hash[meta_key] = meta end end end def namespace if module_name = get_namespace Serializer.serializers_cache.fetch_or_store(module_name) do Utils._const_get(module_name) end end end def embedded_in_root_associations {} end private def get_namespace modules = self.class.name.split('::') modules[0..-2].join('::') if modules.size > 1 end def instrument(&block) payload = { serializer: self.class.name } ActiveSupport::Notifications.instrument(INSTRUMENTATION_KEY, payload, &block) end end end active_model_serializers-0.9.7/lib/active_model/serializable/000077500000000000000000000000001310165616500244515ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializable/utils.rb000066400000000000000000000005111310165616500261330ustar00rootroot00000000000000module ActiveModel module Serializable module Utils extend self def _const_get(const) begin method = RUBY_VERSION >= '2.0' ? :const_get : :qualified_const_get Object.send method, const rescue NameError const.safe_constantize end end end end endactive_model_serializers-0.9.7/lib/active_model/serializer.rb000066400000000000000000000222271310165616500245060ustar00rootroot00000000000000require 'active_model/array_serializer' require 'active_model/serializable' require 'active_model/serializer/association' require 'active_model/serializer/config' require 'thread' require 'concurrent/map' module ActiveModel class Serializer include Serializable @mutex = Mutex.new class << self def inherited(base) base._root = _root base._attributes = (_attributes || []).dup base._associations = (_associations || {}).dup end def setup @mutex.synchronize do yield CONFIG end end EMBED_IN_ROOT_OPTIONS = [ :include, :embed_in_root, :embed_in_root_key, :embed_namespace ].freeze def embed(type, options={}) CONFIG.embed = type if EMBED_IN_ROOT_OPTIONS.any? { |opt| options[opt].present? } CONFIG.embed_in_root = true end if options[:embed_in_root_key].present? CONFIG.embed_in_root_key = options[:embed_in_root_key] end ActiveSupport::Deprecation.warn <<-WARN ** Notice: embed is deprecated. ** The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope. Please use the global .setup method instead: ActiveModel::Serializer.setup do |config| config.embed = :#{type} config.embed_in_root = #{CONFIG.embed_in_root || false} end WARN end def format_keys(format) @key_format = format end attr_reader :key_format def serializer_for(resource, options = {}) if resource.respond_to?(:serializer_class) resource.serializer_class elsif resource.respond_to?(:to_ary) if Object.constants.include?(:ArraySerializer) ::ArraySerializer else ArraySerializer end else klass_name = build_serializer_class(resource, options) Serializer.serializers_cache.fetch_or_store(klass_name) do _const_get(klass_name) end end end attr_accessor :_root, :_attributes, :_associations alias root _root= alias root= _root= def root_name if name root_name = name.demodulize.underscore.sub(/_serializer$/, '') CONFIG.plural_default_root ? root_name.pluralize : root_name end end def attributes(*attrs) attrs.each do |attr| striped_attr = strip_attribute attr @_attributes << striped_attr define_method striped_attr do object.read_attribute_for_serialization attr end unless method_defined?(attr) end end def has_one(*attrs) associate(Association::HasOne, *attrs) end def has_many(*attrs) associate(Association::HasMany, *attrs) end def serializers_cache @serializers_cache ||= Concurrent::Map.new end private def strip_attribute(attr) symbolized = attr.is_a?(Symbol) attr = attr.to_s.gsub(/\?\Z/, '') attr = attr.to_sym if symbolized attr end def build_serializer_class(resource, options) "".tap do |klass_name| klass_name << "#{options[:namespace]}::" if options[:namespace] klass_name << options[:prefix].to_s.classify if options[:prefix] klass_name << "#{resource.class.name}Serializer" end end def associate(klass, *attrs) options = attrs.extract_options! attrs.each do |attr| define_method attr do object.send attr end unless method_defined?(attr) @_associations[attr] = klass.new(attr, options) end end end def initialize(object, options={}) @object = object @scope = options[:scope] @root = options.fetch(:root, self.class._root) @polymorphic = options.fetch(:polymorphic, false) @meta_key = options[:meta_key] || :meta @meta = options[@meta_key] @wrap_in_array = options[:_wrap_in_array] @only = options[:only] ? Array(options[:only]) : nil @except = options[:except] ? Array(options[:except]) : nil @key_format = options[:key_format] @context = options[:context] @namespace = options[:namespace] end attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context, :polymorphic def json_key key = if root == true || root.nil? self.class.root_name else root end key_format == :lower_camel && key.present? ? key.camelize(:lower) : key end def attributes filter(self.class._attributes.dup).each_with_object({}) do |name, hash| hash[name] = send(name) end end def associations(options={}) associations = self.class._associations included_associations = filter(associations.keys) associations.each_with_object({}) do |(name, association), hash| if included_associations.include? name if association.embed_ids? ids = serialize_ids association if association.embed_namespace? hash = hash[association.embed_namespace] ||= {} hash[association.key] = ids else hash[association.key] = ids end elsif association.embed_objects? if association.embed_namespace? hash = hash[association.embed_namespace] ||= {} end hash[association.embedded_key] = serialize association, options end end end end def filter(keys) if @only keys & @only elsif @except keys - @except else keys end end def embedded_in_root_associations associations = self.class._associations included_associations = filter(associations.keys) associations.each_with_object({}) do |(name, association), hash| if included_associations.include? name association_serializer = build_serializer(association) # we must do this always because even if the current association is not # embedded in root, it might have its own associations that are embedded in root hash.merge!(association_serializer.embedded_in_root_associations) do |key, oldval, newval| if oldval.respond_to?(:to_ary) [oldval, newval].flatten.uniq else oldval.merge(newval) { |_, oldval, newval| [oldval, newval].flatten.uniq } end end if association.embed_in_root? if association.embed_in_root_key? hash = hash[association.embed_in_root_key] ||= {} end serialized_data = association_serializer.serializable_object key = association.root_key if hash.has_key?(key) hash[key].concat(serialized_data).uniq! else hash[key] = serialized_data end end end end end def build_serializer(association) object = send(association.name) association.build_serializer(object, association_options_for_serializer(association)) end def association_options_for_serializer(association) prefix = association.options[:prefix] namespace = association.options[:namespace] || @namespace || self.namespace { scope: scope }.tap do |opts| opts[:namespace] = namespace if namespace opts[:prefix] = prefix if prefix end end def serialize(association,options={}) build_serializer(association).serializable_object(options) end def serialize_ids(association) associated_data = send(association.name) if associated_data.respond_to?(:to_ary) associated_data.map { |elem| serialize_id(elem, association) } else serialize_id(associated_data, association) if associated_data end end def key_format @key_format || self.class.key_format || CONFIG.key_format end def format_key(key) if key_format == :lower_camel key.to_s.camelize(:lower) else key end end def convert_keys(hash) Hash[hash.map do |k,v| key = if k.is_a?(Symbol) format_key(k).to_sym else format_key(k) end [key ,v] end] end attr_writer :serialization_options def serialization_options @serialization_options || {} end def serializable_object(options={}) self.serialization_options = options return @wrap_in_array ? [] : nil if @object.nil? hash = attributes hash.merge! associations(options) hash = convert_keys(hash) if key_format.present? hash = { :type => type_name(@object), type_name(@object) => hash } if @polymorphic @wrap_in_array ? [hash] : hash end alias_method :serializable_hash, :serializable_object def serialize_id(elem, association) id = elem.read_attribute_for_serialization(association.embed_key) association.polymorphic? ? { id: id, type: type_name(elem) } : id end def type_name(elem) elem.class.to_s.demodulize.underscore.to_sym end end end active_model_serializers-0.9.7/lib/active_model/serializer/000077500000000000000000000000001310165616500241545ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/association.rb000066400000000000000000000042761310165616500270260ustar00rootroot00000000000000require 'active_model/default_serializer' require 'active_model/serializer/association/has_one' require 'active_model/serializer/association/has_many' module ActiveModel class Serializer class Association def initialize(name, options={}) if options.has_key?(:include) ActiveSupport::Deprecation.warn <<-WARN ** Notice: include was renamed to embed_in_root. ** WARN end @name = name.to_s @options = options self.embed = options.fetch(:embed) { CONFIG.embed } @polymorphic = options.fetch(:polymorphic, false) @embed_in_root = options.fetch(:embed_in_root) { options.fetch(:include) { CONFIG.embed_in_root } } @key_format = options.fetch(:key_format) { CONFIG.key_format } @embed_key = options[:embed_key] || :id @key = options[:key] @embedded_key = options[:root] || name @embed_in_root_key = options.fetch(:embed_in_root_key) { CONFIG.embed_in_root_key } @embed_namespace = options.fetch(:embed_namespace) { CONFIG.embed_namespace } serializer = @options[:serializer] @serializer_from_options = serializer.is_a?(String) ? serializer.constantize : serializer end attr_reader :name, :embed_ids, :embed_objects, :polymorphic attr_accessor :embed_in_root, :embed_key, :key, :embedded_key, :root_key, :serializer_from_options, :options, :key_format, :embed_in_root_key, :embed_namespace alias embed_ids? embed_ids alias embed_objects? embed_objects alias embed_in_root? embed_in_root alias embed_in_root_key? embed_in_root_key alias embed_namespace? embed_namespace alias polymorphic? polymorphic def embed=(embed) @embed_ids = embed == :id || embed == :ids @embed_objects = embed == :object || embed == :objects end def serializer_from_object(object, options = {}) Serializer.serializer_for(object, options) end def default_serializer DefaultSerializer end def build_serializer(object, options = {}) serializer_class(object, options).new(object, options.merge(self.options)) end end end end active_model_serializers-0.9.7/lib/active_model/serializer/association/000077500000000000000000000000001310165616500264705ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/association/has_many.rb000066400000000000000000000016521310165616500306200ustar00rootroot00000000000000module ActiveModel class Serializer class Association class HasMany < Association def initialize(name, *args) super @root_key = @embedded_key.to_s @key ||= case CONFIG.default_key_type when :name then name.to_s.pluralize else "#{name.to_s.singularize}_ids" end end def serializer_class(object, _) if use_array_serializer? ArraySerializer else serializer_from_options end end def options if use_array_serializer? { each_serializer: serializer_from_options }.merge! super else super end end private def use_array_serializer? !serializer_from_options || serializer_from_options && !(serializer_from_options <= ArraySerializer) end end end end end active_model_serializers-0.9.7/lib/active_model/serializer/association/has_one.rb000066400000000000000000000012651310165616500304350ustar00rootroot00000000000000module ActiveModel class Serializer class Association class HasOne < Association def initialize(name, *args) super @root_key = @embedded_key.to_s.pluralize @key ||= case CONFIG.default_key_type when :name then name.to_s.singularize else "#{name}_id" end end def serializer_class(object, options = {}) (serializer_from_options unless object.nil?) || serializer_from_object(object, options) || default_serializer end def build_serializer(object, options = {}) options[:_wrap_in_array] = embed_in_root? super end end end end endactive_model_serializers-0.9.7/lib/active_model/serializer/config.rb000066400000000000000000000011371310165616500257500ustar00rootroot00000000000000module ActiveModel class Serializer class Config def initialize(data = {}) @data = data end def each(&block) @data.each(&block) end def clear @data.clear end def method_missing(name, *args) name = name.to_s return @data[name] if @data.include?(name) match = name.match(/\A(.*?)([?=]?)\Z/) case match[2] when "=" @data[match[1]] = args.first when "?" !!@data[match[1]] end end end CONFIG = Config.new('embed' => :objects) # :nodoc: end end active_model_serializers-0.9.7/lib/active_model/serializer/generators/000077500000000000000000000000001310165616500263255ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/resource_override.rb000066400000000000000000000003501310165616500323760ustar00rootroot00000000000000require 'rails/generators' require 'rails/generators/rails/resource/resource_generator' module Rails module Generators class ResourceGenerator def add_serializer invoke 'serializer' end end end end active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializer/000077500000000000000000000000001310165616500304765ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializer/USAGE000066400000000000000000000004401310165616500312630ustar00rootroot00000000000000Description: Generates a serializer for the given resource with tests. Example: `rails generate serializer Account name created_at` For TestUnit it creates: Serializer: app/serializers/account_serializer.rb TestUnit: test/unit/account_serializer_test.rb scaffold_controller_generator.rb000066400000000000000000000005621310165616500370410ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializerrequire 'rails/generators' require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator' module Rails module Generators class ScaffoldControllerGenerator if Rails::VERSION::MAJOR >= 4 source_root File.expand_path('../templates', __FILE__) hook_for :serializer, default: true, type: :boolean end end end end serializer_generator.rb000066400000000000000000000022001310165616500351550ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializermodule Rails module Generators class SerializerGenerator < NamedBase source_root File.expand_path('../templates', __FILE__) check_class_collision suffix: 'Serializer' argument :attributes, type: :array, default: [], banner: 'field:type field:type' class_option :parent, type: :string, desc: 'The parent class for the generated serializer' def create_serializer_file template 'serializer.rb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb") end private def attributes_names [:id] + attributes.select { |attr| !attr.reference? }.map { |a| a.name.to_sym } end def association_names attributes.select { |attr| attr.reference? }.map { |a| a.name.to_sym } end def parent_class_name if options[:parent] options[:parent] elsif (ns = Rails::Generators.namespace) && ns.const_defined?(:ApplicationSerializer) || (Object.const_get(:ApplicationSerializer) rescue nil) 'ApplicationSerializer' else 'ActiveModel::Serializer' end end end end end active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializer/templates/000077500000000000000000000000001310165616500324745ustar00rootroot00000000000000controller.rb000066400000000000000000000056471310165616500351410ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializer/templates<% if namespaced? -%> require_dependency "<%= namespaced_file_path %>/application_controller" <% end -%> <% module_namespacing do -%> class <%= controller_class_name %>Controller < ApplicationController before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] # GET <%= route_url %> # GET <%= route_url %>.json def index @<%= plural_table_name %> = <%= orm_class.all(class_name) %> respond_to do |format| format.html # index.html.erb format.json { render json: <%= "@#{plural_table_name}" %> } end end # GET <%= route_url %>/1 # GET <%= route_url %>/1.json def show respond_to do |format| format.html # show.html.erb format.json { render json: <%= "@#{singular_table_name}" %> } end end # GET <%= route_url %>/new def new @<%= singular_table_name %> = <%= orm_class.build(class_name) %> end # GET <%= route_url %>/1/edit def edit end # POST <%= route_url %> # POST <%= route_url %>.json def create @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> respond_to do |format| if @<%= orm_instance.save %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> } format.json { render json: <%= "@#{singular_table_name}" %>, status: :created } else format.html { render action: 'new' } format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity } end end end # PATCH/PUT <%= route_url %>/1 # PATCH/PUT <%= route_url %>/1.json def update respond_to do |format| if @<%= orm_instance.update("#{singular_table_name}_params") %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity } end end end # DELETE <%= route_url %>/1 # DELETE <%= route_url %>/1.json def destroy @<%= orm_instance.destroy %> respond_to do |format| format.html { redirect_to <%= index_helper %>_url } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_<%= singular_table_name %> @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> end # Never trust parameters from the scary internet, only allow the white list through. def <%= "#{singular_table_name}_params" %> <%- if attributes_names.empty? -%> params[<%= ":#{singular_table_name}" %>] <%- else -%> params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) <%- end -%> end end <% end -%> serializer.rb000066400000000000000000000003741310165616500351170ustar00rootroot00000000000000active_model_serializers-0.9.7/lib/active_model/serializer/generators/serializer/templates<% module_namespacing do -%> class <%= class_name %>Serializer < <%= parent_class_name %> attributes <%= attributes_names.map(&:inspect).join(", ") %> <% association_names.each do |attribute| -%> has_one :<%= attribute %> <% end -%> end <% end -%> active_model_serializers-0.9.7/lib/active_model/serializer/railtie.rb000066400000000000000000000013321310165616500261310ustar00rootroot00000000000000module ActiveModel class Railtie < Rails::Railtie initializer 'generators' do |app| app.load_generators require 'active_model/serializer/generators/serializer/serializer_generator' require 'active_model/serializer/generators/serializer/scaffold_controller_generator' require 'active_model/serializer/generators/resource_override' end initializer 'include_routes.active_model_serializer' do |app| ActiveSupport.on_load(:active_model_serializers) do include app.routes.url_helpers end end config.to_prepare do ActiveModel::Serializer.serializers_cache.clear end end end ActiveSupport.run_load_hooks(:active_model_serializers, ActiveModel::Serializer) active_model_serializers-0.9.7/lib/active_model/serializer/version.rb000066400000000000000000000001061310165616500261630ustar00rootroot00000000000000module ActiveModel class Serializer VERSION = '0.9.7' end end active_model_serializers-0.9.7/lib/active_model/serializer_support.rb000066400000000000000000000001501310165616500262710ustar00rootroot00000000000000module ActiveModel module SerializerSupport alias read_attribute_for_serialization send end end active_model_serializers-0.9.7/lib/active_model_serializers.rb000066400000000000000000000012451310165616500247460ustar00rootroot00000000000000require 'active_model' require 'active_model/serializer' require 'active_model/serializer_support' require 'active_model/serializer/version' require 'active_model/serializer/railtie' if defined?(Rails) begin require 'action_controller' require 'action_controller/serialization' require 'action_controller/serialization_test_case' ActiveSupport.on_load(:action_controller) do if ::ActionController::Serialization.enabled ActionController::Base.send(:include, ::ActionController::Serialization) ActionController::TestCase.send(:include, ::ActionController::SerializationAssertions) end end rescue LoadError # rails not installed, continuing end active_model_serializers-0.9.7/test/000077500000000000000000000000001310165616500175615ustar00rootroot00000000000000active_model_serializers-0.9.7/test/benchmark/000077500000000000000000000000001310165616500215135ustar00rootroot00000000000000active_model_serializers-0.9.7/test/benchmark/app.rb000066400000000000000000000032021310165616500226150ustar00rootroot00000000000000# https://github.com/rails-api/active_model_serializers/pull/872 # approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against require 'bundler/setup' require 'rails' require 'active_model' require 'active_support' require 'active_support/json' require 'action_controller' require 'action_controller/test_case' require 'action_controller/railtie' abort "Rails application already defined: #{Rails.application.class}" if Rails.application class NullLogger < Logger def initialize(*_args) end def add(*_args, &_block) end end class BenchmarkLogger < ActiveSupport::Logger def initialize @file = StringIO.new super(@file) end def messages @file.rewind @file.read end end # ref: https://gist.github.com/bf4/8744473 class BenchmarkApp < Rails::Application # Set up production configuration config.eager_load = true config.cache_classes = true # CONFIG: CACHE_ON={on,off} config.action_controller.perform_caching = ENV['CACHE_ON'] != 'off' config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) config.active_support.test_order = :random config.secret_token = 'S' * 30 config.secret_key_base = 'abc123' config.consider_all_requests_local = false # otherwise deadlock occurred config.middleware.delete 'Rack::Lock' # to disable log files config.logger = NullLogger.new config.active_support.deprecation = :log config.log_level = :info end require 'active_model_serializers' # Initialize app before any serializers are defined, for running across revisions. # ref: https://github.com/rails-api/active_model_serializers/pull/1478 Rails.application.initialize! active_model_serializers-0.9.7/test/benchmark/benchmarking_support.rb000066400000000000000000000033271310165616500262710ustar00rootroot00000000000000require 'benchmark/ips' require 'json' # Add benchmarking runner from ruby-bench-suite # https://github.com/ruby-bench/ruby-bench-suite/blob/master/rails/benchmarks/support/benchmark_rails.rb module Benchmark module ActiveModelSerializers module TestMethods def request(method, path) response = Rack::MockRequest.new(BenchmarkApp).send(method, path) if response.status.in?([404, 500]) fail "omg, #{method}, #{path}, '#{response.status}', '#{response.body}'" end response end end # extend Benchmark with an `ams` method def ams(label = nil, time:, disable_gc: true, warmup: 3, &block) fail ArgumentError.new, 'block should be passed' unless block_given? if disable_gc GC.disable else GC.enable end report = Benchmark.ips(time, warmup, true) do |x| x.report(label) { yield } end entry = report.entries.first output = { label: label, version: ::ActiveModel::Serializer::VERSION.to_s, rails_version: ::Rails.version.to_s, iterations_per_second: entry.ips, iterations_per_second_standard_deviation: entry.error_percentage, total_allocated_objects_per_iteration: count_total_allocated_objects(&block) }.to_json puts output output end def count_total_allocated_objects if block_given? key = if RUBY_VERSION < '2.2' :total_allocated_object else :total_allocated_objects end before = GC.stat[key] yield after = GC.stat[key] after - before else -1 end end end extend Benchmark::ActiveModelSerializers end active_model_serializers-0.9.7/test/benchmark/bm_active_record.rb000066400000000000000000000026651310165616500253400ustar00rootroot00000000000000require_relative './benchmarking_support' require_relative './app' require_relative './setup' time = 10 disable_gc = true authors_query = Author.preload(:posts).preload(:profile) author = authors_query.first authors = authors_query.to_a Benchmark.ams('Single: DefaultSerializer', time: time, disable_gc: disable_gc) do ActiveModel::DefaultSerializer.new(author).to_json end Benchmark.ams('ArraySerializer', time: time, disable_gc: disable_gc) do ActiveModel::ArraySerializer.new(authors).to_json end Benchmark.ams('ArraySerializer: each_serializer: DefaultSerializer', time: time, disable_gc: disable_gc) do ActiveModel::ArraySerializer.new(authors, each_serializer:ActiveModel::DefaultSerializer).to_json end Benchmark.ams('FlatAuthorSerializer', time: time, disable_gc: disable_gc) do FlatAuthorSerializer.new(author).to_json end Benchmark.ams('ArraySerializer: each_serializer: FlatAuthorSerializer', time: time, disable_gc: disable_gc) do ActiveModel::ArraySerializer.new(authors, each_serializer: FlatAuthorSerializer).to_json end Benchmark.ams('AuthorWithDefaultRelationshipsSerializer', time: time, disable_gc: disable_gc) do AuthorWithDefaultRelationshipsSerializer.new(author).to_json end Benchmark.ams('ArraySerializer: each_serializer: AuthorWithDefaultRelationshipsSerializer', time: time, disable_gc: disable_gc) do ActiveModel::ArraySerializer.new(authors, each_serializer: AuthorWithDefaultRelationshipsSerializer).to_json end active_model_serializers-0.9.7/test/benchmark/setup.rb000066400000000000000000000032641310165616500232050ustar00rootroot00000000000000########################################### # Setup active record models ########################################## require 'active_record' require 'sqlite3' # Change the following to reflect your database settings ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ':memory:' ) # Don't show migration output when constructing fake db ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :authors, force: true do |t| t.string :name end create_table :posts, force: true do |t| t.text :body t.string :title t.references :author end create_table :profiles, force: true do |t| t.text :project_url t.text :bio t.date :birthday t.references :author end end class Author < ActiveRecord::Base has_one :profile has_many :posts end class Post < ActiveRecord::Base belongs_to :author end class Profile < ActiveRecord::Base belongs_to :author end # Build out the data to serialize author = Author.create(name: 'Preston Sego') Profile.create(project_url: 'https://github.com/NullVoxPopuli', author: author) 50.times do Post.create( body: 'something about how password restrictions are evil, and less secure, and with the math to prove it.', title: 'Your bank is does not know how to do security', author: author ) end ActiveModel::Serializer.root = false ActiveModel::ArraySerializer.root = false class FlatAuthorSerializer < ActiveModel::Serializer attributes :id, :name end class AuthorWithDefaultRelationshipsSerializer < ActiveModel::Serializer attributes :id, :name has_one :profile has_many :posts end # For debugging SQL output #ActiveRecord::Base.logger = Logger.new(STDERR) active_model_serializers-0.9.7/test/fixtures/000077500000000000000000000000001310165616500214325ustar00rootroot00000000000000active_model_serializers-0.9.7/test/fixtures/active_record.rb000066400000000000000000000043561310165616500246000ustar00rootroot00000000000000require 'active_record' ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ':memory:' ) ActiveRecord::Schema.define do create_table :ar_posts, force: true do |t| t.string :title t.text :body t.belongs_to :ar_section, index: true t.timestamps end create_table :ar_comments, force: true do |t| t.text :body t.belongs_to :ar_post, index: true t.timestamps end create_table :ar_tags, force: true do |t| t.string :name end create_table :ar_sections, force: true do |t| t.string :name end create_table :ar_posts_tags, force: true do |t| t.references :ar_post, :ar_tag, index: true end create_table :ar_comments_tags, force: true do |t| t.references :ar_comment, :ar_tag, index: true end end class ARPost < ActiveRecord::Base has_many :ar_comments, class_name: 'ARComment' has_and_belongs_to_many :ar_tags, class_name: 'ARTag', join_table: :ar_posts_tags belongs_to :ar_section, class_name: 'ARSection' end class ARComment < ActiveRecord::Base belongs_to :ar_post, class_name: 'ARPost' has_and_belongs_to_many :ar_tags, class_name: 'ARTag', join_table: :ar_comments_tags end class ARTag < ActiveRecord::Base end class ARSection < ActiveRecord::Base end class ARPostSerializer < ActiveModel::Serializer attributes :title, :body has_many :ar_comments, :ar_tags has_one :ar_section end class ARCommentSerializer < ActiveModel::Serializer attributes :body has_many :ar_tags end class ARTagSerializer < ActiveModel::Serializer attributes :name end class ARSectionSerializer < ActiveModel::Serializer attributes 'name' end class AREmbeddedSerializer < ActiveModel::Serializer has_many :ar_tags, :ar_comments end ARPost.create(title: 'New post', body: 'A body!!!', ar_section: ARSection.create(name: 'ruby')).tap do |post| short_tag = post.ar_tags.create(name: 'short') whiny_tag = post.ar_tags.create(name: 'whiny') happy_tag = ARTag.create(name: 'happy') post.ar_comments.create(body: 'what a dumb post').tap do |comment| comment.ar_tags.concat happy_tag, whiny_tag end post.ar_comments.create(body: 'i liked it').tap do |comment| comment.ar_tags.concat happy_tag, short_tag end end active_model_serializers-0.9.7/test/fixtures/poro.rb000066400000000000000000000103331310165616500227360ustar00rootroot00000000000000class Model def initialize(hash = {}) @attributes = hash end def read_attribute_for_serialization(name) if name == :id || name == 'id' object_id elsif respond_to?(name) send name else @attributes[name] end end end ### ## Models ### class User < Model def profile @profile ||= Profile.new(name: 'N1', description: 'D1') end end class UserInfo < Model def user @user ||= User.new(name: 'N1', email: 'E1') end end class Profile < Model end class Category < Model def posts @posts ||= [Post.new(title: 'T1', body: 'B1'), Post.new(title: 'T2', body: 'B2')] end end class Post < Model def comments @comments ||= [Comment.new(content: 'C1'), Comment.new(content: 'C2')] end end class SpecialPost < Post def special_comment @special_comment ||= Comment.new(content: 'special') end end class Type < Model end class SelfReferencingUser < Model def type @type ||= Type.new(name: 'N1') end def parent @parent ||= SelfReferencingUserParent.new(name: 'N1') end end class SelfReferencingUserParent < Model def type @type ||= Type.new(name: 'N2') end def parent end end class Comment < Model end class WebLog < Model end class Interview < Model def attachment @attachment ||= Image.new(url: 'U1') end end class Mail < Model def attachments @attachments ||= [Image.new(url: 'U1'), Video.new(html: 'H1')] end end class Image < Model end class Video < Model end ### ## Serializers ### class UserSerializer < ActiveModel::Serializer attributes :name, :email has_one :profile end class TypeSerializer < ActiveModel::Serializer attributes :name end class SelfReferencingUserParentSerializer < ActiveModel::Serializer attributes :name has_one :type, serializer: TypeSerializer, embed: :ids, include: true end class SelfReferencingUserSerializer < ActiveModel::Serializer attributes :name has_one :type, serializer: TypeSerializer, embed: :ids, include: true has_one :parent, serializer: SelfReferencingUserSerializer, embed: :ids, include: true end class UserInfoSerializer < ActiveModel::Serializer has_one :user, serializer: UserSerializer end class ProfileSerializer < ActiveModel::Serializer def description description = object.read_attribute_for_serialization(:description) scope ? "#{description} - #{scope}" : description end attributes :name, :description end class CategorySerializer < ActiveModel::Serializer attributes :name has_many :posts end class PostSerializer < ActiveModel::Serializer attributes :title, :body def title keyword = serialization_options[:highlight_keyword] title = object.read_attribute_for_serialization(:title) title = title.gsub(keyword,"'#{keyword}'") if keyword title end has_many :comments end class SpecialPostSerializer < ActiveModel::Serializer attributes :title, :body has_many :comments, root: :comments, embed_in_root: true, embed: :ids has_one :special_comment, root: :comments, embed_in_root: true, embed: :ids end class CommentSerializer < ActiveModel::Serializer attributes :content end class WebLogSerializer < ActiveModel::Serializer attributes :name, :display_name end class WebLogLowerCamelSerializer < WebLogSerializer format_keys :lower_camel end class InterviewSerializer < ActiveModel::Serializer attributes :text has_one :attachment, polymorphic: true end class MailSerializer < ActiveModel::Serializer attributes :body has_many :attachments, polymorphic: true end class ImageSerializer < ActiveModel::Serializer attributes :url end class VideoSerializer < ActiveModel::Serializer attributes :html end class ShortProfileSerializer < ::ProfileSerializer; end module TestNamespace class ProfileSerializer < ::ProfileSerializer; end class UserSerializer < ::UserSerializer; end end ActiveModel::Serializer.setup do |config| config.default_key_type = :name end class NameKeyUserSerializer < ActiveModel::Serializer attributes :name, :email has_one :profile end class NameKeyPostSerializer < ActiveModel::Serializer attributes :title, :body has_many :comments end ActiveModel::Serializer.setup do |config| config.default_key_type = nil end active_model_serializers-0.9.7/test/fixtures/template.html.erb000066400000000000000000000000161310165616500246770ustar00rootroot00000000000000

Hello.

active_model_serializers-0.9.7/test/integration/000077500000000000000000000000001310165616500221045ustar00rootroot00000000000000active_model_serializers-0.9.7/test/integration/action_controller/000077500000000000000000000000001310165616500256245ustar00rootroot00000000000000active_model_serializers-0.9.7/test/integration/action_controller/namespaced_serialization_test.rb000066400000000000000000000056641310165616500342600ustar00rootroot00000000000000require 'test_helper' module ActionController module Serialization class NamespacedSerializationTest < ActionController::TestCase class TestNamespace::MyController < ActionController::Base def render_profile_with_namespace render json: Profile.new({ name: 'Name 1', description: 'Description 1'}) end def render_profiles_with_namespace render json: [Profile.new({ name: 'Name 1', description: 'Description 1'})] end def render_comment render json: Comment.new(content: 'Comment 1') end def render_comments render json: [Comment.new(content: 'Comment 1')] end def render_hash render json: {message: 'not found'}, status: 404 end end tests TestNamespace::MyController def test_render_profile_with_namespace get :render_profile_with_namespace assert_serializer TestNamespace::ProfileSerializer end def test_render_profiles_with_namespace get :render_profiles_with_namespace assert_serializer TestNamespace::ProfileSerializer end def test_fallback_to_a_version_without_namespace get :render_comment assert_serializer CommentSerializer end def test_array_fallback_to_a_version_without_namespace get :render_comments assert_serializer CommentSerializer end def test_render_hash_regression get :render_hash assert_equal JSON.parse(response.body), {'message' => 'not found'} end end class OptionNamespacedSerializationTest < ActionController::TestCase class MyController < ActionController::Base def default_serializer_options { namespace: TestNamespace } end def render_profile_with_namespace_option render json: Profile.new({ name: 'Name 1', description: 'Description 1'}) end def render_profiles_with_namespace_option render json: [Profile.new({ name: 'Name 1', description: 'Description 1'})] end def render_comment render json: Comment.new(content: 'Comment 1') end def render_comments render json: [Comment.new(content: 'Comment 1')] end end tests MyController def test_render_profile_with_namespace_option get :render_profile_with_namespace_option assert_serializer TestNamespace::ProfileSerializer end def test_render_profiles_with_namespace_option get :render_profiles_with_namespace_option assert_serializer TestNamespace::ProfileSerializer end def test_fallback_to_a_version_without_namespace get :render_comment assert_serializer CommentSerializer end def test_array_fallback_to_a_version_without_namespace get :render_comments assert_serializer CommentSerializer end end end end active_model_serializers-0.9.7/test/integration/action_controller/serialization_test.rb000066400000000000000000000224141310165616500320700ustar00rootroot00000000000000require 'test_helper' module ActionController module Serialization class ImplicitSerializerTest < ActionController::TestCase class MyController < ActionController::Base def render_using_implicit_serializer render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end end tests MyController def test_render_using_implicit_serializer get :render_using_implicit_serializer assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1"}}', @response.body end end class ImplicitSerializerScopeTest < ActionController::TestCase class MyController < ActionController::Base def render_using_implicit_serializer_and_scope render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end private def current_user 'current_user' end end tests MyController def test_render_using_implicit_serializer_and_scope get :render_using_implicit_serializer_and_scope assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1 - current_user"}}', @response.body end end class DefaultOptionsForSerializerScopeTest < ActionController::TestCase class MyController < ActionController::Base def default_serializer_options { scope: current_admin } end def render_using_scope_set_in_default_serializer_options render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end private def current_user 'current_user' end def current_admin 'current_admin' end end tests MyController def test_render_using_scope_set_in_default_serializer_options get :render_using_scope_set_in_default_serializer_options assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1 - current_admin"}}', @response.body end end class ExplicitSerializerScopeTest < ActionController::TestCase class MyController < ActionController::Base def render_using_implicit_serializer_and_explicit_scope render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), scope: current_admin end private def current_user 'current_user' end def current_admin 'current_admin' end end tests MyController def test_render_using_implicit_serializer_and_explicit_scope get :render_using_implicit_serializer_and_explicit_scope assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1 - current_admin"}}', @response.body end end class OverridingSerializationScopeTest < ActionController::TestCase class MyController < ActionController::Base def render_overriding_serialization_scope render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end private def current_user 'current_user' end def serialization_scope 'current_admin' end end tests MyController def test_render_overriding_serialization_scope get :render_overriding_serialization_scope assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1 - current_admin"}}', @response.body end end class CallingSerializationScopeTest < ActionController::TestCase class MyController < ActionController::Base def render_calling_serialization_scope render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end private def current_user 'current_user' end serialization_scope :current_user end tests MyController def test_render_calling_serialization_scope get :render_calling_serialization_scope assert_equal 'application/json', @response.content_type assert_equal '{"profile":{"name":"Name 1","description":"Description 1 - current_user"}}', @response.body end end class JSONDumpSerializerTest < ActionController::TestCase class MyController < ActionController::Base def render_using_json_dump render json: JSON.dump(hello: 'world') end end tests MyController def test_render_using_json_dump get :render_using_json_dump assert_equal 'application/json', @response.content_type assert_equal '{"hello":"world"}', @response.body end end class RailsSerializerTest < ActionController::TestCase class MyController < ActionController::Base def render_using_rails_behavior render json: [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })], serializer: false end end tests MyController def test_render_using_rails_behavior get :render_using_rails_behavior assert_equal 'application/json', @response.content_type assert_equal '[{"attributes":{"name":"Name 1","description":"Description 1","comments":"Comments 1"}}]', @response.body end end class ArraySerializerTest < ActionController::TestCase class MyController < ActionController::Base def render_array render json: [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })] end end tests MyController def test_render_array get :render_array assert_equal 'application/json', @response.content_type assert_equal '{"my":[{"name":"Name 1","description":"Description 1"}]}', @response.body end end class LowerCamelArraySerializerTest < ActionController::TestCase class WebLogController < ActionController::Base def render_array render json: [WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}), WebLog.new({name: 'Name 2', display_name: 'Display Name 2'})], each_serializer: WebLogLowerCamelSerializer end end tests WebLogController def test_render_array get :render_array assert_equal 'application/json', @response.content_type assert_equal '{"webLog":[{"name":"Name 1","displayName":"Display Name 1"},{"name":"Name 2","displayName":"Display Name 2"}]}', @response.body end end class LowerCamelWoRootSerializerTest < ActionController::TestCase class WebLogController < ActionController::Base def render_without_root render json: WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}), root: false, serializer: WebLogLowerCamelSerializer end end tests WebLogController def test_render_without_root get :render_without_root assert_equal 'application/json', @response.content_type assert_equal '{"name":"Name 1","displayName":"Display Name 1"}', @response.body end end class LowerCamelArrayWoRootSerializerTest < ActionController::TestCase class WebLogController < ActionController::Base def render_array_without_root render json: [WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}), WebLog.new({name: 'Name 2', display_name: 'Display Name 2'})], root: false, each_serializer: WebLogLowerCamelSerializer end end tests WebLogController def test_render_array_without_root get :render_array_without_root assert_equal 'application/json', @response.content_type assert_equal '[{"name":"Name 1","displayName":"Display Name 1"},{"name":"Name 2","displayName":"Display Name 2"}]', @response.body end end class ArrayEmbedingSerializerTest < ActionController::TestCase def setup super @association = UserSerializer._associations[:profile] @old_association = @association.dup end def teardown super UserSerializer._associations[:profile] = @old_association end class MyController < ActionController::Base def initialize(*) super @user = User.new({ name: 'Name 1', email: 'mail@server.com', gender: 'M' }) end attr_reader :user def render_array_embeding_in_root render json: [@user] end end tests MyController def test_render_array_embeding_in_root @association.embed = :ids @association.embed_in_root = true get :render_array_embeding_in_root assert_equal 'application/json', @response.content_type assert_equal("{\"my\":[{\"name\":\"Name 1\",\"email\":\"mail@server.com\",\"profile_id\":#{@controller.user.profile.object_id}}],\"profiles\":[{\"name\":\"N1\",\"description\":\"D1\"}]}", @response.body) end end end end active_model_serializers-0.9.7/test/integration/action_controller/serialization_test_case_test.rb000066400000000000000000000041641310165616500341240ustar00rootroot00000000000000require 'test_helper' module ActionController module SerializationsAssertions class RenderSerializerTest < ActionController::TestCase class MyController < ActionController::Base def render_using_serializer render json: Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end def render_text render text: 'ok' end def render_template prepend_view_path "./test/fixtures" render template: "template" end end tests MyController def test_supports_specifying_serializers_with_a_serializer_class get :render_using_serializer assert_serializer ProfileSerializer end def test_supports_specifying_serializers_with_a_regexp get :render_using_serializer assert_serializer %r{\AProfile.+\Z} end def test_supports_specifying_serializers_with_a_string get :render_using_serializer assert_serializer 'ProfileSerializer' end def test_supports_specifying_serializers_with_a_symbol get :render_using_serializer assert_serializer :profile_serializer end def test_supports_specifying_serializers_with_a_nil get :render_text assert_serializer nil end def test_raises_descriptive_error_message_when_serializer_was_not_rendered get :render_using_serializer e = assert_raise ActiveSupport::TestCase::Assertion do assert_serializer 'PostSerializer' end assert_match 'expecting <"PostSerializer"> but rendering with <["ProfileSerializer"]>', e.message end def test_raises_argument_error_when_asserting_with_invalid_object get :render_using_serializer e = assert_raise ArgumentError do assert_serializer Hash end assert_match 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil', e.message end def test_does_not_overwrite_notification_subscriptions get :render_template assert_template "template" end end end end active_model_serializers-0.9.7/test/integration/active_record/000077500000000000000000000000001310165616500247155ustar00rootroot00000000000000active_model_serializers-0.9.7/test/integration/active_record/active_record_test.rb000066400000000000000000000064641310165616500311240ustar00rootroot00000000000000require 'test_helper' require 'fixtures/active_record' module ActiveModel class Serializer class ActiveRecordTest < Minitest::Test def setup @post = ARPost.first end def test_serialization_embedding_objects post_serializer = ARPostSerializer.new(@post) assert_equal({ 'ar_post' => { title: 'New post', body: 'A body!!!', ar_comments: [{ body: 'what a dumb post', ar_tags: [{ name: 'happy' }, { name: 'whiny' }] }, { body: 'i liked it', ar_tags: [{:name=>"happy"}, {:name=>"short"}] }], ar_tags: [{ name: 'short' }, { name: 'whiny' }], ar_section: { 'name' => 'ruby' } } }, post_serializer.as_json) end def test_serialization_embedding_ids post_serializer = ARPostSerializer.new(@post) embed(ARPostSerializer, embed: :ids) do assert_equal({ 'ar_post' => { title: 'New post', body: 'A body!!!', 'ar_comment_ids' => [1, 2], 'ar_tag_ids' => [1, 2], 'ar_section_id' => 1 } }, post_serializer.as_json) end end def test_serialization_embedding_ids_including_in_root post_serializer = ARPostSerializer.new(@post) embed(ARPostSerializer, embed: :ids, embed_in_root: true) do embed(ARCommentSerializer, embed: :ids, embed_in_root: true) do assert_equal({ 'ar_post' => { title: 'New post', body: 'A body!!!', 'ar_comment_ids' => [1, 2], 'ar_tag_ids' => [1, 2], 'ar_section_id' => 1 }, 'ar_comments' => [{ body: 'what a dumb post', 'ar_tag_ids' => [3, 2] }, { body: 'i liked it', 'ar_tag_ids' => [3, 1] }], 'ar_tags' => [{ name: 'happy' }, { name: 'whiny' }, { name: 'short' }], 'ar_sections' => [{ 'name' => 'ruby' }] }, post_serializer.as_json) end end end def test_serialization_embedding_ids_in_common_root_key post_serializer = AREmbeddedSerializer.new(@post) embed(AREmbeddedSerializer, embed: :ids, embed_in_root: true, embed_in_root_key: :linked) do embed(ARCommentSerializer, embed: :ids, embed_in_root: true, embed_in_root_key: :linked) do assert_equal({ 'ar_tags' => [{ name: 'short' }, { name: 'whiny' }, { name: 'happy' }], 'ar_comments' => [{ body: 'what a dumb post', 'ar_tag_ids' => [3, 2] }, { body: 'i liked it', 'ar_tag_ids' => [3, 1] }] }, post_serializer.as_json[:linked]) end end end private def embed(serializer_class, options = {}) old_assocs = Hash[serializer_class._associations.to_a.map { |(name, association)| [name, association.dup] }] serializer_class._associations.each_value do |association| association.embed = options[:embed] association.embed_in_root = options[:embed_in_root] association.embed_in_root_key = options[:embed_in_root_key] end yield ensure serializer_class._associations = old_assocs end end end end active_model_serializers-0.9.7/test/integration/generators/000077500000000000000000000000001310165616500242555ustar00rootroot00000000000000active_model_serializers-0.9.7/test/integration/generators/resource_generator_test.rb000066400000000000000000000013201310165616500315320ustar00rootroot00000000000000require 'test_helper' require 'rails' require 'active_model/serializer/railtie' require 'test_app' class ResourceGeneratorTest < Rails::Generators::TestCase destination File.expand_path('../../../tmp', __FILE__) setup :prepare_destination, :copy_routes tests Rails::Generators::ResourceGenerator arguments %w(account) def test_serializer_file_is_generated run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ end private def copy_routes config_dir = File.join(destination_root, 'config') FileUtils.mkdir_p(config_dir) File.write(File.join(config_dir, 'routes.rb'), 'Rails.application.routes.draw { }') end end active_model_serializers-0.9.7/test/integration/generators/scaffold_controller_generator_test.rb000066400000000000000000000047011310165616500337350ustar00rootroot00000000000000require 'test_helper' require 'rails' require 'active_model/serializer/railtie' require 'test_app' class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase destination File.expand_path('../../../tmp', __FILE__) setup :prepare_destination tests Rails::Generators::ScaffoldControllerGenerator arguments %w(account name:string description:text business:references) def test_generated_controller return true if Rails::VERSION::MAJOR < 4 run_generator assert_file 'app/controllers/accounts_controller.rb' do |content| assert_instance_method :index, content do |m| assert_match /@accounts = Account\.all/, m assert_match /format.html/, m assert_match /format.json \{ render json: @accounts \}/, m end assert_instance_method :show, content do |m| assert_match /format.html/, m assert_match /format.json \{ render json: @account \}/, m end assert_instance_method :new, content do |m| assert_match /@account = Account\.new/, m end assert_instance_method :edit, content do |m| assert m.blank? end assert_instance_method :create, content do |m| assert_match /@account = Account\.new\(account_params\)/, m assert_match /@account\.save/, m assert_match /format\.html \{ redirect_to @account, notice: 'Account was successfully created\.' \}/, m assert_match /format\.json \{ render json: @account, status: :created \}/, m assert_match /format\.html \{ render action: 'new' \}/, m assert_match /format\.json \{ render json: @account\.errors, status: :unprocessable_entity \}/, m end assert_instance_method :update, content do |m| assert_match /format\.html \{ redirect_to @account, notice: 'Account was successfully updated\.' \}/, m assert_match /format\.json \{ head :no_content \}/, m assert_match /format\.html \{ render action: 'edit' \}/, m assert_match /format\.json \{ render json: @account.errors, status: :unprocessable_entity \}/, m end assert_instance_method :destroy, content do |m| assert_match /@account\.destroy/, m assert_match /format\.html { redirect_to accounts_url \}/, m assert_match /format\.json \{ head :no_content \}/, m end assert_match(/def account_params/, content) assert_match(/params\.require\(:account\)\.permit\(:name, :description, :business_id\)/, content) end end end active_model_serializers-0.9.7/test/integration/generators/serializer_generator_test.rb000066400000000000000000000030041310165616500320550ustar00rootroot00000000000000require 'test_helper' require 'rails' require 'active_model/serializer/railtie' require 'test_app' class SerializerGeneratorTest < Rails::Generators::TestCase destination File.expand_path('../../../tmp', __FILE__) setup :prepare_destination tests Rails::Generators::SerializerGenerator arguments %w(account name:string description:text business:references) def test_generates_a_serializer_with_attributes_and_associations run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ do |serializer| assert_match(/attributes :id, :name, :description/, serializer) assert_match(/has_one :business/, serializer) end end def test_generates_a_namespaced_serializer run_generator ['admin/account'] assert_file 'app/serializers/admin/account_serializer.rb', /class Admin::AccountSerializer < ActiveModel::Serializer/ end def test_uses_application_serializer_if_one_exists Object.const_set(:ApplicationSerializer, Class.new) run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ApplicationSerializer/ ensure Object.send :remove_const, :ApplicationSerializer end def test_uses_given_parent Object.const_set(:ApplicationSerializer, Class.new) run_generator ['Account', '--parent=MySerializer'] assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < MySerializer/ ensure Object.send :remove_const, :ApplicationSerializer end end active_model_serializers-0.9.7/test/test_app.rb000066400000000000000000000006071310165616500217300ustar00rootroot00000000000000class TestApp < Rails::Application if Rails.version.to_s.first >= '4' config.eager_load = false config.secret_key_base = 'abc123' end config.after_initialize do Rails.application.routes.default_url_options = { host: 'http://example.com' } end # Set up a logger to avoid creating a log directory on every run. config.logger = Logger.new(nil) end TestApp.initialize! active_model_serializers-0.9.7/test/test_helper.rb000066400000000000000000000011631310165616500224250ustar00rootroot00000000000000require 'bundler/setup' require 'minitest/autorun' require 'active_model_serializers' require 'fixtures/poro' # Ensure backward compatibility with Minitest 4 Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) module TestHelper Routes = ActionDispatch::Routing::RouteSet.new Routes.draw do get ':controller(/:action(/:id))' get ':controller(/:action)' end ActionController::Base.send :include, Routes.url_helpers ActionController::Base.send :include, ActionController::Serialization end ActionController::TestCase.class_eval do def setup @routes = TestHelper::Routes end end active_model_serializers-0.9.7/test/unit/000077500000000000000000000000001310165616500205405ustar00rootroot00000000000000active_model_serializers-0.9.7/test/unit/active_model/000077500000000000000000000000001310165616500231735ustar00rootroot00000000000000active_model_serializers-0.9.7/test/unit/active_model/array_serializer/000077500000000000000000000000001310165616500265425ustar00rootroot00000000000000active_model_serializers-0.9.7/test/unit/active_model/array_serializer/except_test.rb000066400000000000000000000011541310165616500314170ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class ExceptTest < Minitest::Test def test_array_serializer_pass_except_to_items_serializers array = [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array, except: [:description]) expected = [{ name: 'Name 1' }, { name: 'Name 2' }] assert_equal expected, serializer.serializable_array end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/key_format_test.rb000066400000000000000000000012001310165616500322570ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class KeyFormatTest < Minitest::Test def test_array_serializer_pass_options_to_items_serializers array = [WebLog.new({ name: 'Name 1', display_name: 'Display Name 1'}), WebLog.new({ name: 'Name 2', display_name: 'Display Name 2'})] serializer = ArraySerializer.new(array, key_format: :lower_camel) expected = [{ name: 'Name 1', displayName: 'Display Name 1' }, { name: 'Name 2', displayName: 'Display Name 2' }] assert_equal expected, serializer.serializable_array end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/meta_test.rb000066400000000000000000000025161310165616500310600ustar00rootroot00000000000000require 'test_helper' require 'active_model/serializer' module ActiveModel class ArraySerializer class MetaTest < Minitest::Test def setup @profile1 = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile2 = Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }) @serializer = ArraySerializer.new([@profile1, @profile2], root: 'profiles') end def test_meta @serializer.meta = { total: 10 } assert_equal({ 'profiles' => [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ], meta: { total: 10 } }, @serializer.as_json) end def test_meta_using_meta_key @serializer.meta_key = :my_meta @serializer.meta = { total: 10 } assert_equal({ 'profiles' => [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ], my_meta: { total: 10 } }, @serializer.as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/only_test.rb000066400000000000000000000011371310165616500311110ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class OnlyTest < Minitest::Test def test_array_serializer_pass_only_to_items_serializers array = [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array, only: [:name]) expected = [{ name: 'Name 1' }, { name: 'Name 2' }] assert_equal expected, serializer.serializable_array end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/options_test.rb000066400000000000000000000010371310165616500316220ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class OptionsTest < Minitest::Test def test_custom_options_are_accessible_from_serializer array = [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array, only: [:name], context: {foo: :bar}) assert_equal({foo: :bar}, serializer.context) end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/root_test.rb000066400000000000000000000065731310165616500311240ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class RootAsOptionTest < Minitest::Test def setup @old_root = ArraySerializer._root @profile1 = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile2 = Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }) @serializer = ArraySerializer.new([@profile1, @profile2], root: :initialize) end def teardown ArraySerializer._root = @old_root end def test_root_is_not_displayed_using_serializable_array assert_equal([ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ], @serializer.serializable_array) end def test_root_using_as_json assert_equal({ initialize: [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] }, @serializer.as_json) end def test_root_as_argument_takes_precedence assert_equal({ argument: [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] }, @serializer.as_json(root: :argument)) end def test_using_false_root_in_initialize_takes_precedence ArraySerializer._root = 'root' @serializer = ArraySerializer.new([@profile1, @profile2], root: false) assert_equal([ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ], @serializer.as_json) end end class RootInSerializerTest < Minitest::Test def setup @old_root = ArraySerializer._root ArraySerializer._root = :in_serializer @profile1 = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile2 = Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }) @serializer = ArraySerializer.new([@profile1, @profile2]) @rooted_serializer = ArraySerializer.new([@profile1, @profile2], root: :initialize) end def teardown ArraySerializer._root = @old_root end def test_root_is_not_displayed_using_serializable_hash assert_equal([ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ], @serializer.serializable_array) end def test_root_using_as_json assert_equal({ in_serializer: [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] }, @serializer.as_json) end def test_root_in_initializer_takes_precedence assert_equal({ initialize: [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] }, @rooted_serializer.as_json) end def test_root_as_argument_takes_precedence assert_equal({ argument: [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] }, @rooted_serializer.as_json(root: :argument)) end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/scope_test.rb000066400000000000000000000013631310165616500312420ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class ScopeTest < Minitest::Test def test_array_serializer_pass_options_to_items_serializers array = [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array, scope: current_user) expected = [{ name: 'Name 1', description: 'Description 1 - user' }, { name: 'Name 2', description: 'Description 2 - user' }] assert_equal expected, serializer.serializable_array end private def current_user 'user' end end end end active_model_serializers-0.9.7/test/unit/active_model/array_serializer/serialization_test.rb000066400000000000000000000157511310165616500330140ustar00rootroot00000000000000require 'test_helper' module ActiveModel class ArraySerializer class BasicObjectsSerializationTest < Minitest::Test def setup array = [1, 2, 3] @serializer = Serializer.serializer_for(array).new(array) end def test_serializer_for_array_returns_appropriate_type assert_kind_of ActiveModel::ArraySerializer, @serializer end def test_array_serializer_serializes_simple_objects assert_equal [1, 2, 3], @serializer.serializable_array assert_equal [1, 2, 3], @serializer.as_json end end class CustomArraySerializerSupport < Minitest::Test def setup Object.const_set(:ArraySerializer, Class.new) array = [1, 2, 3] @serializer_class = Serializer.serializer_for(array) end def teardown Object.send(:remove_const, :ArraySerializer) end def test_serializer_for_array_returns_appropriate_type assert_equal ::ArraySerializer, @serializer_class end end class CustomSerializerClassTest < Minitest::Test def setup Object.const_set(:CustomSerializer, Class.new) end def teardown Object.send(:remove_const, :CustomSerializer) end def test_serializer_for_array_returns_appropriate_type object = {} def object.serializer_class; CustomSerializer; end assert_equal CustomSerializer, Serializer.serializer_for(object) end end class ModelSerializationTest < Minitest::Test def test_array_serializer_serializes_models array = [Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array) expected = [{ name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' }] assert_equal expected, serializer.serializable_array assert_equal expected, serializer.as_json end def test_array_serializers_each_serializer array = [::Model.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), ::Model.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' })] serializer = ArraySerializer.new(array, each_serializer: ProfileSerializer) expected = [{ name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' }] assert_equal expected, serializer.serializable_array assert_equal expected, serializer.as_json end def test_associated_objects_of_multiple_instances_embedded_in_root @association = PostSerializer._associations[:comments] @old_association = @association.dup @association.embed = :ids @association.embed_in_root = true @post1 = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' }) @post2 = Post.new({ title: 'Title 2', body: 'Body 2', date: '1/1/2000' }) class << @post2 attr_writer :comments end @post2.comments = [ Comment.new(content: 'C3'), Comment.new(content: 'C4') ] @serializer = ArraySerializer.new([@post1, @post2], root: :posts) assert_equal({ posts: [ {title: "Title 1", body: "Body 1", "comment_ids" => @post1.comments.map(&:object_id) }, {title: "Title 2", body: "Body 2", "comment_ids" => @post2.comments.map(&:object_id) } ], 'comments' => [ {content: "C1"}, {content: "C2"}, {content: "C3"}, {content: "C4"} ] }, @serializer.as_json) ensure PostSerializer._associations[:comments] = @old_association end def test_embed_object_for_has_one_association_with_nil_value @association = UserSerializer._associations[:profile] @old_association = @association.dup @association.embed = :objects @user1 = User.new({ name: 'User 1', email: 'email1@server.com' }) @user2 = User.new({ name: 'User 2', email: 'email2@server.com' }) class << @user1 def profile nil end end class << @user2 def profile @profile ||= Profile.new(name: 'Name 1', description: 'Desc 1') end end @serializer = ArraySerializer.new([@user1, @user2]) #, root: :posts) assert_equal([ { name: "User 1", email: "email1@server.com", profile: nil }, { name: "User 2", email: "email2@server.com", profile: { name: 'Name 1', description: 'Desc 1' } } ], @serializer.as_json) ensure UserSerializer._associations[:profile] = @old_association end def test_embed_object_in_root_for_has_one_association_with_nil_value @association = UserSerializer._associations[:profile] @old_association = @association.dup @association.embed = :ids @association.embed_in_root = true @user1 = User.new({ name: 'User 1', email: 'email1@server.com' }) @user2 = User.new({ name: 'User 2', email: 'email2@server.com' }) class << @user1 def profile nil end end class << @user2 def profile @profile ||= Profile.new(name: 'Name 1', description: 'Desc 1') end end @serializer = ArraySerializer.new([@user1, @user2], root: :users) assert_equal({ users: [ { name: "User 1", email: "email1@server.com", 'profile_id' => nil }, { name: "User 2", email: "email2@server.com", 'profile_id' => @user2.profile.object_id } ], 'profiles' => [ { name: 'Name 1', description: 'Desc 1' } ] }, @serializer.as_json) ensure UserSerializer._associations[:profile] = @old_association end def test_embed_object_in_root_for_has_one_association_with_all_nil_values @association = UserSerializer._associations[:profile] @old_association = @association.dup @association.embed = :ids @association.embed_in_root = true @user1 = User.new({ name: 'User 1', email: 'email1@server.com' }) @user2 = User.new({ name: 'User 2', email: 'email2@server.com' }) class << @user1 def profile nil end end class << @user2 def profile nil end end @serializer = ArraySerializer.new([@user1, @user2], root: :users) assert_equal({ users: [ { name: "User 1", email: "email1@server.com", 'profile_id' => nil }, { name: "User 2", email: "email2@server.com", 'profile_id' => nil } ] }, @serializer.as_json) ensure UserSerializer._associations[:profile] = @old_association end end end end active_model_serializers-0.9.7/test/unit/active_model/default_serializer_test.rb000066400000000000000000000005751310165616500304430ustar00rootroot00000000000000require 'test_helper' module ActiveModel class DefaultSerializer class Test < Minitest::Test def test_serialize_objects assert_equal(nil, DefaultSerializer.new(nil).serializable_object) assert_equal(1, DefaultSerializer.new(1).serializable_object) assert_equal('hi', DefaultSerializer.new('hi').serializable_object) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/000077500000000000000000000000001310165616500253445ustar00rootroot00000000000000active_model_serializers-0.9.7/test/unit/active_model/serializer/associations/000077500000000000000000000000001310165616500300435ustar00rootroot00000000000000build_serializer_test.rb000066400000000000000000000022241310165616500347000ustar00rootroot00000000000000active_model_serializers-0.9.7/test/unit/active_model/serializer/associationsrequire 'test_helper' module ActiveModel class Serializer class Association class BuildSerializerTest < Minitest::Test def setup @association = Association::HasOne.new('post', serializer: PostSerializer) @post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' }) @user = User.new end def test_build_serializer_for_array_called_twice 2.times do serializer = @association.build_serializer(@post) assert_instance_of(PostSerializer, serializer) end end def test_build_serializer_from_in_a_namespace assoc = Association::HasOne.new('profile') serializer = TestNamespace::UserSerializer.new(@user).build_serializer(assoc) assert_instance_of(TestNamespace::ProfileSerializer, serializer) end def test_build_serializer_with_prefix assoc = Association::HasOne.new('profile', prefix: :short) serializer = UserSerializer.new(@user).build_serializer(assoc) assert_instance_of(ShortProfileSerializer, serializer) end end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/associations_test.rb000066400000000000000000000027171310165616500314360ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class AssociationsTest < Minitest::Test def test_associations_inheritance inherited_serializer_klass = Class.new(PostSerializer) do has_many :users end another_inherited_serializer_klass = Class.new(PostSerializer) assert_equal([:comments, :users], inherited_serializer_klass._associations.keys) assert_equal([:comments], another_inherited_serializer_klass._associations.keys) end def test_multiple_nested_associations parent = SelfReferencingUserParent.new(name: "The Parent") child = SelfReferencingUser.new(name: "The child", parent: parent) self_referencing_user_serializer = SelfReferencingUserSerializer.new(child) result = self_referencing_user_serializer.as_json expected_result = { "self_referencing_user"=>{ :name=>"The child", "type_id"=>child.type.object_id, "parent_id"=>child.parent.object_id }, "types"=>[ { :name=>"N1", }, { :name=>"N2", } ], "parents"=>[ { :name=>"N1", "type_id"=>child.parent.type.object_id, "parent_id"=>nil } ] } assert_equal(expected_result, result) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/attributes_test.rb000066400000000000000000000032441310165616500311210ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class AttributesTest < Minitest::Test def setup @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile_serializer = ProfileSerializer.new(@profile) end def test_attributes_definition assert_equal([:name, :description], @profile_serializer.class._attributes) end def test_attributes_serialization_using_serializable_hash assert_equal({ name: 'Name 1', description: 'Description 1' }, @profile_serializer.serializable_hash) end def test_attributes_serialization_using_as_json assert_equal({ 'profile' => { name: 'Name 1', description: 'Description 1' } }, @profile_serializer.as_json) end def test_attributes_inheritance inherited_serializer_klass = Class.new(ProfileSerializer) do attributes :comments end another_inherited_serializer_klass = Class.new(ProfileSerializer) assert_equal([:name, :description, :comments], inherited_serializer_klass._attributes) assert_equal([:name, :description], another_inherited_serializer_klass._attributes) end def tests_query_attributes_strip_question_mark model = Class.new(::Model) do def strip? true end end serializer = Class.new(ActiveModel::Serializer) do attributes :strip? end actual = serializer.new(model.new).as_json assert_equal({ strip: true }, actual) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/config_test.rb000066400000000000000000000044771310165616500302110ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class Config class Test < Minitest::Test def test_config_const_is_an_instance_of_config assert_kind_of Config, CONFIG end def test_config_instance config = Config.new config.setting1 = 'value1' assert_equal 'value1', config.setting1 end def test_each_config config = Config.new config.setting1 = 'value1' config.setting2 = 'value2' actual = {} config.each do |k, v| actual[k] = v end assert_equal({ 'setting1' => 'value1', 'setting2' => 'value2' }, actual) end end class ConfigTest < Minitest::Test def test_setup Serializer.setup do |config| config.a = 'v1' config.b = 'v2' end assert_equal 'v1', CONFIG.a assert_equal 'v2', CONFIG.b ensure CONFIG.clear end def test_config_accessors Serializer.setup do |config| config.foo = 'v1' config.bar = 'v2' end assert_equal 'v1', CONFIG.foo assert_equal 'v2', CONFIG.bar ensure CONFIG.clear end def test_acessor_when_nil assert_nil CONFIG.foo CONFIG.foo = 1 assert_equal 1, CONFIG.foo assert_nil CONFIG.bar end end class ApplyConfigTest < Minitest::Test def test_apply_config_to_associations CONFIG.embed = :ids CONFIG.embed_in_root = true CONFIG.key_format = :lower_camel association = PostSerializer._associations[:comments] old_association = association.dup association.send :initialize, association.name, association.options assert association.embed_ids? assert !association.embed_objects? assert association.embed_in_root assert_equal :lower_camel, association.key_format assert_equal 'post', PostSerializer.root_name CONFIG.plural_default_root = true assert_equal 'posts', PostSerializer.root_name ensure PostSerializer._associations[:comments] = old_association CONFIG.clear end end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/filter_test.rb000066400000000000000000000040271310165616500302200ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class FilterOptionsTest < Minitest::Test def setup @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end def test_only_option @profile_serializer = ProfileSerializer.new(@profile, only: :name) assert_equal({ 'profile' => { name: 'Name 1' } }, @profile_serializer.as_json) end def test_except_option @profile_serializer = ProfileSerializer.new(@profile, except: :comments) assert_equal({ 'profile' => { name: 'Name 1', description: 'Description 1' } }, @profile_serializer.as_json) end end class FilterAttributesTest < Minitest::Test def setup @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile_serializer = ProfileSerializer.new(@profile) @profile_serializer.instance_eval do def filter(keys) keys - [:description] end end end def test_filtered_attributes_serialization assert_equal({ 'profile' => { name: 'Name 1' } }, @profile_serializer.as_json) end end class FilterAssociationsTest < Minitest::Test def setup @association = PostSerializer._associations[:comments] @old_association = @association.dup @association.embed = :ids @association.embed_in_root = true @post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' }) @post_serializer = PostSerializer.new(@post) @post_serializer.instance_eval do def filter(keys) keys - [:body, :comments] end end end def teardown PostSerializer._associations[:comments] = @old_association end def test_filtered_associations_serialization assert_equal({ 'post' => { title: 'Title 1' } }, @post_serializer.as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/has_many_polymorphic_test.rb000066400000000000000000000130361310165616500331570ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class HasManyPolymorphicTest < ActiveModel::TestCase def setup @association = MailSerializer._associations[:attachments] @old_association = @association.dup @mail = Mail.new({ body: 'Body 1' }) @mail_serializer = MailSerializer.new(@mail) end def teardown MailSerializer._associations[:attachments] = @old_association end def model_name(object) object.class.to_s.demodulize.underscore.to_sym end def test_associations_definition assert_equal 1, MailSerializer._associations.length assert_kind_of Association::HasMany, @association assert_equal true, @association.polymorphic assert_equal 'attachments', @association.name end def test_associations_embedding_ids_serialization_using_serializable_hash @association.embed = :ids assert_equal({ body: 'Body 1', 'attachment_ids' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end }, @mail_serializer.serializable_hash) end def test_associations_embedding_ids_serialization_using_as_json @association.embed = :ids assert_equal({ 'mail' => { :body => 'Body 1', 'attachment_ids' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end } }, @mail_serializer.as_json) end def test_associations_embedding_ids_serialization_using_serializable_hash_and_key_from_options @association.embed = :ids @association.key = 'key' assert_equal({ body: 'Body 1', 'key' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end }, @mail_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_serializable_hash @association.embed = :objects assert_equal({ body: 'Body 1', :attachments => [ { type: :image, image: { url: 'U1' }}, { type: :video, video: { html: 'H1' }} ] }, @mail_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_as_json @association.embed = :objects assert_equal({ 'mail' => { body: 'Body 1', attachments: [ { type: :image, image: { url: 'U1' }}, { type: :video, video: { html: 'H1' }} ] } }, @mail_serializer.as_json) end def test_associations_embedding_nil_objects_serialization_using_as_json @association.embed = :objects @mail.instance_eval do def attachments [nil] end end assert_equal({ 'mail' => { :body => 'Body 1', :attachments => [nil] } }, @mail_serializer.as_json) end def test_associations_embedding_objects_serialization_using_serializable_hash_and_root_from_options @association.embed = :objects @association.embedded_key = 'root' assert_equal({ body: 'Body 1', 'root' => [ { type: :image, image: { url: 'U1' }}, { type: :video, video: { html: 'H1' }} ] }, @mail_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_serializable_hash @association.embed = :ids @association.embed_in_root = true assert_equal({ body: 'Body 1', 'attachment_ids' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end }, @mail_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_as_json @association.embed = :ids @association.embed_in_root = true assert_equal({ 'mail' => { body: 'Body 1', 'attachment_ids' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end, }, 'attachments' => [ { type: :image, image: { url: 'U1' }}, { type: :video, video: { html: 'H1' }} ] }, @mail_serializer.as_json) end def test_associations_embedding_nothing_including_objects_serialization_using_as_json @association.embed = nil @association.embed_in_root = true assert_equal({ 'mail' => { body: 'Body 1' }, 'attachments' => [ { type: :image, image: { url: 'U1' }}, { type: :video, video: { html: 'H1' }} ] }, @mail_serializer.as_json) end def test_associations_using_a_given_serializer @association.embed = :ids @association.embed_in_root = true @association.serializer_from_options = Class.new(ActiveModel::Serializer) do def fake 'fake' end attributes :fake end assert_equal({ 'mail' => { body: 'Body 1', 'attachment_ids' => @mail.attachments.map do |c| { id: c.object_id, type: model_name(c) } end }, 'attachments' => [ { type: :image, image: { fake: 'fake' }}, { type: :video, video: { fake: 'fake' }} ] }, @mail_serializer.as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/has_many_test.rb000066400000000000000000000221621310165616500305320ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class HasManyTest < Minitest::Test def setup @association = PostSerializer._associations[:comments] @old_association = @association.dup @post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' }) @post_serializer = PostSerializer.new(@post) end def teardown PostSerializer._associations[:comments] = @old_association end def test_associations_definition assert_equal 1, PostSerializer._associations.length assert_kind_of Association::HasMany, @association assert_equal 'comments', @association.name end def test_associations_inheritance inherited_serializer_klass = Class.new(PostSerializer) do has_many :some_associations end another_inherited_serializer_klass = Class.new(PostSerializer) assert_equal(PostSerializer._associations.length + 1, inherited_serializer_klass._associations.length) assert_equal(PostSerializer._associations.length, another_inherited_serializer_klass._associations.length) end def test_associations_embedding_ids_serialization_using_serializable_hash @association.embed = :ids assert_equal({ title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end def test_associations_embedding_ids_serialization_using_as_json @association.embed = :ids assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } } }, @post_serializer.as_json) end def test_associations_embedding_ids_serialization_using_serializable_hash_and_key_from_options @association.embed = :ids @association.key = 'key' assert_equal({ title: 'Title 1', body: 'Body 1', 'key' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_serializable_hash @association.embed = :objects assert_equal({ title: 'Title 1', body: 'Body 1', comments: [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_as_json @association.embed = :objects assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', comments: [{ content: 'C1' }, { content: 'C2' }] } }, @post_serializer.as_json) end def test_associations_embedding_nil_objects_serialization_using_as_json @association.embed = :objects @post.instance_eval do def comments [nil] end end assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', comments: [nil] } }, @post_serializer.as_json) end def test_associations_embedding_objects_serialization_using_serializable_hash_and_root_from_options @association.embed = :objects @association.embedded_key = 'root' assert_equal({ title: 'Title 1', body: 'Body 1', 'root' => [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_serializable_hash @association.embed = :ids @association.embed_in_root = true assert_equal({ title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_as_json @association.embed = :ids @association.embed_in_root = true assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, 'comments' => [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.as_json) end def test_associations_embedding_ids_including_objects_serialization_when_invoked_from_parent_serializer @association.embed = :ids @association.embed_in_root = true category = Category.new(name: 'Name 1') category.instance_variable_set(:@posts, [@post]) category_serializer = CategorySerializer.new(category) assert_equal({ 'category' => { name: 'Name 1', posts: [{ title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }] }, "comments" => [{ content: 'C1' }, { content: 'C2' }] }, category_serializer.as_json) end def test_associations_embedding_nothing_including_objects_serialization_using_as_json @association.embed = nil @association.embed_in_root = true assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1' }, 'comments' => [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.as_json) end def test_associations_using_a_given_serializer @association.embed = :ids @association.embed_in_root = true @association.serializer_from_options = Class.new(Serializer) do def content object.read_attribute_for_serialization(:content) + '!' end attributes :content end assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, 'comments' => [{ content: 'C1!' }, { content: 'C2!' }] }, @post_serializer.as_json) end def test_associations_embedding_ids_using_a_given_array_serializer @association.embed = :ids @association.embed_in_root = true @association.serializer_from_options = Class.new(ArraySerializer) do def serializable_object { my_content: ['fake'] } end end assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, 'comments' => { my_content: ['fake'] } }, @post_serializer.as_json) end def test_associations_embedding_objects_using_a_given_array_serializer @association.serializer_from_options = Class.new(ArraySerializer) do def serializable_object(options={}) { my_content: ['fake'] } end end assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', comments: { my_content: ['fake'] } } }, @post_serializer.as_json) end def test_associations_embedding_ids_including_objects_serialization_with_embed_in_root_key @association.embed_in_root = true @association.embed_in_root_key = :linked @association.embed = :ids assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map(&:object_id) }, linked: { 'comments' => [ { content: 'C1' }, { content: 'C2' } ] }, }, @post_serializer.as_json) end def test_associations_embedding_ids_using_embed_namespace_including_object_serialization_with_embed_in_root_key @association.embed_in_root = true @association.embed_in_root_key = :linked @association.embed = :ids @association.embed_namespace = :links @association.key = :comments assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', links: { comments: @post.comments.map(&:object_id) } }, linked: { 'comments' => [ { content: 'C1' }, { content: 'C2' } ] }, }, @post_serializer.as_json) end def test_associations_embedding_objects_using_embed_namespace @association.embed = :objects @association.embed_namespace = :links assert_equal({ 'post' => { title: 'Title 1', body: 'Body 1', links: { comments: [ { content: 'C1' }, { content: 'C2' } ] } } }, @post_serializer.as_json) end def test_associations_name_key_embedding_ids_serialization_using_serializable_hash @association = NameKeyPostSerializer._associations[:comments] @association.embed = :ids assert_equal({ title: 'Title 1', body: 'Body 1', 'comments' => @post.comments.map { |c| c.object_id } }, NameKeyPostSerializer.new(@post).serializable_hash) end def test_associations_name_key_embedding_ids_serialization_using_as_json @association = NameKeyPostSerializer._associations[:comments] @association.embed = :ids assert_equal({ 'name_key_post' => { title: 'Title 1', body: 'Body 1', 'comments' => @post.comments.map { |c| c.object_id } } }, NameKeyPostSerializer.new(@post).as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/has_one_and_has_many_test.rb000066400000000000000000000014001310165616500330400ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class HasOneAndHasManyTest < Minitest::Test def setup @post = SpecialPost.new({ title: 'T1', body: 'B1'}) @post_serializer = SpecialPostSerializer.new(@post) end def teardown end def test_side_load_has_one_and_has_many_in_same_array assert_equal({ "post" => { title: 'T1', body: 'B1', 'comment_ids' => @post.comments.map { |c| c.object_id }, 'special_comment_id' => @post_serializer.special_comment.object_id, }, "comments" => [{ content: 'C1' }, { content: 'C2' }, { content: 'special' }] }, @post_serializer.as_json(root: 'post')) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/has_one_polymorphic_test.rb000066400000000000000000000134041310165616500327730ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class HasOnePolymorphicTest < ActiveModel::TestCase def setup @association = InterviewSerializer._associations[:attachment] @old_association = @association.dup @interview = Interview.new({ text: 'Text 1' }) @interview_serializer = InterviewSerializer.new(@interview) end def teardown InterviewSerializer._associations[:attachment] = @old_association end def model_name(object) object.class.to_s.demodulize.underscore.to_sym end def test_associations_definition assert_equal 1, InterviewSerializer._associations.length assert_kind_of Association::HasOne, @association assert_equal true, @association.polymorphic assert_equal 'attachment', @association.name end def test_associations_embedding_ids_serialization_using_serializable_hash @association.embed = :ids assert_equal({ text: 'Text 1', 'attachment_id' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } }, @interview_serializer.serializable_hash) end def test_associations_embedding_ids_serialization_using_as_json @association.embed = :ids assert_equal({ 'interview' => { text: 'Text 1', 'attachment_id' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } } }, @interview_serializer.as_json) end def test_associations_embedding_ids_serialization_using_serializable_hash_and_key_from_options @association.embed = :ids @association.key = 'key' assert_equal({ text: 'Text 1', 'key' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } }, @interview_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_serializable_hash @association.embed = :objects assert_equal({ text: 'Text 1', attachment: { type: model_name(@interview.attachment), model_name(@interview.attachment) => { url: 'U1'} } }, @interview_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_as_json @association.embed = :objects assert_equal({ 'interview' => { text: 'Text 1', attachment: { type: model_name(@interview.attachment), model_name(@interview.attachment) => { url: 'U1'} } } }, @interview_serializer.as_json) end def test_associations_embedding_nil_ids_serialization_using_as_json @association.embed = :ids @interview.instance_eval do def attachment nil end end assert_equal({ 'interview' => { text: 'Text 1', 'attachment_id' => nil } }, @interview_serializer.as_json) end def test_associations_embedding_nil_objects_serialization_using_as_json @association.embed = :objects @interview.instance_eval do def attachment nil end end assert_equal({ 'interview' => { text: 'Text 1', attachment: nil } }, @interview_serializer.as_json) end def test_associations_embedding_objects_serialization_using_serializable_hash_and_root_from_options @association.embed = :objects @association.embedded_key = 'root' assert_equal({ text: 'Text 1', 'root' => { type: model_name(@interview.attachment), model_name(@interview.attachment) => { url: 'U1'} } }, @interview_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_serializable_hash @association.embed = :ids @association.embed_in_root = true assert_equal({ text: 'Text 1', 'attachment_id' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } }, @interview_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_as_json @association.embed = :ids @association.embed_in_root = true assert_equal({ 'interview' => { text: 'Text 1', 'attachment_id' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } }, "attachments" => [{ type: model_name(@interview.attachment), model_name(@interview.attachment) => { url: 'U1' } }] }, @interview_serializer.as_json) end def test_associations_using_a_given_serializer @association.embed = :ids @association.embed_in_root = true @association.serializer_from_options = Class.new(ActiveModel::Serializer) do def name 'fake' end attributes :name end assert_equal({ 'interview' => { text: 'Text 1', 'attachment_id' => { type: model_name(@interview.attachment), id: @interview.attachment.object_id } }, "attachments" => [{ type: model_name(@interview.attachment), model_name(@interview.attachment) => { name: 'fake' } }] }, @interview_serializer.as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/has_one_test.rb000066400000000000000000000204311310165616500303440ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class HasOneTest < Minitest::Test def setup @association = UserSerializer._associations[:profile] @old_association = @association.dup @user = User.new({ name: 'Name 1', email: 'mail@server.com', gender: 'M' }) @user_serializer = UserSerializer.new(@user) end def teardown UserSerializer._associations[:profile] = @old_association end def test_associations_definition assert_equal 1, UserSerializer._associations.length assert_kind_of Association::HasOne, @association assert_equal 'profile', @association.name end def test_associations_embedding_ids_serialization_using_serializable_hash @association.embed = :ids assert_equal({ name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id }, @user_serializer.serializable_hash) end def test_associations_embedding_ids_serialization_using_as_json @association.embed = :ids assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id } }, @user_serializer.as_json) end def test_associations_embedding_ids_serialization_using_serializable_hash_and_key_from_options @association.embed = :ids @association.key = 'key' assert_equal({ name: 'Name 1', email: 'mail@server.com', 'key' => @user.profile.object_id }, @user_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_serializable_hash @association.embed = :objects assert_equal({ name: 'Name 1', email: 'mail@server.com', profile: { name: 'N1', description: 'D1' } }, @user_serializer.serializable_hash) end def test_associations_embedding_objects_serialization_using_as_json @association.embed = :objects assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', profile: { name: 'N1', description: 'D1' } } }, @user_serializer.as_json) end def test_associations_embedding_nil_ids_serialization_using_as_json @association.embed = :ids @user.instance_eval do def profile nil end end assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => nil } }, @user_serializer.as_json) end def test_associations_embedding_nil_objects_serialization_using_as_json @association.embed = :objects @user.instance_eval do def profile nil end end assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', profile: nil } }, @user_serializer.as_json) end def test_associations_embedding_objects_serialization_using_serializable_hash_and_root_from_options @association.embed = :objects @association.embedded_key = 'root' assert_equal({ name: 'Name 1', email: 'mail@server.com', 'root' => { name: 'N1', description: 'D1' } }, @user_serializer.serializable_hash) end def test_associations_embedding_ids_including_objects_serialization_using_serializable_hash @association.embed = :ids @association.embed_in_root = true assert_equal({ name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id }, @user_serializer.serializable_hash) end def test_associations_embedding_nil_ids_including_objects_serialization_using_as_json @association.embed = :ids @association.embed_in_root = true @user.instance_eval do def profile nil end end assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => nil }, 'profiles' => [] }, @user_serializer.as_json) end def test_associations_embedding_ids_including_objects_serialization_using_as_json @association.embed = :ids @association.embed_in_root = true assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id }, 'profiles' => [{ name: 'N1', description: 'D1' }] }, @user_serializer.as_json) end def test_associations_embedding_ids_including_objects_serialization_when_invoked_from_parent_serializer @association.embed = :ids @association.embed_in_root = true user_info = UserInfo.new user_info.instance_variable_set(:@user, @user) user_info_serializer = UserInfoSerializer.new(user_info) assert_equal({ 'user_info' => { user: { name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id } }, 'profiles' => [{ name: 'N1', description: 'D1' }] }, user_info_serializer.as_json) end def test_associations_embedding_ids_using_a_given_serializer @association.embed = :ids @association.embed_in_root = true @association.serializer_from_options = Class.new(Serializer) do def name 'fake' end attributes :name end assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id }, 'profiles' => [{ name: 'fake' }] }, @user_serializer.as_json) end def test_associations_embedding_objects_using_a_given_serializer @association.serializer_from_options = Class.new(Serializer) do def name 'fake' end attributes :name end assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', profile: { name: 'fake' } } }, @user_serializer.as_json) end def test_associations_embedding_objects_with_nil_values user_info = UserInfo.new user_info.instance_eval do def user nil end end user_info_serializer = UserInfoSerializer.new(user_info) assert_equal({ 'user_info' => { user: nil } }, user_info_serializer.as_json) end def test_associations_embedding_ids_using_embed_namespace @association.embed_namespace = :links @association.embed = :ids @association.key = :profile assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', links: { profile: @user.profile.object_id } } }, @user_serializer.as_json) end def test_asociations_embedding_objects_using_embed_namespace @association.embed_namespace = :links @association.embed = :objects assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', links: { profile: { name: 'N1', description: 'D1' } } } }, @user_serializer.as_json) end def test_associations_embedding_ids_using_embed_namespace_and_embed_in_root_key @association.embed_in_root = true @association.embed_in_root_key = :linked @association.embed = :ids assert_equal({ 'user' => { name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id }, linked: { 'profiles' => [ { name: 'N1', description: 'D1' } ] } }, @user_serializer.as_json) end def test_associations_name_key_embedding_ids_serialization_using_serializable_hash @association = NameKeyUserSerializer._associations[:profile] @association.embed = :ids assert_equal({ name: 'Name 1', email: 'mail@server.com', 'profile' => @user.profile.object_id }, NameKeyUserSerializer.new(@user).serializable_hash) end def test_associations_name_key_embedding_ids_serialization_using_as_json @association = NameKeyUserSerializer._associations[:profile] @association.embed = :ids assert_equal({ 'name_key_user' => { name: 'Name 1', email: 'mail@server.com', 'profile' => @user.profile.object_id } }, NameKeyUserSerializer.new(@user).as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/key_format_test.rb000066400000000000000000000014331310165616500310710ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class KeyFormatTest < Minitest::Test def test_lower_camel_format_option object = WebLog.new({ name: 'Name 1', display_name: 'Display Name 1'}) serializer = WebLogSerializer.new(object, key_format: :lower_camel) expected = { name: 'Name 1', displayName: 'Display Name 1' } assert_equal expected, serializer.serializable_object end def test_lower_camel_format_serializer object = WebLog.new({ name: 'Name 1', display_name: 'Display Name 1'}) serializer = WebLogLowerCamelSerializer.new(object) expected = { name: 'Name 1', displayName: 'Display Name 1' } assert_equal expected, serializer.serializable_object end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/meta_test.rb000066400000000000000000000017371310165616500276660ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class MetaTest < Minitest::Test def setup @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) end def test_meta profile_serializer = ProfileSerializer.new(@profile, root: 'profile', meta: { total: 10 }) assert_equal({ 'profile' => { name: 'Name 1', description: 'Description 1' }, meta: { total: 10 } }, profile_serializer.as_json) end def test_meta_using_meta_key profile_serializer = ProfileSerializer.new(@profile, root: 'profile', meta_key: :my_meta, my_meta: { total: 10 }) assert_equal({ 'profile' => { name: 'Name 1', description: 'Description 1' }, my_meta: { total: 10 } }, profile_serializer.as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/options_test.rb000066400000000000000000000025251310165616500304270ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class OptionsTest < Minitest::Test def setup @serializer = ProfileSerializer.new(nil, context: {foo: :bar}) end def test_custom_options_are_accessible_from_serializer assert_equal({foo: :bar}, @serializer.context) end end class SerializationOptionsTest < Minitest::Test def setup @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @profile_serializer = ProfileSerializer.new(@profile) @profile_serializer.instance_eval do def description serialization_options[:force_the_description] end end @category = Category.new({name: 'Category 1'}) @category_serializer = CategorySerializer.new(@category) end def test_filtered_attributes_serialization forced_description = "This is a test" assert_equal({ 'profile' => { name: 'Name 1', description: forced_description } }, @profile_serializer.as_json(force_the_description: forced_description)) end def test_filtered_attributes_serialization_across_association assert_equal("'T1'", @category_serializer.as_json(highlight_keyword: 'T1')['category'][:posts][0][:title]) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/root_test.rb000066400000000000000000000065711310165616500277240ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class RootAsOptionTest < Minitest::Test def setup @old_root = ProfileSerializer._root @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @serializer = ProfileSerializer.new(@profile, root: :initialize) ProfileSerializer._root = true end def teardown ProfileSerializer._root = @old_root end def test_root_is_not_displayed_using_serializable_hash assert_equal({ name: 'Name 1', description: 'Description 1' }, @serializer.serializable_hash) end def test_root_using_as_json assert_equal({ initialize: { name: 'Name 1', description: 'Description 1' } }, @serializer.as_json) end def test_root_from_serializer_name @serializer = ProfileSerializer.new(@profile) assert_equal({ 'profile' => { name: 'Name 1', description: 'Description 1' } }, @serializer.as_json) end def test_root_as_argument_takes_precedence assert_equal({ argument: { name: 'Name 1', description: 'Description 1' } }, @serializer.as_json(root: :argument)) end def test_using_false_root_in_initializer_takes_precedence ProfileSerializer._root = 'root' @serializer = ProfileSerializer.new(@profile, root: false) assert_equal({ name: 'Name 1', description: 'Description 1' }, @serializer.as_json) end def test_root_inheritance ProfileSerializer._root = 'profile' inherited_serializer_klass = Class.new(ProfileSerializer) inherited_serializer_klass._root = 'inherited_profile' another_inherited_serializer_klass = Class.new(ProfileSerializer) assert_equal('inherited_profile', inherited_serializer_klass._root) assert_equal('profile', another_inherited_serializer_klass._root) end end class RootInSerializerTest < Minitest::Test def setup @old_root = ProfileSerializer._root ProfileSerializer._root = :in_serializer profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @serializer = ProfileSerializer.new(profile) @rooted_serializer = ProfileSerializer.new(profile, root: :initialize) end def teardown ProfileSerializer._root = @old_root end def test_root_is_not_displayed_using_serializable_hash assert_equal({ name: 'Name 1', description: 'Description 1' }, @serializer.serializable_hash) end def test_root_using_as_json assert_equal({ in_serializer: { name: 'Name 1', description: 'Description 1' } }, @serializer.as_json) end def test_root_in_initializer_takes_precedence assert_equal({ initialize: { name: 'Name 1', description: 'Description 1' } }, @rooted_serializer.as_json) end def test_root_as_argument_takes_precedence assert_equal({ argument: { name: 'Name 1', description: 'Description 1' } }, @rooted_serializer.as_json(root: :argument)) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/scope_test.rb000066400000000000000000000021741310165616500300450ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class ScopeTest < Minitest::Test def setup @serializer = ProfileSerializer.new(nil, scope: current_user) end def test_scope assert_equal('user', @serializer.scope) end private def current_user 'user' end end class NestedScopeTest < Minitest::Test def setup @association = UserSerializer._associations[:profile] @old_association = @association.dup @user = User.new({ name: 'Name 1', email: 'mail@server.com', gender: 'M' }) @user_serializer = UserSerializer.new(@user, scope: 'user') end def teardown UserSerializer._associations[:profile] = @old_association end def test_scope_passed_through @association.serializer_from_options = Class.new(Serializer) do def name scope end attributes :name end assert_equal({ name: 'Name 1', email: 'mail@server.com', profile: { name: 'user' } }, @user_serializer.serializable_hash) end end end end active_model_serializers-0.9.7/test/unit/active_model/serializer/url_helpers_test.rb000066400000000000000000000015451310165616500312610ustar00rootroot00000000000000require 'test_helper' module ActiveModel class Serializer class UrlHelpersTest < Minitest::Test include Rails.application.routes.url_helpers def setup Object.const_set 'UserController', Class.new(ActionController::Base) do def show render text: 'profile' end end Rails.application.routes.draw do get '/profile/:id', as: :profile, controller: :user, action: :show end end def test_url_helpers_are_available serializer = Class.new(ActiveModel::Serializer) do attributes :url def url profile_url(id: object.object_id) end end profile = Profile.new assert_equal({ url: profile_url(id: profile.object_id) }, serializer.new(profile).as_json) end end end end active_model_serializers-0.9.7/test/unit/active_model/serilizable_test.rb000066400000000000000000000033441310165616500270700ustar00rootroot00000000000000require 'test_helper' module ActiveModel class SerializableTest class InstrumentationTest < Minitest::Test def setup @events = [] @subscriber = ActiveSupport::Notifications.subscribe('!serialize.active_model_serializers') do |name, start, finish, id, payload| @events << { name: name, serializer: payload[:serializer] } end end def teardown ActiveSupport::Notifications.unsubscribe(@subscriber) if defined?(@subscriber) end def test_instruments_default_serializer DefaultSerializer.new(1).as_json assert_equal [{ name: '!serialize.active_model_serializers', serializer: 'ActiveModel::DefaultSerializer' }], @events end def test_instruments_serializer profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') serializer = ProfileSerializer.new(profile) serializer.as_json assert_equal [{ name: '!serialize.active_model_serializers', serializer: 'ProfileSerializer' }], @events end def test_instruments_array_serializer profiles = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2') ] serializer = ArraySerializer.new(profiles) serializer.as_json assert_equal [ { name: '!serialize.active_model_serializers', serializer: 'ProfileSerializer' }, { name: '!serialize.active_model_serializers', serializer: 'ProfileSerializer' }, { name: '!serialize.active_model_serializers', serializer: 'ActiveModel::ArraySerializer' } ], @events end end end end