mirror of
https://github.com/samsonjs/grape-active_model_serializers.git
synced 2026-03-25 08:45:55 +00:00
Compare commits
66 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c985c86410 | |||
| 55f3861ae3 | |||
| af29f9c76e | |||
| 7ca250a305 | |||
| 61673fe322 | |||
| 26767f5fac | |||
| 6f1e4037bc | |||
|
|
cce31fe187 | ||
| 7f759b6560 | |||
| 1a1c8b8173 | |||
| 9941241efc | |||
|
|
6ca5a7c5cf | ||
| b609ebc639 | |||
| dcc17a065d | |||
| 0da8ef5bd3 | |||
| 0a11b5d101 | |||
| 096fd41c11 | |||
| 7e58753b76 | |||
|
|
eb200812c8 | ||
|
|
a8ecfa3daf | ||
|
|
7e9c064f88 | ||
|
|
791c1b0f5e | ||
|
|
7670d9aed7 | ||
|
|
1995ba4676 | ||
|
|
fcb4c3a8c5 | ||
|
|
27ad60099e | ||
|
|
003e69d2bb | ||
|
|
dc01b70f26 | ||
|
|
03af2a385a | ||
|
|
cea5cf9d3e | ||
|
|
c5d7a8b071 | ||
|
|
4b7a08fecb | ||
|
|
8b8da16ffe | ||
|
|
b2654190c1 | ||
|
|
fab476772e | ||
|
|
6566fb517f | ||
|
|
1b09c69174 | ||
|
|
da18531590 | ||
|
|
eb28eb2636 | ||
|
|
ed5d5b4ca4 | ||
|
|
2748d5ba2b | ||
|
|
0963774ecc | ||
|
|
4315930a55 | ||
|
|
f304c8e487 | ||
|
|
c83394cb20 | ||
|
|
bc7a0b0420 | ||
|
|
22ea34d159 | ||
|
|
12bff3bcf2 | ||
|
|
5ae81261ab | ||
|
|
5360021287 | ||
|
|
eda39c5833 | ||
|
|
6bb205731a | ||
|
|
2a4be72387 | ||
|
|
eb43f68af8 | ||
|
|
629afb9887 | ||
|
|
f76ec6dcff | ||
|
|
ac9466192d | ||
|
|
37786d6a38 | ||
|
|
1dd73e9cd7 | ||
|
|
a48d57c2ba | ||
|
|
fb34024e04 | ||
|
|
8f1687b7f6 | ||
|
|
d2a5bb4c4e | ||
|
|
62faac16c3 | ||
|
|
35abadd8f7 | ||
|
|
78229c9dea |
38 changed files with 1450 additions and 206 deletions
38
.github/workflows/ci.yml
vendored
Normal file
38
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
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 }}
|
||||||
11
.rubocop.yml
11
.rubocop.yml
|
|
@ -1,2 +1,13 @@
|
||||||
inherit_from: .rubocop_todo.yml
|
inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
|
AllCops:
|
||||||
|
Exclude:
|
||||||
|
- Guardfile
|
||||||
|
- grape-active_model_serializers.gemspec
|
||||||
|
NewCops: enable
|
||||||
|
|
||||||
|
Style/BlockDelimiters:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/FrozenStringLiteralComment:
|
||||||
|
Enabled: false
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,71 @@
|
||||||
# This configuration was generated by `rubocop --auto-gen-config`
|
# This configuration was generated by
|
||||||
# on 2015-01-13 18:47:14 -0500 using RuboCop version 0.28.0.
|
# `rubocop --auto-gen-config`
|
||||||
|
# on 2018-03-14 14:59:23 -0400 using RuboCop version 0.53.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 25
|
# Offense count: 1
|
||||||
# Configuration parameters: AllowURI, URISchemes.
|
# Configuration parameters: Include.
|
||||||
|
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
|
||||||
|
Bundler/DuplicatedGem:
|
||||||
|
Exclude:
|
||||||
|
- 'Gemfile'
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 20
|
Max: 18
|
||||||
|
|
||||||
Metrics/LineLength:
|
# Offense count: 23
|
||||||
Max: 179
|
# Configuration parameters: CountComments, ExcludedMethods.
|
||||||
|
Metrics/BlockLength:
|
||||||
|
Max: 133
|
||||||
|
|
||||||
# Offense count: 7
|
# Offense count: 1
|
||||||
Style/Documentation:
|
Metrics/CyclomaticComplexity:
|
||||||
Enabled: false
|
Max: 9
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
|
# Configuration parameters: CountComments.
|
||||||
|
Metrics/MethodLength:
|
||||||
|
Max: 15
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
|
Max: 10
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Configuration parameters: Exclude.
|
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
|
||||||
Style/FileName:
|
# 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
|
||||||
Enabled: false
|
Naming/FileName:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/grape-active_model_serializers.rb'
|
||||||
|
- 'spec/grape-active_model_serializers_spec.rb'
|
||||||
|
|
||||||
# Offense count: 4
|
# Offense count: 5
|
||||||
Style/RegexpLiteral:
|
Style/Documentation:
|
||||||
MaxSlashes: 0
|
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'
|
||||||
|
|
||||||
|
# Offense count: 2
|
||||||
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||||
|
# URISchemes: http, https
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 87
|
||||||
|
|
||||||
|
Lint/ConstantDefinitionInBlock:
|
||||||
|
Exclude:
|
||||||
|
- spec/integration/sequel_spec.rb
|
||||||
|
|
|
||||||
36
.travis.yml
36
.travis.yml
|
|
@ -1,36 +0,0 @@
|
||||||
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
|
|
||||||
73
CHANGELOG.md
73
CHANGELOG.md
|
|
@ -1,40 +1,69 @@
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### 1.5.0 (Next)
|
### 2.0.2 (Next)
|
||||||
|
|
||||||
* Your contribution here.
|
* Your contribution here.
|
||||||
|
|
||||||
### 1.4.0 (July 14, 2016)
|
### 2.0.1 (2025/12/08)
|
||||||
|
|
||||||
* [#49](https://github.com/jrhe/grape-active_model_serializers/pull/49): Adds support for active model serializer namespace - [@syntaxTerr0r](https://github.com/syntaxTerr0r).
|
* [#92](https://github.com/ruby-grape/grape-active_model_serializers/pull/92): Fix: accept nil serializer - [@mateusnava](https://github.com/mateusnava).
|
||||||
* [#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).
|
* [#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).
|
||||||
|
|
||||||
### v1.3.2 (February 27, 2015)
|
### 2.0.0 (2025/06/02)
|
||||||
|
|
||||||
* [#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).
|
* [#96](https://github.com/ruby-grape/grape-active_model_serializers/pull/96): Add compatibility for Grape 2.3 - [@samsonjs](https://github.com/samsonjs).
|
||||||
* [#40](https://github.com/jrhe/grape-active_model_serializers/pull/40): Use env to pass AMS meta around - [@dblock](https://github.com/dblock).
|
* [#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).
|
||||||
|
|
||||||
### v1.3.1 (November 20, 2014)
|
### 1.5.2 (2018/03/14)
|
||||||
|
|
||||||
* [#30](https://github.com/jrhe/grape-active_model_serializers/pull/30): Read options from default_serializer_options - [@siong1987](https://github.com/siong1987).
|
* [#76](https://github.com/ruby-grape/grape-active_model_serializers/pull/76): Add custom error formatter - [@xn](https://github.com/xn).
|
||||||
* [#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).
|
|
||||||
|
|
||||||
### v1.2.1 (July 23, 2014)
|
### 1.5.1 (2017/04/25)
|
||||||
|
|
||||||
* [#21](https://github.com/jrhe/grape-active_model_serializers/pull/21): Correctly fetch serialization scope - [@radanskoric](https://github.com/radanskoric).
|
* [#74](https://github.com/ruby-grape/grape-active_model_serializers/pull/74): Add support for Sequel - [@drn](https://github.com/drn).
|
||||||
* [#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).
|
* [#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).
|
||||||
|
|
||||||
* [#15](https://github.com/jrhe/grape-active_model_serializers/pull/15): Added `render` syntactic sugar - [@zph](https://github.com/zph).
|
### 1.5.0 (2016/08/24)
|
||||||
* [#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).
|
|
||||||
|
|
||||||
### v1.0.1 (September 9, 2013)
|
* [#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).
|
||||||
|
|
||||||
* [#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).
|
### 1.4.0 (2016/07/14)
|
||||||
* [#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).
|
|
||||||
|
|
||||||
### v1.0.0
|
* [#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)
|
||||||
|
|
||||||
* Initial public release - [@jrhe](https://github.com/jrhe).
|
* Initial public release - [@jrhe](https://github.com/jrhe).
|
||||||
|
|
|
||||||
1
Dangerfile
Normal file
1
Dangerfile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
danger.import_dangerfile(gem: 'ruby-grape-danger')
|
||||||
17
Gemfile
17
Gemfile
|
|
@ -2,9 +2,24 @@ source 'https://rubygems.org'
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
case version = ENV['GRAPE_VERSION'] || '~> 0.10.0'
|
case version = ENV['GRAPE_VERSION'] || '~> 3.0.0'
|
||||||
when 'HEAD'
|
when 'HEAD'
|
||||||
gem 'grape', github: 'intridea/grape'
|
gem 'grape', github: 'intridea/grape'
|
||||||
else
|
else
|
||||||
gem 'grape', version
|
gem 'grape', version
|
||||||
end
|
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
|
||||||
|
|
|
||||||
71
README.md
71
README.md
|
|
@ -1,8 +1,29 @@
|
||||||
|
# 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
|
# Grape::ActiveModelSerializers
|
||||||
|
|
||||||
Use [active_model_serializers](https://github.com/rails-api/active_model_serializers) with [Grape](https://github.com/intridea/grape)!
|
Use [active_model_serializers](https://github.com/rails-api/active_model_serializers) with [Grape](https://github.com/intridea/grape)!
|
||||||
|
|
||||||
[](http://travis-ci.org/ruby-grape/grape-active_model_serializers) [](https://gemnasium.com/ruby-grape/grape-active_model_serializers) [](https://codeclimate.com/github/jrhe/grape-active_model_serializers)
|
[](https://badge.fury.io/rb/grape-active_model_serializers)
|
||||||
|
[](https://github.com/ruby-grape/grape-active_model_serializers/actions/workflows/ci.yml) [](https://codeclimate.com/github/ruby-grape/grape-active_model_serializers)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -14,6 +35,12 @@ gem 'grape-active_model_serializers'
|
||||||
|
|
||||||
See [UPGRADING](UPGRADING.md) if you're upgrading from a previous version.
|
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
|
## Usage
|
||||||
|
|
||||||
### Require grape-active_model_serializers
|
### Require grape-active_model_serializers
|
||||||
|
|
@ -29,6 +56,12 @@ require 'grape-active_model_serializers'
|
||||||
class API < Grape::API
|
class API < Grape::API
|
||||||
format :json
|
format :json
|
||||||
formatter :json, Grape::Formatter::ActiveModelSerializers
|
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
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -143,10 +176,10 @@ Or as follows.
|
||||||
|
|
||||||
ActiveModelSerializer will fetch automatically the right serializer to render.
|
ActiveModelSerializer will fetch automatically the right serializer to render.
|
||||||
|
|
||||||
### Manually specifying serializer options
|
### Manually specifying serializer / adapter options
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# Serializer options can be specified on routes or namespaces.
|
# Serializer and adapter options can be specified on routes or namespaces.
|
||||||
namespace 'foo', serializer: BarSerializer do
|
namespace 'foo', serializer: BarSerializer do
|
||||||
get "/" do
|
get "/" do
|
||||||
# will use "bar" serializer
|
# will use "bar" serializer
|
||||||
|
|
@ -164,6 +197,36 @@ namespace 'foo', serializer: BarSerializer do
|
||||||
end
|
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
|
### Custom Metadata
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
|
@ -240,4 +303,4 @@ See [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
Structured and based upon [grape-rabl](https://github.com/LTe/grape-rabl).
|
Structured and based upon [grape-rabl](https://github.com/ruby-grape/grape-rabl).
|
||||||
|
|
|
||||||
70
RELEASING.md
Normal file
70
RELEASING.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# 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
Normal file → Executable file
3
Rakefile
Normal file → Executable file
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env rake
|
#!/usr/bin/env rake
|
||||||
|
|
||||||
require 'bundler/gem_tasks'
|
require 'bundler/gem_tasks'
|
||||||
|
|
||||||
require 'rspec/core'
|
require 'rspec/core'
|
||||||
|
|
@ -11,4 +12,4 @@ end
|
||||||
require 'rubocop/rake_task'
|
require 'rubocop/rake_task'
|
||||||
RuboCop::RakeTask.new(:rubocop)
|
RuboCop::RakeTask.new(:rubocop)
|
||||||
|
|
||||||
task default: [:rubocop, :spec]
|
task default: %i[rubocop spec]
|
||||||
|
|
|
||||||
148
UPGRADING.md
148
UPGRADING.md
|
|
@ -1,12 +1,145 @@
|
||||||
### Upgrading to v.1.4.0
|
### 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
|
||||||
|
|
||||||
#### Changes in Serializer Namespacing
|
#### 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 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.
|
For example, given the following API.
|
||||||
|
|
||||||
|
|
@ -48,7 +181,8 @@ module Chocolate
|
||||||
end
|
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
|
app
|
||||||
|
|
@ -73,13 +207,15 @@ or alternatively:
|
||||||
└── candy_bar_user_serializer.rb
|
└── 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
|
### Upgrading to v1.0.0
|
||||||
|
|
||||||
#### Changes to Array Roots
|
#### 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`.
|
In the following example the array root is `users`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,6 @@ Gem::Specification.new do |gem|
|
||||||
gem.version = Grape::ActiveModelSerializers::VERSION
|
gem.version = Grape::ActiveModelSerializers::VERSION
|
||||||
gem.licenses = ['MIT']
|
gem.licenses = ['MIT']
|
||||||
|
|
||||||
gem.add_dependency 'grape'
|
gem.add_dependency 'grape', '>= 2.3.0'
|
||||||
gem.add_dependency 'active_model_serializers', '>= 0.9.0'
|
gem.add_dependency 'active_model_serializers', '>= 0.10.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
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
require 'active_model_serializers'
|
require 'active_model_serializers'
|
||||||
require 'grape'
|
require 'grape'
|
||||||
require 'grape-active_model_serializers/endpoint_extension'
|
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/formatter'
|
||||||
|
require 'grape-active_model_serializers/serializer_resolver'
|
||||||
|
require 'grape-active_model_serializers/options_builder'
|
||||||
require 'grape-active_model_serializers/version'
|
require 'grape-active_model_serializers/version'
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ module Grape
|
||||||
attr_accessor :controller_name
|
attr_accessor :controller_name
|
||||||
|
|
||||||
def namespace_options
|
def namespace_options
|
||||||
if self.respond_to?(:inheritable_setting)
|
if respond_to?(:inheritable_setting)
|
||||||
inheritable_setting.namespace
|
inheritable_setting.namespace
|
||||||
else
|
else
|
||||||
settings[:namespace] ? settings[:namespace].options : {}
|
settings[:namespace] ? settings[:namespace].options : {}
|
||||||
|
|
@ -17,7 +17,7 @@ module Grape
|
||||||
end
|
end
|
||||||
|
|
||||||
def route_options
|
def route_options
|
||||||
if self.respond_to?(:inheritable_setting)
|
if respond_to?(:inheritable_setting)
|
||||||
inheritable_setting.route
|
inheritable_setting.route
|
||||||
else
|
else
|
||||||
options[:route_options]
|
options[:route_options]
|
||||||
|
|
@ -30,22 +30,31 @@ module Grape
|
||||||
|
|
||||||
base.class_eval do
|
base.class_eval do
|
||||||
def serialization_scope
|
def serialization_scope
|
||||||
send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope, true)
|
return unless _serialization_scope
|
||||||
|
return unless respond_to?(_serialization_scope, true)
|
||||||
|
|
||||||
|
send(_serialization_scope)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(resources, meta = {})
|
def render(resources, extra_options = {})
|
||||||
env['ams_meta'] = meta
|
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]
|
||||||
resources
|
resources
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_serializer_options
|
def default_serializer_options; end
|
||||||
end
|
|
||||||
|
|
||||||
def url_options
|
def url_options; end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Endpoint.send(:include, EndpointExtension)
|
Endpoint.include EndpointExtension
|
||||||
end
|
end
|
||||||
|
|
|
||||||
51
lib/grape-active_model_serializers/error_formatter.rb
Normal file
51
lib/grape-active_model_serializers/error_formatter.rb
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
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
|
||||||
|
|
@ -1,62 +1,30 @@
|
||||||
module Grape
|
module Grape
|
||||||
module Formatter
|
module Formatter
|
||||||
module ActiveModelSerializers
|
class ActiveModelSerializers
|
||||||
class << self
|
class << self
|
||||||
def call(resource, env)
|
def call(resource, env)
|
||||||
serializer = fetch_serializer(resource, env)
|
options = build_options(resource, env)
|
||||||
|
serializer = fetch_serializer(resource, options)
|
||||||
|
|
||||||
if serializer
|
if serializer
|
||||||
serializer.to_json
|
::ActiveModelSerializers::Adapter.create(
|
||||||
|
serializer, options
|
||||||
|
).to_json
|
||||||
else
|
else
|
||||||
Grape::Formatter::Json.call resource, env
|
Grape::Formatter::Json.call(resource, env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_serializer(resource, env)
|
def build_options(resource, env)
|
||||||
endpoint = env['api.endpoint']
|
Grape::ActiveModelSerializers::OptionsBuilder.new(
|
||||||
options = build_options_from_endpoint(endpoint)
|
resource, env
|
||||||
ams_options = {}.tap do |ns|
|
).options
|
||||||
# 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
|
end
|
||||||
|
|
||||||
def other_options(env)
|
def fetch_serializer(resource, options)
|
||||||
options = {}
|
Grape::ActiveModelSerializers::SerializerResolver.new(
|
||||||
ams_meta = env['ams_meta'] || {}
|
resource, options
|
||||||
meta = ams_meta.delete(:meta)
|
).serializer
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
77
lib/grape-active_model_serializers/options_builder.rb
Normal file
77
lib/grape-active_model_serializers/options_builder.rb
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
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
|
||||||
107
lib/grape-active_model_serializers/serializer_resolver.rb
Normal file
107
lib/grape-active_model_serializers/serializer_resolver.rb
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
module Grape
|
module Grape
|
||||||
module ActiveModelSerializers
|
module ActiveModelSerializers
|
||||||
VERSION = '1.4.0'
|
VERSION = '2.0.2'.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ describe '#render' do
|
||||||
let(:app) { Class.new(Grape::API) }
|
let(:app) { Class.new(Grape::API) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
ActiveModelSerializers.config.adapter = :json
|
||||||
app.format :json
|
app.format :json
|
||||||
app.formatter :json, Grape::Formatter::ActiveModelSerializers
|
app.formatter :json, Grape::Formatter::ActiveModelSerializers
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,41 +2,140 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe 'Grape::EndpointExtension' do
|
describe 'Grape::EndpointExtension' do
|
||||||
if Grape::Util.const_defined?('InheritableSetting')
|
if Grape::Util.const_defined?('InheritableSetting')
|
||||||
subject { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo') }
|
subject do
|
||||||
|
Grape::Endpoint.new(
|
||||||
|
Grape::Util::InheritableSetting.new,
|
||||||
|
path: '/',
|
||||||
|
method: 'foo'
|
||||||
|
)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
subject { Grape::Endpoint.new({}, path: '/', method: 'foo') }
|
subject do
|
||||||
|
Grape::Endpoint.new({}, path: '/', method: 'foo')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:serializer) { Grape::Formatter::ActiveModelSerializers }
|
let(:serializer) { Grape::Formatter::ActiveModelSerializers }
|
||||||
|
let(:user) {
|
||||||
let(:user) do
|
|
||||||
Object.new do
|
Object.new do
|
||||||
def name
|
def name
|
||||||
'sven'
|
'sven'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
}
|
||||||
|
|
||||||
let(:users) { [user, user] }
|
let(:users) { [user, user] }
|
||||||
|
|
||||||
describe '#render' do
|
describe '#render' do
|
||||||
|
let(:env) { {} }
|
||||||
|
let(:env_key) { nil }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(subject).to receive(:env).and_return({})
|
allow(subject).to receive(:env).and_return(env)
|
||||||
end
|
end
|
||||||
it { should respond_to(:render) }
|
|
||||||
let(:meta_content) { { total: 2 } }
|
shared_examples_for 'option capture' do
|
||||||
let(:meta_full) { { meta: meta_content } }
|
it 'captures options' do
|
||||||
context 'supplying meta' do
|
subject.render(users, options)
|
||||||
it 'passes through the Resource and uses given meta settings' do
|
expect(env[env_key]).to eq(options)
|
||||||
allow(subject).to receive(:env).and_return(meta: meta_full)
|
|
||||||
expect(subject.render(users, meta_full)).to eq(users)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
context 'supplying meta and key' do
|
|
||||||
let(:meta_key) { { meta_key: :custom_key_name } }
|
shared_examples_for 'skipped option capture' do
|
||||||
it 'passes through the Resource and uses given meta settings' do
|
it 'does not capture options' do
|
||||||
allow(subject).to receive(:env).and_return(meta: meta_full.merge(meta_key))
|
subject.render(users, options)
|
||||||
expect(subject.render(users, meta_full.merge(meta_key))).to eq(users)
|
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'
|
||||||
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
92
spec/grape-active_model_serializers/error_formatter_spec.rb
Normal file
92
spec/grape-active_model_serializers/error_formatter_spec.rb
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
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
|
||||||
|
|
@ -18,19 +18,24 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
end
|
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
|
it 'should read serializer options like "root"' do
|
||||||
expect(described_class.build_options_from_endpoint(app.endpoints.first)).to include :root
|
expect(options).to include(:root)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.fetch_serializer' do
|
describe '.fetch_serializer' do
|
||||||
let(:user) { User.new(first_name: 'John') }
|
let(:user) { User.new(first_name: 'John') }
|
||||||
|
|
||||||
|
let(:params) { { path: '/', method: 'foo', root: false } }
|
||||||
if Grape::Util.const_defined?('InheritableSetting')
|
if Grape::Util.const_defined?('InheritableSetting')
|
||||||
let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo', root: false) }
|
let(:setting) { Grape::Util::InheritableSetting.new }
|
||||||
else
|
else
|
||||||
let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'foo', root: false) }
|
let(:setting) { {} }
|
||||||
end
|
end
|
||||||
|
let(:endpoint) { Grape::Endpoint.new(setting, params) }
|
||||||
|
|
||||||
let(:env) { { 'api.endpoint' => endpoint } }
|
let(:env) { { 'api.endpoint' => endpoint } }
|
||||||
|
|
||||||
|
|
@ -44,7 +49,10 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { described_class.fetch_serializer(user, env) }
|
let(:options) { described_class.build_options(user, env) }
|
||||||
|
subject { described_class.fetch_serializer(user, options) }
|
||||||
|
|
||||||
|
let(:instance_options) { subject.instance_variable_get(:@instance_options) }
|
||||||
|
|
||||||
it { should be_a UserSerializer }
|
it { should be_a UserSerializer }
|
||||||
|
|
||||||
|
|
@ -53,12 +61,12 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should read default serializer options' do
|
it 'should read default serializer options' do
|
||||||
expect(subject.instance_variable_get('@only')).to eq([:only])
|
expect(instance_options[:only]).to eq(:only)
|
||||||
expect(subject.instance_variable_get('@except')).to eq([:except])
|
expect(instance_options[:except]).to eq(:except)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should read serializer options like "root"' do
|
it 'should read serializer options like "root"' do
|
||||||
expect(described_class.build_options_from_endpoint(endpoint).keys).to include :root
|
expect(options).to include(:root)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,25 +15,35 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
|
|
||||||
app.namespace('space') do |ns|
|
app.namespace('space') do |ns|
|
||||||
ns.get('/', root: false, apiver: 'v1') do
|
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
|
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
|
it 'should read serializer options like "root"' do
|
||||||
expect(described_class.build_options_from_endpoint(app.endpoints.first)).to include :root
|
expect(options).to include(:root)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.fetch_serializer' do
|
describe '.fetch_serializer' do
|
||||||
let(:user) { User.new(first_name: 'John', email: 'j.doe@internet.com') }
|
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')
|
if Grape::Util.const_defined?('InheritableSetting')
|
||||||
let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo', version: 'v1', root: false) }
|
let(:setting) { Grape::Util::InheritableSetting.new }
|
||||||
else
|
else
|
||||||
let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'foo', version: 'v1', root: false) }
|
let(:setting) { {} }
|
||||||
end
|
end
|
||||||
|
let(:endpoint) { Grape::Endpoint.new(setting, params) }
|
||||||
let(:env) { { 'api.endpoint' => endpoint } }
|
let(:env) { { 'api.endpoint' => endpoint } }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
@ -46,7 +56,10 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { described_class.fetch_serializer(user, env) }
|
let(:options) { described_class.build_options(user, env) }
|
||||||
|
subject { described_class.fetch_serializer(user, options) }
|
||||||
|
|
||||||
|
let(:instance_options) { subject.send(:instance_options) }
|
||||||
|
|
||||||
it { should be_a V1::UserSerializer }
|
it { should be_a V1::UserSerializer }
|
||||||
|
|
||||||
|
|
@ -55,12 +68,12 @@ describe Grape::Formatter::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should read default serializer options' do
|
it 'should read default serializer options' do
|
||||||
expect(subject.instance_variable_get('@only')).to eq([:only])
|
expect(instance_options[:only]).to eq(:only)
|
||||||
expect(subject.instance_variable_get('@except')).to eq([:except])
|
expect(instance_options[:except]).to eq(:except)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should read serializer options like "root"' do
|
it 'should read serializer options like "root"' do
|
||||||
expect(described_class.build_options_from_endpoint(endpoint).keys).to include :root
|
expect(options).to include(:root)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe 'grape-active_model_serializers' do
|
|
||||||
end
|
|
||||||
129
spec/grape/active_model_serializers/options_builder_spec.rb
Normal file
129
spec/grape/active_model_serializers/options_builder_spec.rb
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
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
|
||||||
157
spec/grape/active_model_serializers/serializer_resolver_spec.rb
Normal file
157
spec/grape/active_model_serializers/serializer_resolver_spec.rb
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
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
|
||||||
69
spec/integration/sequel_spec.rb
Normal file
69
spec/integration/sequel_spec.rb
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
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
|
||||||
|
|
@ -10,6 +10,7 @@ describe Grape::ActiveModelSerializers do
|
||||||
subject { last_response.body }
|
subject { last_response.body }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
ActiveModelSerializers.config.adapter = :json
|
||||||
app.format :json
|
app.format :json
|
||||||
app.formatter :json, Grape::Formatter::ActiveModelSerializers
|
app.formatter :json, Grape::Formatter::ActiveModelSerializers
|
||||||
end
|
end
|
||||||
|
|
@ -30,31 +31,48 @@ describe Grape::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
it 'uses the built in grape serializer' do
|
it 'uses the built in grape serializer' do
|
||||||
get('/home')
|
get('/home')
|
||||||
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
|
expect(subject).to eq(
|
||||||
|
'{"user":{"first_name":"JR","last_name":"HE"}}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "serializer isn't set" do
|
context "serializer isn't set" do
|
||||||
before do
|
before do
|
||||||
app.get('/home') 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
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'infers the serializer' do
|
it 'infers the serializer' do
|
||||||
get '/home'
|
get '/home'
|
||||||
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
|
expect(subject).to eq(
|
||||||
|
'{"user":{"first_name":"JR","last_name":"HE"}}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'serializes arrays of objects' do
|
it 'serializes arrays of objects' do
|
||||||
app.get('/users') 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]
|
[user, user]
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/users'
|
get '/users'
|
||||||
expect(subject).to eql "{\"users\":[{\"first_name\":\"JR\",\"last_name\":\"HE\"},{\"first_name\":\"JR\",\"last_name\":\"HE\"}]}"
|
expect(subject).to eq(
|
||||||
|
'{"users":[' \
|
||||||
|
'{"first_name":"JR","last_name":"HE"},' \
|
||||||
|
'{"first_name":"JR","last_name":"HE"}' \
|
||||||
|
']}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'models with compound names' do
|
context 'models with compound names' do
|
||||||
|
|
@ -64,17 +82,29 @@ describe Grape::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/home'
|
get '/home'
|
||||||
expect(subject).to eql "{\"blog_post\":{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}}"
|
expect(subject).to eq(
|
||||||
|
'{"blog_post":' \
|
||||||
|
'{"title":"Grape AM::S Rocks!","body":"Really, it does."}' \
|
||||||
|
'}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates the proper 'root' node for serialized arrays" do
|
it "generates the proper 'root' node for serialized arrays" do
|
||||||
app.get('/blog_posts') 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]
|
[blog_post, blog_post]
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/blog_posts'
|
get '/blog_posts'
|
||||||
expect(subject).to eql "{\"blog_posts\":[{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"},{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}]}"
|
expect(subject).to eq(
|
||||||
|
'{"blog_posts":[' \
|
||||||
|
'{"title":"Grape AM::S Rocks!","body":"Really, it does."},' \
|
||||||
|
'{"title":"Grape AM::S Rocks!","body":"Really, it does."}' \
|
||||||
|
']}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -86,7 +116,9 @@ describe Grape::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/admin/jeff'
|
get '/admin/jeff'
|
||||||
expect(subject).to eql "{\"user\":{\"first_name\":\"Jeff\",\"last_name\":null}}"
|
expect(subject).to eq(
|
||||||
|
'{"user":{"first_name":"Jeff","last_name":null}}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'route is in a namespace' do
|
context 'route is in a namespace' do
|
||||||
|
|
@ -99,7 +131,12 @@ describe Grape::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/admin/jeff'
|
get '/admin/jeff'
|
||||||
expect(subject).to eql "{\"admin\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
|
expect(subject).to eq(
|
||||||
|
'{"admin":[' \
|
||||||
|
'{"first_name":"Jeff","last_name":null},' \
|
||||||
|
'{"first_name":"Jeff","last_name":null}' \
|
||||||
|
']}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -111,7 +148,12 @@ describe Grape::ActiveModelSerializers do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/people'
|
get '/people'
|
||||||
expect(subject).to eql "{\"people\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
|
expect(subject).to eq(
|
||||||
|
'{"people":[' \
|
||||||
|
'{"first_name":"Jeff","last_name":null},' \
|
||||||
|
'{"first_name":"Jeff","last_name":null}' \
|
||||||
|
']}'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,4 @@ RSpec.configure do |config|
|
||||||
config.include Rack::Test::Methods
|
config.include Rack::Test::Methods
|
||||||
end
|
end
|
||||||
|
|
||||||
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|
||||||
|
|
|
||||||
13
spec/support/api/users_api.rb
Normal file
13
spec/support/api/users_api.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
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
|
||||||
15
spec/support/api/v4/users_api.rb
Normal file
15
spec/support/api/v4/users_api.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
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
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
class BlogPost
|
class BlogPost
|
||||||
include ActiveModel::SerializerSupport
|
include ActiveModel::Serialization
|
||||||
|
|
||||||
attr_accessor :title, :body
|
attr_accessor :title, :body
|
||||||
|
|
||||||
|
def self.model_name
|
||||||
|
to_s
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(params = {})
|
def initialize(params = {})
|
||||||
params.each do |k, v|
|
params.each do |k, v|
|
||||||
instance_variable_set("@#{k}", v) unless v.nil?
|
instance_variable_set("@#{k}", v) unless v.nil?
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
class User
|
class User
|
||||||
include ActiveModel::SerializerSupport
|
include ActiveModel::Serialization
|
||||||
|
|
||||||
attr_accessor :first_name, :last_name, :password, :email
|
attr_accessor :first_name, :last_name, :password, :email
|
||||||
|
|
||||||
|
def self.model_name
|
||||||
|
to_s
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(params = {})
|
def initialize(params = {})
|
||||||
params.each do |k, v|
|
params.each do |k, v|
|
||||||
instance_variable_set("@#{k}", v) unless v.nil?
|
instance_variable_set("@#{k}", v) unless v.nil?
|
||||||
|
|
|
||||||
5
spec/support/serializers/v2/user_serializer.rb
Normal file
5
spec/support/serializers/v2/user_serializer.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module V2
|
||||||
|
class UserSerializer < ActiveModel::Serializer
|
||||||
|
attributes :first_name, :last_name, :email
|
||||||
|
end
|
||||||
|
end
|
||||||
5
spec/support/serializers/v3/user_serializer.rb
Normal file
5
spec/support/serializers/v3/user_serializer.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module V3
|
||||||
|
class UserSerializer < ActiveModel::Serializer
|
||||||
|
attributes :first_name, :last_name, :email
|
||||||
|
end
|
||||||
|
end
|
||||||
5
spec/support/serializers/v4/user_serializer.rb
Normal file
5
spec/support/serializers/v4/user_serializer.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module V4
|
||||||
|
class UserSerializer < ActiveModel::Serializer
|
||||||
|
attributes :first_name, :last_name, :email
|
||||||
|
end
|
||||||
|
end
|
||||||
5
spec/support/serializers/v5/user_serializer.rb
Normal file
5
spec/support/serializers/v5/user_serializer.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module V5
|
||||||
|
class UserSerializer < ActiveModel::Serializer
|
||||||
|
attributes :first_name, :last_name, :email
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue