Compare commits

..

No commits in common. "master" and "v1.4.0" have entirely different histories.

38 changed files with 204 additions and 1448 deletions

View file

@ -1,38 +0,0 @@
name: CI
on: [push, pull_request]
jobs:
danger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 4.0.0
- name: Install dependencies
run: bundle install --jobs 4 --retry 3
- name: Run Danger
run: |
# the token is public, has public_repo scope and belongs to the grape-bot user owned by @dblock, this is ok
TOKEN=$(echo -n Z2hwX2lYb0dPNXNyejYzOFJyaTV3QUxUdkNiS1dtblFwZTFuRXpmMwo= | base64 --decode)
DANGER_GITHUB_API_TOKEN=$TOKEN bundle exec danger --verbose
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: [4.0.0, 3.4.8, 3.3.10, 3.2.9]
grape-version: ['~> 3.0.0', '~> 2.3.0']
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install --jobs 4 --retry 3
env:
GRAPE_VERSION: ${{ matrix.grape-version }}
- name: Run tests
run: bundle exec rake
env:
GRAPE_VERSION: ${{ matrix.grape-version }}

View file

@ -1,13 +1,2 @@
inherit_from: .rubocop_todo.yml
AllCops:
Exclude:
- Guardfile
- grape-active_model_serializers.gemspec
NewCops: enable
Style/BlockDelimiters:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false

View file

@ -1,71 +1,27 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-03-14 14:59:23 -0400 using RuboCop version 0.53.0.
# This configuration was generated by `rubocop --auto-gen-config`
# on 2015-01-13 18:47:14 -0500 using RuboCop version 0.28.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/DuplicatedGem:
Exclude:
- 'Gemfile'
# Offense count: 1
# Offense count: 25
# Configuration parameters: AllowURI, URISchemes.
Metrics/AbcSize:
Max: 18
Max: 20
# Offense count: 23
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 133
Metrics/LineLength:
Max: 179
# Offense count: 1
Metrics/CyclomaticComplexity:
Max: 9
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 15
# Offense count: 1
Metrics/PerceivedComplexity:
Max: 10
# Offense count: 2
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
Naming/FileName:
Exclude:
- 'lib/grape-active_model_serializers.rb'
- 'spec/grape-active_model_serializers_spec.rb'
# Offense count: 5
# Offense count: 7
Style/Documentation:
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/grape-active_model_serializers/endpoint_extension.rb'
- 'lib/grape-active_model_serializers/error_formatter.rb'
- 'lib/grape-active_model_serializers/formatter.rb'
- 'lib/grape-active_model_serializers/options_builder.rb'
- 'lib/grape-active_model_serializers/serializer_resolver.rb'
# Offense count: 1
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'lib/grape-active_model_serializers/serializer_resolver.rb'
Enabled: false
# Offense count: 2
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Layout/LineLength:
Max: 87
# Configuration parameters: Exclude.
Style/FileName:
Enabled: false
Lint/ConstantDefinitionInBlock:
Exclude:
- spec/integration/sequel_spec.rb
# Offense count: 4
Style/RegexpLiteral:
MaxSlashes: 0

36
.travis.yml Normal file
View file

@ -0,0 +1,36 @@
language: ruby
sudo: false
matrix:
include:
- rvm: 2.3.1
env: GRAPE_VERSION=0.8.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.9.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.10.1
- rvm: 2.3.1
env: GRAPE_VERSION=0.12.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.13.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.14.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.15.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.16.2
- rvm: 2.3.1
env: GRAPE_VERSION=HEAD
- rvm: 2.3.0
- rvm: 2.2.5
- rvm: 2.1
- rvm: rbx-2
- rvm: jruby-19mode
- rvm: ruby-head
- rvm: jruby-head
allow_failures:
- rvm: rbx-2
- rvm: jruby-19mode
- rvm: ruby-head
- rvm: jruby-head

View file

@ -1,69 +1,40 @@
## Changelog
### 2.0.2 (Next)
### 1.5.0 (Next)
* Your contribution here.
### 2.0.1 (2025/12/08)
### 1.4.0 (July 14, 2016)
* [#92](https://github.com/ruby-grape/grape-active_model_serializers/pull/92): Fix: accept nil serializer - [@mateusnava](https://github.com/mateusnava).
* [#100](https://github.com/ruby-grape/grape-active_model_serializers/pull/100): Fix compatibility with Grape 3.0 and test in CI - [@samsonjs](https://github.com/samsonjs).
* [#49](https://github.com/jrhe/grape-active_model_serializers/pull/49): Adds support for active model serializer namespace - [@syntaxTerr0r](https://github.com/syntaxTerr0r).
* [#36](https://github.com/jrhe/grape-active_model_serializers/pull/36), [#50](https://github.com/jrhe/grape-active_model_serializers/pull/50): Added support through Grape 0.16.x - [@dblock](https://github.com/dblock).
### 2.0.0 (2025/06/02)
### v1.3.2 (February 27, 2015)
* [#96](https://github.com/ruby-grape/grape-active_model_serializers/pull/96): Add compatibility for Grape 2.3 - [@samsonjs](https://github.com/samsonjs).
* [#98](https://github.com/ruby-grape/grape-active_model_serializers/pull/98): Out with Travis CI, in with GitHub Actions - [@samsonjs](https://github.com/samsonjs).
* [#39](https://github.com/jrhe/grape-active_model_serializers/pull/39): Look for namespace and other options to configure serializers - [@jwkoelewijn](https://github.com/jwkoelewijn).
* [#40](https://github.com/jrhe/grape-active_model_serializers/pull/40): Use env to pass AMS meta around - [@dblock](https://github.com/dblock).
### 1.5.2 (2018/03/14)
### v1.3.1 (November 20, 2014)
* [#76](https://github.com/ruby-grape/grape-active_model_serializers/pull/76): Add custom error formatter - [@xn](https://github.com/xn).
* [#30](https://github.com/jrhe/grape-active_model_serializers/pull/30): Read options from default_serializer_options - [@siong1987](https://github.com/siong1987).
* [#24](https://github.com/jrhe/grape-active_model_serializers/pull/24): Makes it possible to use `current_user` within serializers - [@sonxurxo](https://github.com/sonxurxo).
### 1.5.1 (2017/04/25)
### v1.2.1 (July 23, 2014)
* [#74](https://github.com/ruby-grape/grape-active_model_serializers/pull/74): Add support for Sequel - [@drn](https://github.com/drn).
* [#73](https://github.com/ruby-grape/grape-active_model_serializers/pull/73): Ensure all AMS options are passed through - [@drn](https://github.com/drn).
* [#65](https://github.com/ruby-grape/grape-active_model_serializers/pull/65): Added Danger, PR linter - [@dblock](https://github.com/dblock).
* [#63](https://github.com/ruby-grape/grape-active_model_serializers/pull/63): Pass adapter options through render - [@drn](https://github.com/drn).
* [#21](https://github.com/jrhe/grape-active_model_serializers/pull/21): Correctly fetch serialization scope - [@radanskoric](https://github.com/radanskoric).
* [#18](https://github.com/jrhe/grape-active_model_serializers/pull/18): Added support for active model serializer 0.9.x - [@sbounmy](https://github.com/sbounmy).
### 1.5.0 (2016/08/24)
* [#15](https://github.com/jrhe/grape-active_model_serializers/pull/15): Added `render` syntactic sugar - [@zph](https://github.com/zph).
* [#14](https://github.com/jrhe/grape-active_model_serializers/pull/14): Fix: `default_root` method to support symbol route in Grape - [@wjp2013](https://github.com/wjp2013).
* [#12](https://github.com/jrhe/grape-active_model_serializers/pull/12): Added support for `current_user` - [@kpassapk](https://github.com/kpassapk).
* [#11](https://github.com/jrhe/grape-active_model_serializers/pull/11): Fixed require path - [@schickling](https://github.com/schickling).
* [#61](https://github.com/ruby-grape/grape-active_model_serializers/pull/61): Adds support for collection serializers - [@drn](https://github.com/drn).
* [#60](https://github.com/ruby-grape/grape-active_model_serializers/pull/60): Namespace serializer inference - [@drn](https://github.com/drn).
* [#59](https://github.com/ruby-grape/grape-active_model_serializers/pull/59): Refactor option and serializer resolution - [@drn](https://github.com/drn).
* [#57](https://github.com/ruby-grape/grape-active_model_serializers/pull/57): Solve line length linter issues - [@drn](https://github.com/drn).
* [#54](https://github.com/ruby-grape/grape-active_model_serializers/pull/54): Adding support for ASM v0.10. Drops support for ASM v0.9 - [@drn](https://github.com/drn).
### v1.0.1 (September 9, 2013)
### 1.4.0 (2016/07/14)
* [#6](https://github.com/jrhe/grape-active_model_serializers/pull/6): Conform to ActiveModel::Serializers way of determining array-ness - [@tfe](https://github.com/tfe).
* [#4](https://github.com/jrhe/grape-active_model_serializers/pull/4): Support for namespace options and rely more on active_model_serializers - [@johnallen3d](https://github.com/johnallen3d).
* [#1](https://github.com/jrhe/grape-active_model_serializers/pull/1): Fix: Grape::ActiveModelSerializers for models with compound names - [@george](https://github.com/george).
* [#49](https://github.com/ruby-grape/grape-active_model_serializers/pull/49): Adds support for active model serializer namespace - [@syntaxTerr0r](https://github.com/syntaxTerr0r).
* [#36](https://github.com/ruby-grape/grape-active_model_serializers/pull/36), [#50](https://github.com/jrhe/grape-active_model_serializers/pull/50): Added support through Grape 0.16.x - [@dblock](https://github.com/dblock).
### 1.3.2 (2015/02/27)
* [#40](https://github.com/ruby-grape/grape-active_model_serializers/pull/40): Use env to pass AMS meta around - [@dblock](https://github.com/dblock).
* [#39](https://github.com/ruby-grape/grape-active_model_serializers/pull/39): Look for namespace and other options to configure serializers - [@jwkoelewijn](https://github.com/jwkoelewijn).
### 1.3.1 (2014/11/20)
* [#30](https://github.com/ruby-grape/grape-active_model_serializers/pull/30): Read options from default_serializer_options - [@siong1987](https://github.com/siong1987).
* [#24](https://github.com/ruby-grape/grape-active_model_serializers/pull/24): Makes it possible to use `current_user` within serializers - [@sonxurxo](https://github.com/sonxurxo).
### 1.2.1 (2014/07/23)
* [#21](https://github.com/ruby-grape/grape-active_model_serializers/pull/21): Correctly fetch serialization scope - [@radanskoric](https://github.com/radanskoric).
* [#18](https://github.com/ruby-grape/grape-active_model_serializers/pull/18): Added support for active model serializer 0.9.x - [@sbounmy](https://github.com/sbounmy).
* [#15](https://github.com/ruby-grape/grape-active_model_serializers/pull/15): Added `render` syntactic sugar - [@zph](https://github.com/zph).
* [#14](https://github.com/ruby-grape/grape-active_model_serializers/pull/14): Fix: `default_root` method to support symbol route in Grape - [@wjp2013](https://github.com/wjp2013).
* [#12](https://github.com/ruby-grape/grape-active_model_serializers/pull/12): Added support for `current_user` - [@kpassapk](https://github.com/kpassapk).
* [#11](https://github.com/ruby-grape/grape-active_model_serializers/pull/11): Fixed require path - [@schickling](https://github.com/schickling).
### 1.0.1 (2013/09/09)
* [#6](https://github.com/ruby-grape/grape-active_model_serializers/pull/6): Conform to ActiveModel::Serializers way of determining array-ness - [@tfe](https://github.com/tfe).
* [#4](https://github.com/ruby-grape/grape-active_model_serializers/pull/4): Support for namespace options and rely more on active_model_serializers - [@johnallen3d](https://github.com/johnallen3d).
* [#1](https://github.com/ruby-grape/grape-active_model_serializers/pull/1): Fix: Grape::ActiveModelSerializers for models with compound names - [@george](https://github.com/george).
### 1.0.0 (2013/09/09)
### v1.0.0
* Initial public release - [@jrhe](https://github.com/jrhe).

View file

@ -1 +0,0 @@
danger.import_dangerfile(gem: 'ruby-grape-danger')

17
Gemfile
View file

@ -2,24 +2,9 @@ source 'https://rubygems.org'
gemspec
case version = ENV['GRAPE_VERSION'] || '~> 3.0.0'
case version = ENV['GRAPE_VERSION'] || '~> 0.10.0'
when 'HEAD'
gem 'grape', github: 'intridea/grape'
else
gem 'grape', version
end
group :test do
gem 'rack-test'
gem 'ruby-grape-danger', '~> 0.2.0', require: false
gem 'sequel', '~> 5.91', require: false
gem 'sqlite3'
end
group :development, :test do
gem 'guard-rspec'
gem 'listen', '~> 3.9.0'
gem 'rake'
gem 'rspec'
gem 'rubocop', '1.75.7'
end

View file

@ -1,29 +1,8 @@
# Table of Contents
- [Grape::ActiveModelSerializers](#grapeactivemodelserializers)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Usage](#usage)
- [Require grape-active_model_serializers](#require-grape-active_model_serializers)
- [Tell your API to use Grape::Formatter::ActiveModelSerializers](#tell-your-api-to-use-grapeformatteractivemodelserializers)
- [Writing Serializers](#writing-serializers)
- [Serializers are inferred by active_record model names](#serializers-are-inferred-by-active_record-model-names)
- [Array Roots](#array-roots)
- [API Versioning](#api-versioning)
- [Manually specifying serializer / adapter options](#manually-specifying-serializer--adapter-options)
- [Custom Metadata](#custom-metadata)
- [Default Serializer Options](#default-serializer-options)
- [Current User](#current-user)
- [Full Example](#full-example)
- [Contributing](#contributing)
- [History](#history)
# Grape::ActiveModelSerializers
Use [active_model_serializers](https://github.com/rails-api/active_model_serializers) with [Grape](https://github.com/intridea/grape)!
[![Gem Version](https://badge.fury.io/rb/grape-active_model_serializers.svg)](https://badge.fury.io/rb/grape-active_model_serializers)
[![Build Status](https://github.com/ruby-grape/grape-active_model_serializers/actions/workflows/ci.yml/badge.svg)](https://github.com/ruby-grape/grape-active_model_serializers/actions/workflows/ci.yml) [![Code Climate](https://codeclimate.com/github/ruby-grape/grape-active_model_serializers.svg)](https://codeclimate.com/github/ruby-grape/grape-active_model_serializers)
[![Build Status](https://api.travis-ci.org/ruby-grape/grape-active_model_serializers.svg)](http://travis-ci.org/ruby-grape/grape-active_model_serializers) [![Dependency Status](https://gemnasium.com/ruby-grape/grape-active_model_serializers.svg)](https://gemnasium.com/ruby-grape/grape-active_model_serializers) [![Code Climate](https://codeclimate.com/github/jrhe/grape-active_model_serializers.svg)](https://codeclimate.com/github/jrhe/grape-active_model_serializers)
## Installation
@ -35,12 +14,6 @@ gem 'grape-active_model_serializers'
See [UPGRADING](UPGRADING.md) if you're upgrading from a previous version.
## Dependencies
* >= Ruby v3.1
* >= [grape](https://github.com/ruby-grape/grape) v2.3.0
* >= [active_model_serializers](https://github.com/rails-api/active_model_serializers) v0.10.0
## Usage
### Require grape-active_model_serializers
@ -56,12 +29,6 @@ require 'grape-active_model_serializers'
class API < Grape::API
format :json
formatter :json, Grape::Formatter::ActiveModelSerializers
# Serializes errors with ActiveModel::Serializer::ErrorSerializer if an ActiveModel.
# Serializer conforms to the adapter, ex: json, jsonapi.
# So an error formatted with a jsonapi formatter would render as per:
# http://jsonapi.org/format/#error-objects
error_formatter :json, Grape::Formatter::ActiveModelSerializers
end
```
@ -176,10 +143,10 @@ Or as follows.
ActiveModelSerializer will fetch automatically the right serializer to render.
### Manually specifying serializer / adapter options
### Manually specifying serializer options
```ruby
# Serializer and adapter options can be specified on routes or namespaces.
# Serializer options can be specified on routes or namespaces.
namespace 'foo', serializer: BarSerializer do
get "/" do
# will use "bar" serializer
@ -197,36 +164,6 @@ namespace 'foo', serializer: BarSerializer do
end
```
```ruby
# Serializer and adapter options can also be specified in the body of the route
resource :users do
get '/:id' do
if conditional
# uses UserSerializer and configured default adapter automatically
current_user
else
# uses specified serializer and adapter
render current_user, serializer: ErrorSerializer, adapter: :attributes
end
end
end
```
```ruby
# Adhoc serializer options can be specified in the body of the route
resource :users do
get '/:id' do
render current_user, extra: { adhoc_name_option: 'value' }
end
end
class UserSerializer
def name
instance_options[:adhoc_name_option] # accessible in instance_options
end
end
```
### Custom Metadata
```ruby
@ -303,4 +240,4 @@ See [CONTRIBUTING](CONTRIBUTING.md).
## History
Structured and based upon [grape-rabl](https://github.com/ruby-grape/grape-rabl).
Structured and based upon [grape-rabl](https://github.com/LTe/grape-rabl).

View file

@ -1,70 +0,0 @@
# Releasing Grape::ActiveModelSerializers
There're no particular rules about when to release grape-active_model_serializers. Release bug fixes frequently, features not so frequently and breaking API changes rarely.
### Release
Run tests, check that all tests succeed locally.
```
bundle install
rake
```
Check that the last build succeeded in [GitHub Actions](https://github.com/ruby-grape/grape-active_model_serializers/actions/workflows/ci.yml) for all supported platforms.
Change "Next Release" in [CHANGELOG.md](CHANGELOG.md) to the new version.
```
### 0.7.2 (2014/02/06)
```
Remove the line with "Your contribution here.", since there will be no more contributions to this release.
Commit your changes.
```
git add CHANGELOG.md lib/grape-active_model_serializers/version.rb
git commit -m "Preparing for release, 0.7.2."
git push origin master
```
Release.
```
$ rake release
grape-active_model_serializers 0.7.2 built to pkg/grape-active_model_serializers-0.7.2.gem.
Tagged v0.7.2.
Pushed git commits and tags.
Pushed grape-active_model_serializers 0.7.2 to rubygems.org.
```
### Prepare for the Next Version
Modify [lib/grape-active_model_serializers/version.rb](lib/grape-active_model_serializers/version.rb), increment the third number (eg. change `0.7.2` to `0.7.3`).
```ruby
module Grape
module ActiveModelSerializers
VERSION = '0.7.3'.freeze
end
end
```
Add the next release to [CHANGELOG.md](CHANGELOG.md).
```
#### 0.7.3 (Next)
* Your contribution here.
```
Commit your changes.
```
git add CHANGELOG.md lib/grape-active_model_serializers/version.rb
git commit -m "Preparing for next development iteration, 0.7.3."
git push origin master
```

3
Rakefile Executable file → Normal file
View file

@ -1,5 +1,4 @@
#!/usr/bin/env rake
require 'bundler/gem_tasks'
require 'rspec/core'
@ -12,4 +11,4 @@ end
require 'rubocop/rake_task'
RuboCop::RakeTask.new(:rubocop)
task default: %i[rubocop spec]
task default: [:rubocop, :spec]

View file

@ -1,145 +1,12 @@
### Upgrading to v1.5.0
#### ASM v0.10.x support added, v0.9.x support dropped
[ASM](https://github.com/rails-api/active_model_serializers) introduced
breaking changes with ASM v0.10.x. Support has been introduced in v1.5.0.
If you require support for older version of ASM, please lock your Gemfile
to the relevant version.
#### Non-collection Serializer Resolution
Serializer resolution now uses the following strategy:
1. Defined by the resource
```
class UsersApi < Grape::Api
resource :users do
get '/:id', serializer: SpecifiedUserSerializer do
current_user
end
end
end
class User
def serializer_class
SpecifiedUserSerializer
end
end
```
2. Specified by options
```
class UsersApi < Grape::Api
resource :users do
get '/:id', serializer: SpecifiedUserSerializer do
current_user
end
end
end
```
2. Namespace inferred
```
class V1::UsersApi < Grape::Api
resource :users do
get '/:id' do
current_user
end
end
end
# 'V1::UsersApi'.deconstantize => 'V1'
# searches for serializers in the V1:: namespace with the name UserSerializer
# used
class V1::UserSerializer
...
end
# not used since V1::UserSerializer resolved first
class UserSerializer
...
end
```
3. Version inferred
```
class UsersApi < Grape::Api
version 'v2'
resource :users do
get '/:id' do
current_user
end
end
end
# 'v2'.classify => V2
# searches for serializers in the V2:: namespace with the name UserSerializer
# used
class V2::UserSerializer
...
end
# not used since V2::UserSerializer resolved first
class UserSerializer
...
end
```
4. Default ASM lookup
```
class V1::UsersApi < Grape::Api
version 'v2'
resource :users do
get '/:id' do
current_user
end
end
end
# searches for serializers in the V1:: namespace, none found
# searches for serializers in the V2:: namespace, none found
# used as no other serializers were found
class UserSerializer
...
end
```
#### Collection Serializer Resolution
Serializer resolution for collections also uses the above strategy, but has
the added option of overriding the member serializer if the `each_serializer`
options is specified.
```
class UsersApi < Grape::Api
resource :users do
get each_serializer: SpecifiedUserSerializer do
User.all
end
end
end
```
### Upgrading to v1.4.0
### Upgrading to v.1.4.0
#### Changes in Serializer Namespacing
Version 1.4.0 introduced changes in serializer namespacing when using Grape
API versioning.
Version 1.4.0 introduced changes in serializer namespacing when using Grape API versioning.
If you haven't declared an API version in Grape, nothing changed.
If your Grape API is versioned, which means you have declared at least one
version in Grape, you must namespace your serializers accordingly.
If your Grape API is versioned, which means you have declared at least one version in Grape, you must namespace your serializers accordingly.
For example, given the following API.
@ -181,8 +48,7 @@ module Chocolate
end
```
This will allow you to keep your serializers easier to maintain and more
organized.
This will allow you to keep your serializers easier to maintain and more organized.
```
app
@ -207,15 +73,13 @@ or alternatively:
└── candy_bar_user_serializer.rb
```
Thus, ActiveModelSerializer will fetch automatically the right serializer to
render.
Thus, ActiveModelSerializer will fetch automatically the right serializer to render.
### Upgrading to v1.0.0
#### Changes to Array Roots
When serializing an array, the array root is set to the innermost namespace
name if there is one, otherwise it is set to the route name.
When serializing an array, the array root is set to the innermost namespace name if there is one, otherwise it is set to the route name.
In the following example the array root is `users`.

View file

@ -16,6 +16,13 @@ Gem::Specification.new do |gem|
gem.version = Grape::ActiveModelSerializers::VERSION
gem.licenses = ['MIT']
gem.add_dependency 'grape', '>= 2.3.0'
gem.add_dependency 'active_model_serializers', '>= 0.10.0'
gem.add_dependency 'grape'
gem.add_dependency 'active_model_serializers', '>= 0.9.0'
gem.add_development_dependency 'listen', '~> 3.0.7'
gem.add_development_dependency 'rspec'
gem.add_development_dependency 'rack-test'
gem.add_development_dependency 'rake'
gem.add_development_dependency 'guard-rspec'
gem.add_development_dependency 'rubocop', '0.28.0'
end

View file

@ -1,8 +1,5 @@
require 'active_model_serializers'
require 'grape'
require 'grape-active_model_serializers/endpoint_extension'
require 'grape-active_model_serializers/error_formatter'
require 'grape-active_model_serializers/formatter'
require 'grape-active_model_serializers/serializer_resolver'
require 'grape-active_model_serializers/options_builder'
require 'grape-active_model_serializers/version'

View file

@ -9,7 +9,7 @@ module Grape
attr_accessor :controller_name
def namespace_options
if respond_to?(:inheritable_setting)
if self.respond_to?(:inheritable_setting)
inheritable_setting.namespace
else
settings[:namespace] ? settings[:namespace].options : {}
@ -17,7 +17,7 @@ module Grape
end
def route_options
if respond_to?(:inheritable_setting)
if self.respond_to?(:inheritable_setting)
inheritable_setting.route
else
options[:route_options]
@ -30,31 +30,22 @@ module Grape
base.class_eval do
def serialization_scope
return unless _serialization_scope
return unless respond_to?(_serialization_scope, true)
send(_serialization_scope)
send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope, true)
end
end
end
def render(resources, extra_options = {})
options = extra_options.symbolize_keys
env['ams_meta'] = options.slice(
:meta, :meta_key
)
env['ams_adapter'] = options.slice(
:adapter, :serializer, :each_serializer, :include,
:fields, :key_transform, :links, :namespace
)
env['ams_extra'] = options[:extra]
def render(resources, meta = {})
env['ams_meta'] = meta
resources
end
def default_serializer_options; end
def default_serializer_options
end
def url_options; end
def url_options
end
end
Endpoint.include EndpointExtension
Endpoint.send(:include, EndpointExtension)
end

View file

@ -1,51 +0,0 @@
module Grape
module ErrorFormatter
class ActiveModelSerializers < Base
class << self
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
message = present(message, env) if respond_to?(:present)
message = wrap_message(message)
rescue_options = options[:rescue_options] || {}
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
message = message.merge(backtrace: backtrace)
end
if rescue_options[:original_exception] && original_exception
message = message
.merge(original_exception: original_exception.inspect)
end
if ::Grape.const_defined? :Json
::Grape::Json.dump(message)
else
::MultiJson.dump(message)
end
end
private
def wrap_message(message)
if active_model?(message)
::ActiveModelSerializers::SerializableResource.new(
message,
serializer: ActiveModel::Serializer::ErrorSerializer
).as_json
elsif ok_to_pass_through?(message)
message
else
{ error: message }
end
end
def active_model?(message)
message.respond_to?(:errors) &&
message.errors.is_a?(ActiveModel::Errors)
end
def ok_to_pass_through?(message)
message.is_a?(Exceptions::ValidationErrors) ||
message.is_a?(Hash)
end
end
end
end
end

View file

@ -1,30 +1,62 @@
module Grape
module Formatter
class ActiveModelSerializers
module ActiveModelSerializers
class << self
def call(resource, env)
options = build_options(resource, env)
serializer = fetch_serializer(resource, options)
serializer = fetch_serializer(resource, env)
if serializer
::ActiveModelSerializers::Adapter.create(
serializer, options
).to_json
serializer.to_json
else
Grape::Formatter::Json.call(resource, env)
Grape::Formatter::Json.call resource, env
end
end
def build_options(resource, env)
Grape::ActiveModelSerializers::OptionsBuilder.new(
resource, env
).options
def fetch_serializer(resource, env)
endpoint = env['api.endpoint']
options = build_options_from_endpoint(endpoint)
ams_options = {}.tap do |ns|
# Extracting declared version from Grape
ns[:namespace] = options[:version].try(:classify) if options.try(:[], :version)
end
serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource, ams_options))
return nil unless serializer
options[:scope] = endpoint unless options.key?(:scope)
# ensure we have an root to fallback on
options[:resource_name] = default_root(endpoint) if resource.respond_to?(:to_ary)
serializer.new(resource, options.merge(other_options(env)))
end
def fetch_serializer(resource, options)
Grape::ActiveModelSerializers::SerializerResolver.new(
resource, options
).serializer
def other_options(env)
options = {}
ams_meta = env['ams_meta'] || {}
meta = ams_meta.delete(:meta)
meta_key = ams_meta.delete(:meta_key)
options[:meta_key] = meta_key if meta && meta_key
options[meta_key || :meta] = meta if meta
options
end
def build_options_from_endpoint(endpoint)
[endpoint.default_serializer_options || {}, endpoint.namespace_options, endpoint.route_options, endpoint.options, endpoint.options.fetch(:route_options)].reduce(:merge)
end
# array root is the innermost namespace name ('space') if there is one,
# otherwise the route name (e.g. get 'name')
def default_root(endpoint)
innermost_scope = if endpoint.respond_to?(:namespace_stackable)
endpoint.namespace_stackable(:namespace).last
else
endpoint.settings.peek[:namespace]
end
if innermost_scope
innermost_scope.space
else
endpoint.options[:path][0].to_s.split('/')[-1]
end
end
end
end

View file

@ -1,77 +0,0 @@
module Grape
module ActiveModelSerializers
class OptionsBuilder
def initialize(resource, env)
self.resource = resource
self.env = env
end
def options
@options ||= begin
options = endpoint_options
options[:scope] = endpoint unless options.key?(:scope)
options.merge!(default_root_options) unless options.key?(:root)
options.merge!(meta_options)
options.merge!(adapter_options)
options.merge!(extra_options)
options
end
end
private
attr_accessor :resource, :env
def endpoint_options
[
endpoint.default_serializer_options || {},
endpoint.namespace_options,
endpoint.route_options,
endpoint.options,
endpoint.options.fetch(:route_options)
].reduce(:merge)
end
def endpoint
@endpoint ||= env['api.endpoint']
end
# array root is the innermost namespace name ('space') if there is one,
# otherwise the route name (e.g. get 'name')
def default_root_options
return {} unless resource.respond_to?(:to_ary)
if innermost_scope
{ root: innermost_scope.space }
else
{ root: endpoint.options[:path].first.to_s.split('/').last }
end
end
def innermost_scope
endpoint.inheritable_setting.namespace_stackable[:namespace]&.last
end
def meta_options
options = {}
meta_options = env['ams_meta'] || {}
meta = meta_options[:meta]
meta_key = meta_options[:meta_key]
options[:meta] = meta if meta
options[:meta_key] = meta_key if meta && meta_key
options
end
def adapter_options
env['ams_adapter'] || {}
end
def extra_options
options = env['ams_extra'] || {}
return options if options.is_a?(Hash)
raise 'Extra options must be a hash'
end
end
end
end

View file

@ -1,107 +0,0 @@
module Grape
module ActiveModelSerializers
class SerializerResolver
def initialize(resource, options)
self.resource = resource
self.options = options
end
def serializer
serializer_class&.new(resource, serializer_options)
end
private
attr_accessor :resource, :options
def serializer_class
return @serializer_class if defined?(@serializer_class)
return nil if options.key?(:serializer) && options[:serializer].nil?
@serializer_class = resource_defined_class ||
collection_class ||
options[:serializer] ||
namespace_inferred_class ||
version_inferred_class ||
resource_serializer_class
end
def serializer_options
if collection_serializer? && !options.key?(:serializer)
options.merge(each_serializer_option)
else
options
end
end
def collection_serializer?
serializer_class == ActiveModel::Serializer.config.collection_serializer
end
def each_serializer_option
serializer_class = options[:each_serializer]
serializer_class ||= namespace_inferred_class
serializer_class ||= version_inferred_class
serializer_class ? { serializer: serializer_class } : {}
end
def resource_defined_class
resource.serializer_class if resource.respond_to?(:serializer_class)
end
def collection_class
if resource.respond_to?(:to_ary) || resource.respond_to?(:all)
ActiveModel::Serializer.config.collection_serializer
end
end
def namespace_inferred_class
return nil unless options.key?(:for)
namespace = options[:for].to_s.deconstantize
"#{namespace}::#{resource_serializer_klass}".safe_constantize
end
def version_inferred_class
return nil unless options.key?(:version)
"#{version}::#{resource_serializer_klass}".safe_constantize
end
def version
options[:version].try(:classify)
end
def resource_serializer_klass
@resource_serializer_klass ||= [
resource_namespace,
"#{resource_klass}Serializer"
].compact.join('::')
end
def resource_klass
resource_class.name.demodulize
end
def resource_namespace
klass = resource_class.name.deconstantize
klass.empty? ? nil : klass
end
def resource_class
if resource.respond_to?(:klass)
resource.klass
elsif resource.respond_to?(:to_ary) || resource.respond_to?(:all)
resource.first.class
else
resource.class
end
end
def resource_serializer_class
ActiveModel::Serializer.serializer_for(resource)
end
end
end
end

View file

@ -1,5 +1,5 @@
module Grape
module ActiveModelSerializers
VERSION = '2.0.2'.freeze
VERSION = '1.4.0'
end
end

View file

@ -5,7 +5,6 @@ describe '#render' do
let(:app) { Class.new(Grape::API) }
before do
ActiveModelSerializers.config.adapter = :json
app.format :json
app.formatter :json, Grape::Formatter::ActiveModelSerializers
end

View file

@ -2,140 +2,41 @@ require 'spec_helper'
describe 'Grape::EndpointExtension' do
if Grape::Util.const_defined?('InheritableSetting')
subject do
Grape::Endpoint.new(
Grape::Util::InheritableSetting.new,
path: '/',
method: 'foo'
)
end
subject { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo') }
else
subject do
Grape::Endpoint.new({}, path: '/', method: 'foo')
end
subject { Grape::Endpoint.new({}, path: '/', method: 'foo') }
end
let(:serializer) { Grape::Formatter::ActiveModelSerializers }
let(:user) {
let(:user) do
Object.new do
def name
'sven'
end
end
}
end
let(:users) { [user, user] }
describe '#render' do
let(:env) { {} }
let(:env_key) { nil }
before do
allow(subject).to receive(:env).and_return(env)
allow(subject).to receive(:env).and_return({})
end
shared_examples_for 'option capture' do
it 'captures options' do
subject.render(users, options)
expect(env[env_key]).to eq(options)
end
end
shared_examples_for 'skipped option capture' do
it 'does not capture options' do
subject.render(users, options)
expect(env[env_key]).to eq({})
end
end
it { should respond_to(:render) }
context 'meta options' do
let(:env_key) { 'ams_meta' }
let(:meta_full) { { meta: meta_content } }
context 'meta' do
let(:options) { { meta: { total: 2 } } }
it_behaves_like 'option capture'
end
context 'meta_key' do
let(:options) { { meta_key: 'custom_meta' } }
it_behaves_like 'option capture'
end
context 'unknown option' do
let(:options) { { unknown: 'value' } }
it_behaves_like 'skipped option capture'
let(:meta_content) { { total: 2 } }
let(:meta_full) { { meta: meta_content } }
context 'supplying meta' do
it 'passes through the Resource and uses given meta settings' do
allow(subject).to receive(:env).and_return(meta: meta_full)
expect(subject.render(users, meta_full)).to eq(users)
end
end
context 'adapter options' do
let(:options) { {} }
let(:env_key) { 'ams_adapter' }
context 'adapter' do
let(:options) { { adapter: :json } }
it_behaves_like 'option capture'
end
context 'include' do
let(:options) { { include: '*' } }
it_behaves_like 'option capture'
end
context 'fields' do
let(:options) { { fields: [:id] } }
it_behaves_like 'option capture'
end
context 'key_transform' do
let(:options) { { key_transform: :camel_lower } }
it_behaves_like 'option capture'
end
context 'links' do
let(:links_object) {
{
href: 'http://example.com/api/posts',
meta: {
count: 10
}
}
}
let(:options) { { links: links_object } }
it_behaves_like 'option capture'
end
context 'namespace' do
let(:options) { { namespace: V4 } }
it_behaves_like 'option capture'
end
context 'unknown option' do
let(:options) { { unknown: 'value' } }
it_behaves_like 'skipped option capture'
end
end
context 'extra options' do
let(:env_key) { 'ams_extra' }
context 'namespace' do
let(:options) { { extra: { option: 'value' } } }
it 'captures options' do
subject.render(users, options)
expect(env[env_key]).to eq(options[:extra])
end
end
context 'unknown option' do
let(:options) { { unknown: 'value' } }
it 'does not capture options' do
subject.render(users, options)
expect(env[env_key]).to eq(nil)
end
context 'supplying meta and key' do
let(:meta_key) { { meta_key: :custom_key_name } }
it 'passes through the Resource and uses given meta settings' do
allow(subject).to receive(:env).and_return(meta: meta_full.merge(meta_key))
expect(subject.render(users, meta_full.merge(meta_key))).to eq(users)
end
end
end

View file

@ -1,92 +0,0 @@
require 'spec_helper'
require 'grape-active_model_serializers/error_formatter'
describe Grape::ErrorFormatter::ActiveModelSerializers do
subject { Grape::ErrorFormatter::ActiveModelSerializers }
let(:backtrace) { ['Line:1'] }
let(:options) { {} }
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:original_exception) { StandardError.new('oh noes!') }
let(:app) {
Class.new(Grape::API) do |app|
app.format :json
app.formatter :jsonapi, Grape::Formatter::ActiveModelSerializers
app.error_formatter :jsonapi, Grape::ErrorFormatter::ActiveModelSerializers
app.namespace('space') do |ns|
ns.get('/', root: false) do
error!(message)
end
end
end
}
let(:foo) {
Class.new {
include ActiveModel::Model
attr_accessor :name
def initialize(attributes = {})
super
errors.add(:name, 'We don\'t like bears')
end
}
}
before do
ActiveModel::Serializer.config.adapter = :json_api
end
after do
ActiveModel::Serializer.config.adapter = :json
end
describe '#call' do
context 'message is an activemodel' do
let(:message) {
foo.new(name: 'bar')
}
it 'formats the error' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)
expected_result = {
'errors' => [
{
'source' => {
'pointer' => '/data/attributes/name'
},
'detail' => 'We don\'t like bears'
}
]
}
expect(json_hash == expected_result).to eq(true)
end
end
context 'message is hash like' do
let(:message) { { 'errors' => ['error'] } }
it 'passes the message through' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)
expect(json_hash == message).to eq(true)
end
end
context 'message is text' do
let(:message) { 'error' }
it 'wraps the error' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)
expect(json_hash == { 'error' => message }).to eq(true)
end
end
end
end

View file

@ -18,24 +18,19 @@ describe Grape::Formatter::ActiveModelSerializers do
end
end
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:options) { described_class.build_options(nil, env) }
it 'should read serializer options like "root"' do
expect(options).to include(:root)
expect(described_class.build_options_from_endpoint(app.endpoints.first)).to include :root
end
end
describe '.fetch_serializer' do
let(:user) { User.new(first_name: 'John') }
let(:params) { { path: '/', method: 'foo', root: false } }
if Grape::Util.const_defined?('InheritableSetting')
let(:setting) { Grape::Util::InheritableSetting.new }
let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo', root: false) }
else
let(:setting) { {} }
let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'foo', root: false) }
end
let(:endpoint) { Grape::Endpoint.new(setting, params) }
let(:env) { { 'api.endpoint' => endpoint } }
@ -49,10 +44,7 @@ describe Grape::Formatter::ActiveModelSerializers do
end
end
let(:options) { described_class.build_options(user, env) }
subject { described_class.fetch_serializer(user, options) }
let(:instance_options) { subject.instance_variable_get(:@instance_options) }
subject { described_class.fetch_serializer(user, env) }
it { should be_a UserSerializer }
@ -61,12 +53,12 @@ describe Grape::Formatter::ActiveModelSerializers do
end
it 'should read default serializer options' do
expect(instance_options[:only]).to eq(:only)
expect(instance_options[:except]).to eq(:except)
expect(subject.instance_variable_get('@only')).to eq([:only])
expect(subject.instance_variable_get('@except')).to eq([:except])
end
it 'should read serializer options like "root"' do
expect(options).to include(:root)
expect(described_class.build_options_from_endpoint(endpoint).keys).to include :root
end
end
end

View file

@ -15,35 +15,25 @@ describe Grape::Formatter::ActiveModelSerializers do
app.namespace('space') do |ns|
ns.get('/', root: false, apiver: 'v1') do
{
user: {
first_name: 'JR',
last_name: 'HE',
email: 'jrhe@github.com'
}
}
{ user: { first_name: 'JR', last_name: 'HE', email: 'jrhe@github.com' } }
end
end
end
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:options) { described_class.build_options(nil, env) }
it 'should read serializer options like "root"' do
expect(options).to include(:root)
expect(described_class.build_options_from_endpoint(app.endpoints.first)).to include :root
end
end
describe '.fetch_serializer' do
let(:user) { User.new(first_name: 'John', email: 'j.doe@internet.com') }
let(:params) { { path: '/', method: 'foo', version: 'v1', root: false } }
if Grape::Util.const_defined?('InheritableSetting')
let(:setting) { Grape::Util::InheritableSetting.new }
let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo', version: 'v1', root: false) }
else
let(:setting) { {} }
let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'foo', version: 'v1', root: false) }
end
let(:endpoint) { Grape::Endpoint.new(setting, params) }
let(:env) { { 'api.endpoint' => endpoint } }
before do
@ -56,10 +46,7 @@ describe Grape::Formatter::ActiveModelSerializers do
end
end
let(:options) { described_class.build_options(user, env) }
subject { described_class.fetch_serializer(user, options) }
let(:instance_options) { subject.send(:instance_options) }
subject { described_class.fetch_serializer(user, env) }
it { should be_a V1::UserSerializer }
@ -68,12 +55,12 @@ describe Grape::Formatter::ActiveModelSerializers do
end
it 'should read default serializer options' do
expect(instance_options[:only]).to eq(:only)
expect(instance_options[:except]).to eq(:except)
expect(subject.instance_variable_get('@only')).to eq([:only])
expect(subject.instance_variable_get('@except')).to eq([:except])
end
it 'should read serializer options like "root"' do
expect(options).to include(:root)
expect(described_class.build_options_from_endpoint(endpoint).keys).to include :root
end
end
end

View file

@ -0,0 +1,4 @@
require 'spec_helper'
describe 'grape-active_model_serializers' do
end

View file

@ -1,129 +0,0 @@
require 'spec_helper'
describe Grape::ActiveModelSerializers::OptionsBuilder do
let(:resolver) { described_class.new(resource, env) }
let(:resource) { User.new }
let(:env) { { 'api.endpoint' => UsersApi.endpoints.first } }
context '#options' do
let(:options) { resolver.options }
context 'meta options' do
let(:env) { super().merge('ams_meta' => meta_options) }
let(:meta_options) {
{
meta: meta,
meta_key: meta_key
}
}
let(:meta) { { sample: 'metadata' } }
let(:meta_key) { 'specified_key' }
context 'meta option set' do
context 'meta_key set' do
it 'includes meta' do
expect(options[:meta]).to eq(meta)
end
it 'includes meta_key' do
expect(options[:meta_key]).to eq(meta_key)
end
end
context 'meta_key unset' do
let(:meta_key) { nil }
it 'includes meta' do
expect(options[:meta]).to eq(meta)
end
it 'does not include meta_key' do
expect(options.keys).not_to include(:meta_key)
end
end
end
context 'meta option unset' do
let(:meta) { nil }
context 'meta_key set' do
it 'does not include meta' do
expect(options.keys).not_to include(:meta)
end
it 'does not include meta_key' do
expect(options.keys).not_to include(:meta_key)
end
end
context 'meta_key unset' do
let(:meta_key) { nil }
it 'does not include meta' do
expect(options.keys).not_to include(:meta)
end
it 'does not include meta_key' do
expect(options.keys).not_to include(:meta_key)
end
end
end
end
context 'adapter options' do
let(:env) { super().merge('ams_adapter' => adapter_options) }
let(:adapter_options) { {} }
context 'adapter' do
let(:adapter_options) { { adapter: adapter } }
let(:adapter) { :attributes }
it 'includes adapter as top-level option' do
expect(options[:adapter]).to eq(adapter)
end
end
context 'serializer' do
let(:adapter_options) { { serializer: serializer } }
let(:serializer) { V2::UserSerializer }
it 'includes serializer as top-level option' do
expect(options[:serializer]).to eq(serializer)
end
end
context 'each_serializer' do
let(:adapter_options) { { each_serializer: each_serializer } }
let(:each_serializer) { V2::UserSerializer }
it 'includes each_serializer as top-level option' do
expect(options[:each_serializer]).to eq(each_serializer)
end
end
end
context 'extra options' do
let(:env) { super().merge('ams_extra' => extra_options) }
context 'hash' do
let(:extra_options) { { extra: 'info' } }
it 'includes extra options in top-level options' do
expect(options.keys).to include(:extra)
end
end
context 'not a hash' do
let(:extra_options) { 'extra' }
it 'raises an exception' do
expect {
options
}.to raise_exception(
'Extra options must be a hash'
)
end
end
end
end
end

View file

@ -1,157 +0,0 @@
require 'spec_helper'
# asserts serializer resolution order:
# 1. resource_defined_class # V1::UserSerializer
# 2. collection_class # CollectionSerializer, V2::UserSerializer
# 3. options[:serializer] # V3::UserSerializer
# 4. namespace_inferred_class # V4::UserSerializer
# 5. version_inferred_class # V5::UserSerializer
# 6. resource_serializer_class # UserSerializer
# 7. missing resource # nil
describe Grape::ActiveModelSerializers::SerializerResolver do
let(:resolver) { described_class.new(resource, options) }
let(:resource) { User.new }
let(:options) {
{
serializer: options_serializer_class, # options defined
for: V4::UsersApi, # namespace inference
version: 'v5' # version inference
}
}
# resource defined
let(:resource_defined?) { true }
let(:defined_serializer_class) { V1::UserSerializer }
# options defined
let(:options_serializer_class) { V3::UserSerializer }
let(:serializer) { resolver.serializer }
before do
if resource_defined?
allow(resource).to receive(:respond_to?).and_call_original
allow(resource).to receive(:respond_to?).with(:to_ary) { true }
allow(resource).to receive(:serializer_class) { defined_serializer_class }
end
end
context 'resource defined' do
it 'returns serializer' do
expect(serializer).to be_kind_of(defined_serializer_class)
end
end
context 'not resource defined' do
let(:resource_defined?) { false }
context 'resource collection' do
let(:resource) { [User.new] }
let(:serializer_class) { ActiveModel::Serializer::CollectionSerializer }
it 'returns serializer' do
expect(serializer).to be_kind_of(serializer_class)
end
context 'specified nil by options' do
let(:options) {
super().merge(
serializer: nil
)
}
it 'returns nil' do
expect(serializer).to be_nil
end
end
context 'each serializer' do
let(:options) {
super().except(:serializer).merge(
each_serializer: V2::UserSerializer
)
}
let(:each_serializer) { serializer.send(:options)[:serializer] }
context 'each_serializer option' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V2::UserSerializer)
end
end
context 'no each_serializer option' do
let(:options) { super().except(:each_serializer) }
context 'namespace inferred' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V4::UserSerializer)
end
end
context 'not namespace inferred' do
let(:options) { super().except(:for) }
context 'version inferred' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V5::UserSerializer)
end
end
context 'not version inferred' do
let(:options) { super().except(:version) }
it 'returns nil' do
expect(each_serializer).to eq(nil)
end
end
end
end
end
end
context 'not resource collection' do
context 'specified by options' do
it 'returns specified serializer' do
expect(serializer).to be_kind_of(V3::UserSerializer)
end
end
context 'not specified by options' do
let(:options) { super().except(:serializer) }
context 'namespace inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V4::UserSerializer)
end
end
context 'not namespace inferred' do
let(:options) { super().except(:for) }
context 'version inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V5::UserSerializer)
end
end
context 'not version inferred' do
let(:options) { super().except(:version) }
context 'ASM resolved' do
it 'returns serializer' do
expect(serializer).to be_kind_of(UserSerializer)
end
end
context 'not ASM resolved' do
let(:resource) { nil }
it 'returns nil' do
expect(serializer).to eq(nil)
end
end
end
end
end
end
end
end

View file

@ -1,69 +0,0 @@
require 'sequel'
require 'spec_helper'
describe 'Sequel Integration' do
before do
DB = Sequel.sqlite unless defined?(DB)
DB.create_table(:users) do
primary_key :id
String :name
end
ActiveModelSerializers.config.adapter = :json
app.format :json
app.formatter :json, Grape::Formatter::ActiveModelSerializers
end
after do
DB.drop_table(:users)
Object.send(:remove_const, :SequelUser)
Object.send(:remove_const, :SequelUserSerializer)
end
let!(:model) {
SequelUser = Class.new(Sequel::Model(:users)) do
include ActiveModel::Serialization
def self.model_name
'User'
end
end
}
let!(:serializer) {
SequelUserSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
end
}
let(:app) { Class.new(Grape::API) }
context 'collection' do
let!(:users) {
[
model.create(name: 'one'),
model.create(name: 'two')
]
}
it 'renders' do
app.get('/users') { render SequelUser.dataset }
response = get '/users'
expect(JSON.parse(response.body)).to eq(
'users' => [
{ 'id' => 1, 'name' => 'one' },
{ 'id' => 2, 'name' => 'two' }
]
)
end
end
context 'member' do
let!(:user) { model.create(name: 'name') }
it 'renders' do
app.get('/user/1') { render SequelUser.first }
response = get '/user/1'
expect(JSON.parse(response.body)).to eq(
'user' => { 'id' => 1, 'name' => 'name' }
)
end
end
end

View file

@ -10,7 +10,6 @@ describe Grape::ActiveModelSerializers do
subject { last_response.body }
before do
ActiveModelSerializers.config.adapter = :json
app.format :json
app.formatter :json, Grape::Formatter::ActiveModelSerializers
end
@ -31,48 +30,31 @@ describe Grape::ActiveModelSerializers do
end
it 'uses the built in grape serializer' do
get('/home')
expect(subject).to eq(
'{"user":{"first_name":"JR","last_name":"HE"}}'
)
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
end
end
context "serializer isn't set" do
before do
app.get('/home') do
User.new(
first_name: 'JR',
last_name: 'HE',
email: 'contact@jrhe.co.uk'
)
User.new(first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk')
end
end
it 'infers the serializer' do
get '/home'
expect(subject).to eq(
'{"user":{"first_name":"JR","last_name":"HE"}}'
)
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
end
end
it 'serializes arrays of objects' do
app.get('/users') do
user = User.new(
first_name: 'JR',
last_name: 'HE',
email: 'contact@jrhe.co.uk'
)
user = User.new(first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk')
[user, user]
end
get '/users'
expect(subject).to eq(
'{"users":[' \
'{"first_name":"JR","last_name":"HE"},' \
'{"first_name":"JR","last_name":"HE"}' \
']}'
)
expect(subject).to eql "{\"users\":[{\"first_name\":\"JR\",\"last_name\":\"HE\"},{\"first_name\":\"JR\",\"last_name\":\"HE\"}]}"
end
context 'models with compound names' do
@ -82,29 +64,17 @@ describe Grape::ActiveModelSerializers do
end
get '/home'
expect(subject).to eq(
'{"blog_post":' \
'{"title":"Grape AM::S Rocks!","body":"Really, it does."}' \
'}'
)
expect(subject).to eql "{\"blog_post\":{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}}"
end
it "generates the proper 'root' node for serialized arrays" do
app.get('/blog_posts') do
blog_post = BlogPost.new(
title: 'Grape AM::S Rocks!',
body: 'Really, it does.'
)
blog_post = BlogPost.new(title: 'Grape AM::S Rocks!', body: 'Really, it does.')
[blog_post, blog_post]
end
get '/blog_posts'
expect(subject).to eq(
'{"blog_posts":[' \
'{"title":"Grape AM::S Rocks!","body":"Really, it does."},' \
'{"title":"Grape AM::S Rocks!","body":"Really, it does."}' \
']}'
)
expect(subject).to eql "{\"blog_posts\":[{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"},{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}]}"
end
end
@ -116,9 +86,7 @@ describe Grape::ActiveModelSerializers do
end
get '/admin/jeff'
expect(subject).to eq(
'{"user":{"first_name":"Jeff","last_name":null}}'
)
expect(subject).to eql "{\"user\":{\"first_name\":\"Jeff\",\"last_name\":null}}"
end
context 'route is in a namespace' do
@ -131,12 +99,7 @@ describe Grape::ActiveModelSerializers do
end
get '/admin/jeff'
expect(subject).to eq(
'{"admin":[' \
'{"first_name":"Jeff","last_name":null},' \
'{"first_name":"Jeff","last_name":null}' \
']}'
)
expect(subject).to eql "{\"admin\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
end
end
@ -148,12 +111,7 @@ describe Grape::ActiveModelSerializers do
end
get '/people'
expect(subject).to eq(
'{"people":[' \
'{"first_name":"Jeff","last_name":null},' \
'{"first_name":"Jeff","last_name":null}' \
']}'
)
expect(subject).to eql "{\"people\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
end
end
end

View file

@ -15,4 +15,4 @@ RSpec.configure do |config|
config.include Rack::Test::Methods
end
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }

View file

@ -1,13 +0,0 @@
class UsersApi < Grape::API
resource :users do
desc 'all users'
get do
[User.new]
end
desc 'specified user'
get '/:id' do
User.new
end
end
end

View file

@ -1,15 +0,0 @@
module V4
class UsersApi < Grape::API
resource :users do
desc 'all users'
get do
[User.new]
end
desc 'specified user'
get '/:id' do
User.new
end
end
end
end

View file

@ -1,12 +1,7 @@
class BlogPost
include ActiveModel::Serialization
include ActiveModel::SerializerSupport
attr_accessor :title, :body
def self.model_name
to_s
end
def initialize(params = {})
params.each do |k, v|
instance_variable_set("@#{k}", v) unless v.nil?

View file

@ -1,12 +1,7 @@
class User
include ActiveModel::Serialization
include ActiveModel::SerializerSupport
attr_accessor :first_name, :last_name, :password, :email
def self.model_name
to_s
end
def initialize(params = {})
params.each do |k, v|
instance_variable_set("@#{k}", v) unless v.nil?

View file

@ -1,5 +0,0 @@
module V2
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end

View file

@ -1,5 +0,0 @@
module V3
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end

View file

@ -1,5 +0,0 @@
module V4
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end

View file

@ -1,5 +0,0 @@
module V5
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end