diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..b02f6a0a1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,74 @@
+---
+
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ test:
+ name: "Testing"
+ runs-on: ubuntu-18.04
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # Recent Rubies and Rails
+ - ruby-version: '3.1'
+ rails-version: '7.0'
+ - ruby-version: '3.0'
+ rails-version: '7.0'
+ - ruby-version: '2.7'
+ rails-version: '7.0'
+ - ruby-version: '2.6'
+ rails-version: '6.1'
+ - ruby-version: '2.6'
+ rails-version: '6.0'
+ - ruby-version: '2.7'
+ rails-version: '6.0'
+ - ruby-version: '2.6'
+ rails-version: '5.2'
+ # Old Rubies and Rails
+ - ruby-version: '2.5'
+ rails-version: '5.1'
+ bundler: '1'
+ - ruby-version: '2.4'
+ rails-version: '5.0'
+ bundler: '1'
+ - ruby-version: '2.4'
+ rails-version: '4.2'
+ bundler: '1'
+ # Failing with a stack trace in active support
+ # - ruby-version: '2.4'
+ # rails-version: '4.1'
+ # bundler: '1'
+
+ continue-on-error: "${{ endsWith(matrix.ruby-version, 'head') }}"
+
+ env:
+ CI: "1"
+
+ steps:
+ - name: "Checkout Code"
+ uses: "actions/checkout@v2"
+ timeout-minutes: 5
+ with:
+ fetch-depth: 0
+
+ - name: Install required libs
+ run: |
+ sudo apt-get -yqq install libsqlite3-dev
+
+ - name: "Build Ruby"
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: "${{ matrix.ruby-version }}"
+ bundler: "${{ matrix.bundler || 2 }}"
+ bundler-cache: true
+ env:
+ RAILS_VERSION: ${{ matrix.rails-version }}
+
+ - name: "Run tests"
+ run: |
+ bundle exec rake
+ env:
+ RAILS_VERSION: ${{ matrix.rails-version }}
diff --git a/.rubocop.yml b/.rubocop.yml
index 82f076564..6dff73574 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -59,6 +59,10 @@ Style/MissingElse:
Style/EmptyElse:
EnforcedStyle: empty
+Style/FrozenStringLiteralComment:
+ Enabled: true
+ EnforcedStyle: always
+
Style/MultilineOperationIndentation:
EnforcedStyle: indented
diff --git a/.simplecov b/.simplecov
deleted file mode 100644
index 955a60606..000000000
--- a/.simplecov
+++ /dev/null
@@ -1,110 +0,0 @@
-# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config
-# see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb
-# vim: set ft=ruby
-
-## DEFINE VARIABLES
-@minimum_coverage = ENV.fetch('COVERAGE_MINIMUM') {
- case (defined?(RUBY_ENGINE) && RUBY_ENGINE) || "ruby"
- when 'jruby', 'rbx'
- 96.0
- else
- 98.1
- end
-}.to_f.round(2)
-# rubocop:disable Style/DoubleNegation
-ENV['FULL_BUILD'] ||= ENV['CI']
-@running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i)
-@generate_report = @running_ci || !!(ENV['COVERAGE'] =~ /\Atrue\z/i)
-@output = STDOUT
-# rubocop:enable Style/DoubleNegation
-
-## CONFIGURE SIMPLECOV
-
-SimpleCov.profiles.define 'app' do
- coverage_dir 'coverage'
- load_profile 'test_frameworks'
-
- add_group 'Libraries', 'lib'
-
- add_group 'Long files' do |src_file|
- src_file.lines.count > 100
- end
- class MaxLinesFilter < SimpleCov::Filter
- def matches?(source_file)
- source_file.lines.count < filter_argument
- end
- end
- add_group 'Short files', MaxLinesFilter.new(5)
-
- # Exclude these paths from analysis
- add_filter '/config/'
- add_filter '/db/'
- add_filter 'tasks'
- add_filter '/.bundle/'
-end
-
-## START TRACKING COVERAGE (before activating SimpleCov)
-require 'coverage'
-Coverage.start
-
-## ADD SOME CUSTOM REPORTING AT EXIT
-SimpleCov.at_exit do
- next if $! and not ($!.kind_of? SystemExit and $!.success?)
-
- header = "#{'*' * 20} SimpleCov Results #{'*' * 20}"
- results = SimpleCov.result.format!.join("\n")
- exit_message = <<-EOF
-
-#{header}
-{{RESULTS}}
-{{FAILURE_MESSAGE}}
-
-#{'*' * header.size}
- EOF
- percent = Float(SimpleCov.result.covered_percent)
- if percent < @minimum_coverage
- failure_message = <<-EOF
-Spec coverage was not high enough: #{percent.round(2)}% is < #{@minimum_coverage}%
- EOF
- exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', failure_message)
- @output.puts exit_message
- abort(failure_message) if @generate_report
- elsif @running_ci
- exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', <<-EOF)
-Nice job! Spec coverage (#{percent.round(2)}%) is still at or above #{@minimum_coverage}%
- EOF
- @output.puts exit_message
- end
-end
-
-## CAPTURE CONFIG IN CLOSURE 'AppCoverage.start'
-## to defer running until test/test_helper.rb is loaded.
-# rubocop:disable Style/MultilineBlockChain
-AppCoverage = Class.new do
- def initialize(&block)
- @block = block
- end
-
- def start
- @block.call
- end
-end.new do
- SimpleCov.start 'app'
- if @generate_report
- if @running_ci
- require 'codeclimate-test-reporter'
- @output.puts '[COVERAGE] Running with SimpleCov Simple Formatter and CodeClimate Test Reporter'
- formatters = [
- SimpleCov::Formatter::SimpleFormatter,
- CodeClimate::TestReporter::Formatter
- ]
- else
- @output.puts '[COVERAGE] Running with SimpleCov HTML Formatter'
- formatters = [SimpleCov::Formatter::HTMLFormatter]
- end
- else
- formatters = []
- end
- SimpleCov.formatters = formatters
-end
-# rubocop:enable Style/MultilineBlockChain
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9aff1edcb..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-language: ruby
-
-sudo: false
-
-rvm:
- - 2.1
- - 2.2.6
- - 2.3.3
- - ruby-head
- - jruby-9.1.5.0 # is precompiled per http://rubies.travis-ci.org/
- - jruby-head
-
-jdk:
- - oraclejdk8
-
-before_install:
- - gem update --system
- - rvm @global do gem uninstall bundler -a -x
- - rvm @global do gem install bundler -v 1.13.7
-install: bundle install --path=vendor/bundle --retry=3 --jobs=3
-cache:
- directories:
- - vendor/bundle
-
-script:
- - bundle exec rake ci
-after_success:
- - codeclimate-test-reporter
-env:
- global:
- - "JRUBY_OPTS='--dev -J-Xmx1024M --debug'"
- matrix:
- - "RAILS_VERSION=4.1"
- - "RAILS_VERSION=4.2"
- - "RAILS_VERSION=5.0"
- - "RAILS_VERSION=master"
-
-matrix:
- exclude:
- - rvm: 2.1
- env: RAILS_VERSION=master
- - rvm: jruby-9.1.5.0
- env: RAILS_VERSION=master
- - rvm: jruby-head
- env: RAILS_VERSION=master
- - rvm: 2.1
- env: RAILS_VERSION=5.0
- - rvm: jruby-9.1.5.0
- env: RAILS_VERSION=5.0
- - rvm: jruby-head
- env: RAILS_VERSION=5.0
- allow_failures:
- - rvm: ruby-head
- - rvm: jruby-head
- fast_finish: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c975e4733..532a7ca7e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
## 0.10.x
-### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.6...master)
+### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.13...0-10-stable)
Breaking changes:
@@ -10,6 +10,119 @@ Fixes:
Misc:
+### [v0.10.13 (2022-01013)](https://github.com/rails-api/active_model_serializers/compare/v0.10.12...v0.10.13)
+
+Fixes:
+
+- [#2399](https://github.com/rails-api/active_model_serializers/pull/2399) Handles edge case where requested current_page > total_pages (@f3z0)
+
+### [v0.10.12 (2020-12-10)](https://github.com/rails-api/active_model_serializers/compare/v0.10.11...v0.10.12)
+
+Fixes:
+
+- [#2398](https://github.com/rails-api/active_model_serializers/pull/2398) Update rails dependency to < 6.2 (@ritikesh)
+
+### [v0.10.11 (2020-12-04)](https://github.com/rails-api/active_model_serializers/compare/v0.10.10...v0.10.11)
+
+Features:
+
+- [#2361](https://github.com/rails-api/active_model_serializers/pull/2361) Add `ActiveModelSerializers.config.use_sha1_digests` to allow customization of the hashing algorithm used for serializer caching (@alexzherdev)
+
+Fixes:
+
+- [#2344](https://github.com/rails-api/active_model_serializers/pull/2344) Fixes #2341 introduced since #2223 (@wasifhossain)
+- [#2395](https://github.com/rails-api/active_model_serializers/pull/2395) remove explicit require for thread_safe (@ritikesh)
+
+### [v0.10.10 (2019-07-13)](https://github.com/rails-api/active_model_serializers/compare/v0.10.9...v0.10.10)
+
+Fixes:
+
+- [#2319](https://github.com/rails-api/active_model_serializers/pull/2319) Fixes #2316. (@kylekeesling)
+ - Fix Rails 6.0 deprication warnings
+ - update test fixture schema to use `timestamps` instead of `timestamp`
+- [#2223](https://github.com/rails-api/active_model_serializers/pull/2223) Support Fieldset in Attributes/JSON adapters documented in [docs/general/fields.md](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/fields.md) that worked partially before (@bf4)
+- [#2337](https://github.com/rails-api/active_model_serializers/pull/2337) fix incorrect belongs_to serialization when foreign_key on object and belongs_to is blank (@InteNs)
+ - Fixes incorrect json-api generation when `jsonapi_use_foreign_key_on_belongs_to_relationship` is `true` and the relationship is blank
+- [#2172](https://github.com/rails-api/active_model_serializers/pull/2172) Preserve the namespace when falling back to a superclass serializer
+
+Misc:
+
+- [#2327](https://github.com/rails-api/active_model_serializers/pull/2327) Add support for Ruby 2.6 on Travis CI (@wasifhossain)
+- [#2304](https://github.com/rails-api/active_model_serializers/pull/2304) Slim down bundled gem by excluding test files and docs (@greysteil)
+
+### [v0.10.9 (2019-02-08)](https://github.com/rails-api/active_model_serializers/compare/v0.10.8...v0.10.9)
+
+Fixes:
+
+- [#2288](https://github.com/rails-api/active_model_serializers/pull/2288)
+ Change the fetch method to deal with recyclable key cache strategy.
+ Fixes #2287. (@cintamani, @wasifhossain)
+- [#2307](https://github.com/rails-api/active_model_serializers/pull/2307) Falsey attribute values should not be reevaluated.
+
+Misc:
+
+- [#2309](https://github.com/rails-api/active_model_serializers/pull/2309) Performance and memory usage fixes
+
+### [v0.10.8 (2018-11-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.7...v0.10.8)
+
+Features:
+- [#2279](https://github.com/rails-api/active_model_serializers/pull/2279) Support condition options in serializer link statements
+
+Fixes:
+
+- [#2296](https://github.com/rails-api/active_model_serializers/pull/2296) Fixes #2295 (@Hirurg103)
+ - Fix finding of namespaced serializer and non-namespaced model.
+- [#2289](https://github.com/rails-api/active_model_serializers/pull/2289) Fixes #2255 (@f-mer)
+ - Fix autoloading race condition, especially in Rails 5.
+- [#2299](https://github.com/rails-api/active_model_serializers/pull/2299) Fixes #2270 (@chau-bao-long via #2276)
+ - Fix reflection thread-safety bug
+
+### [v0.10.7 (2017-11-14)](https://github.com/rails-api/active_model_serializers/compare/v0.10.6...v0.10.7)
+
+Regressions Fixed From v0.10.6:
+
+- [#2211](https://github.com/rails-api/active_model_serializers/pull/2211). Fixes #2125, #2160. (@bf4)
+ - Fix polymorphic belongs_to tests; passes on v0.10.5, fails on v0.10.6
+ - Fix JSON:API polymorphic type regression from v0.10.5
+ - Fix JSON:API: for_type_and_id should always inflect_type
+ ```
+ Should Serializer._type ever be inflected?
+ Right now, it won't be, but association.serializer._type will be inflected.
+
+ That's the current behavior.
+ ```
+- [#2216](https://github.com/rails-api/active_model_serializers/pull/2216). Fixes #2132, #2180. (@bf4)
+ - Fix JSON:API: Serialize resource type for unpersisted records (blank id)
+- [#2218](https://github.com/rails-api/active_model_serializers/pull/2218). Fixes #2178. (@bf4)
+ - Fix JSON:API: Make using foreign key on belongs_to opt-in. No effect on polymorphic relationships.
+ ```
+ # set to true to opt-in
+ ActiveModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
+ ```
+
+Features:
+
+- [#2136](https://github.com/rails-api/active_model_serializers/pull/2136) Enable inclusion of sideloaded relationship objects by `key`. (@caomania)
+- [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) ActiveModelSerializers::Model#attributes. Originally in [#1982](https://github.com/rails-api/active_model_serializers/pull/1982). (@bf4)
+- [#2130](https://github.com/rails-api/active_model_serializers/pull/2130) Allow serialized ID to be overwritten for belongs-to relationships. (@greysteil)
+- [#2189](https://github.com/rails-api/active_model_serializers/pull/2189)
+ Update version constraint for jsonapi-renderer to `['>= 0.1.1.beta1', '< 0.3']`
+ (@tagliala)
+
+Fixes:
+
+- [#2022](https://github.com/rails-api/active_model_serializers/pull/2022) Mutation of ActiveModelSerializers::Model now changes the attributes. Originally in [#1984](https://github.com/rails-api/active_model_serializers/pull/1984). (@bf4)
+- [#2200](https://github.com/rails-api/active_model_serializers/pull/2200) Fix deserialization of polymorphic relationships. (@dennis95stumm)
+- [#2214](https://github.com/rails-api/active_model_serializers/pull/2214) Fail if unable to infer collection type with json adapter. (@jmeredith16)
+- [#2149](https://github.com/rails-api/active_model_serializers/pull/2149) Always include self, first, last pagination link. (@mecampbellsoup)
+- [#2179](https://github.com/rails-api/active_model_serializers/pull/2179) Fixes #2173, Pass block to Enumerator.new. (@drn)
+
+Misc:
+
+- [#2176](https://github.com/rails-api/active_model_serializers/pull/2176) Documentation for global adapter config. (@mrpinsky)
+- [#2215](https://github.com/rails-api/active_model_serializers/pull/2215) Update `serializers.md` documentation to denote alternate use cases for `scope`. (@stratigos)
+- [#2212](https://github.com/rails-api/active_model_serializers/pull/2212) Remove legacy has_many_embed_ids test. (@bf4)
+
### [v0.10.6 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.5...v0.10.6)
Fixes:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4c006f456..0ebdf788f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -51,7 +51,7 @@ please adhere to these standards:
- Provide a description of the changes contained in the pull request.
- Note any specific areas that should be reviewed.
- Include tests.
-- The test suite must pass on [supported Ruby versions](.travis.yml)
+- The test suite must pass on [supported Ruby versions](.github/workflows/ci.yml)
- Include updates to the [documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs)
where applicable.
- Update the
@@ -79,7 +79,7 @@ and bundling gems. (save this script somewhere executable and run from top of A
```bash
#!/usr/bin/env bash
-rcommand='puts YAML.load_file("./.travis.yml")["env"]["matrix"].join(" ").gsub("RAILS_VERSION=", "")'
+rcommand='puts YAML.load_file(".github/workflows/ci.yml").dig("jobs", "test", "strategy", "matrix", "include").map{|v| v["ruby-version"]}.join(" ")'
versions=$(ruby -ryaml -e "$rcommand")
for version in ${versions[@]}; do
diff --git a/Gemfile b/Gemfile
index e854a2048..94c0ca40c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,12 @@
+# frozen_string_literal: true
+
source 'https://rubygems.org'
+
+git_source(:github) do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
+ "https://github.com/#{repo_name}.git"
+end
+
#
# Add a Gemfile.local to locally bundle gems outside of version control
local_gemfile = File.join(File.expand_path('..', __FILE__), 'Gemfile.local')
@@ -7,11 +15,12 @@ eval_gemfile local_gemfile if File.readable?(local_gemfile)
# Specify your gem's dependencies in active_model_serializers.gemspec
gemspec
-version = ENV['RAILS_VERSION'] || '4.2'
+version = ENV['RAILS_VERSION'] || '6.1'
if version == 'master'
gem 'rack', github: 'rack/rack'
gem 'arel', github: 'rails/arel'
+ gem 'rails', github: 'rails/rails'
git 'https://github.com/rails/rails.git' do
gem 'railties'
gem 'activesupport'
@@ -23,6 +32,7 @@ if version == 'master'
end
else
gem_version = "~> #{version}.0"
+ gem 'rails', gem_version
gem 'railties', gem_version
gem 'activesupport', gem_version
gem 'activemodel', gem_version
@@ -36,18 +46,43 @@ end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: (@windows_platforms + [:jruby])
+if ENV['CI']
+ if RUBY_VERSION < '2.4'
+ # Windows: An error occurred while installing nokogiri (1.8.0)
+ gem 'nokogiri', '< 1.7', platforms: @windows_platforms
+ end
+end
+
group :bench do
# https://github.com/rails-api/active_model_serializers/commit/cb4459580a6f4f37f629bf3185a5224c8624ca76
gem 'benchmark-ips', '>= 2.7.2', require: false, group: :development
end
group :test do
- gem 'sqlite3', platform: (@windows_platforms + [:ruby])
- gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
+ platforms(*(@windows_platforms + [:ruby])) do
+ if version == 'master' || version >= '6'
+ gem 'sqlite3', '~> 1.4'
+ else
+ gem 'sqlite3', '~> 1.3.13'
+ end
+ end
+ platforms :jruby do
+ if version == 'master' || version >= '6.0'
+ gem 'activerecord-jdbcsqlite3-adapter', github: 'jruby/activerecord-jdbc-adapter'
+ elsif version == '5.2'
+ gem 'activerecord-jdbcsqlite3-adapter', '~> 52.0'
+ elsif version == '5.1'
+ gem 'activerecord-jdbcsqlite3-adapter', '~> 51.0'
+ elsif version == '5.0'
+ gem 'activerecord-jdbcsqlite3-adapter', '~> 50.0'
+ else
+ gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.0'
+ end
+ end
gem 'codeclimate-test-reporter', require: false
gem 'm', '~> 1.5'
- gem 'pry', '~> 0.10'
- gem 'pry-byebug', '~> 3.4', platform: :ruby
+ gem 'pry', '>= 0.10'
+ gem 'byebug', '~> 8.2' if RUBY_VERSION < '2.2'
end
group :development, :test do
diff --git a/README.md b/README.md
index 5bdcd20d8..5ac89f625 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@
Build Status |
-
-
+
+
|
@@ -13,7 +13,6 @@
-
|
@@ -41,7 +40,7 @@ these methods to the adapter.)
By default ActiveModelSerializers will use the **Attributes Adapter** (no JSON root).
But we strongly advise you to use **JsonApi Adapter**, which
-follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format).
+follows 1.0 of the format specified in [jsonapi.org/format](https://jsonapi.org/format).
Check how to change the adapter in the sections below.
`0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`.
@@ -49,8 +48,6 @@ Check how to change the adapter in the sections below.
`0.10.x` is based on the `0.8.0` code, but with a more flexible
architecture. We'd love your help. [Learn how you can help here.](CONTRIBUTING.md)
-It is generally safe and recommended to use the master branch.
-
## Installation
Add this line to your application's Gemfile:
@@ -77,9 +74,9 @@ More information is available in the [Guides](docs) and
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new)
and see our [contributing guide](CONTRIBUTING.md).
-If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers).
+If you have a question, please [post to Stack Overflow](https://stackoverflow.com/questions/tagged/active-model-serializers).
-If you'd like to chat, we have a [community slack](http://amserializers.herokuapp.com).
+If you'd like to chat, we have a [community slack](https://amserializers.herokuapp.com).
Thanks!
@@ -89,14 +86,14 @@ If you're reading this at https://github.com/rails-api/active_model_serializers
reading documentation for our `master`, which may include features that have not
been released yet. Please see below for the documentation relevant to you.
-- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
-- [0.10.6 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.6)
- - [](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
- - [Guides](docs)
+- [0.10 (0-10-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-10-stable)
+- [0.10.10 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.10)
+ - [](https://www.rubydoc.info/gems/active_model_serializers/0.10.10)
+ - [Guides](docs)
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
- - [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
+ - [](https://www.rubydoc.info/gems/active_model_serializers/0.9.7)
- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
- - [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable)
+ - [](https://www.rubydoc.info/gems/active_model_serializers/0.8.4)
## High-level behavior
@@ -173,12 +170,12 @@ The original design is also available [here](https://github.com/rails-api/active
### ActiveModel::Serializer
-An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb)
+An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/master/activemodel/lib/active_model/serialization.rb)
and exposes an `attributes` method, among a few others.
It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
It may be useful to think of it as a
-[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
+[presenter](https://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
#### ActiveModel::CollectionSerializer
@@ -187,10 +184,10 @@ and, if there is no serializer, primitives.
### ActiveModelSerializers::Adapter::Base
-The **`ActiveModelSerializeres::Adapter::Base`** describes the structure of the JSON document generated from a
+The **`ActiveModelSerializers::Adapter::Base`** describes the structure of the JSON document generated from a
serializer. For example, the `Attributes` example represents each serializer as its
unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON
-API](http://jsonapi.org/) document.
+API](https://jsonapi.org/) document.
### ActiveModelSerializers::SerializableResource
@@ -231,7 +228,7 @@ High-level overview:
- `:each_serializer` specifies the serializer for each resource in the collection.
- For a **single resource**, the `:serializer` option is the resource serializer.
- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
- [`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5).
+ [`ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
The remaining options are serializer options.
Details:
@@ -256,7 +253,7 @@ Details:
2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)`
1. **ActiveModel::Serializer::CollectionSerializer#new**
1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts
- is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16).
+ is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/lib/active_model/serializer/collection_serializer.rb#L77-L79).
1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
resource as defined by the serializer.
@@ -268,11 +265,11 @@ to know about, but not part of ActiveModelSerializers.)
- An `ActiveRecord::Base` object.
- Any Ruby object that passes the
- [Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
- [code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb).
+ [Lint](https://www.rubydoc.info/gems/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
+ [(code)](lib/active_model/serializer/lint.rb).
ActiveModelSerializers provides a
-[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb),
+[`ActiveModelSerializers::Model`](lib/active_model_serializers/model.rb),
which is a simple serializable PORO (Plain-Old Ruby Object).
`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code.
@@ -300,7 +297,7 @@ ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'),
## Semantic Versioning
-This project adheres to [semver](http://semver.org/)
+This project adheres to [semver](https://semver.org/)
## Contributing
diff --git a/Rakefile b/Rakefile
index 6ba0c2bc9..e7bebff77 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,12 +1,10 @@
+# frozen_string_literal: true
+
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
-begin
- require 'simplecov'
-rescue LoadError # rubocop:disable Lint/HandleExceptions
-end
import('lib/tasks/rubocop.rake')
Bundler::GemHelper.install_tasks
@@ -54,13 +52,20 @@ namespace :test do
# https://github.com/rails/rails/blob/3d590add45/railties/lib/rails/generators/app_base.rb#L345-L363
_bundle_command = Gem.bin_path('bundler', 'bundle')
require 'bundler'
- Bundler.with_clean_env do
+ with_clean_env = proc do
isolated_test_files.all? do |test_file|
command = "-w -I#{dir}/lib -I#{dir}/test #{Shellwords.shellescape(test_file)}"
full_command = %("#{Gem.ruby}" #{command})
system(full_command)
end or fail 'Failures' # rubocop:disable Style/AndOr
end
+ bundler_method =
+ if Bundler.respond_to?(:with_unbundled_env)
+ :with_unbundled_env
+ else
+ :with_clean_env
+ end
+ Bundler.public_send(bundler_method, &with_clean_env)
end
end
diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec
index 805c99c8a..2d1855e8d 100644
--- a/active_model_serializers.gemspec
+++ b/active_model_serializers.gemspec
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
@@ -14,14 +15,13 @@ Gem::Specification.new do |spec|
spec.homepage = 'https://github.com/rails-api/active_model_serializers'
spec.license = 'MIT'
- spec.files = `git ls-files -z`.split("\x0")
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
+ spec.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.md', 'lib/**/*']
spec.require_paths = ['lib']
spec.executables = []
spec.required_ruby_version = '>= 2.1'
- rails_versions = ['>= 4.1', '< 6']
+ rails_versions = ['>= 4.1', '< 7.1']
spec.add_runtime_dependency 'activemodel', rails_versions
# 'activesupport', rails_versions
# 'builder'
@@ -39,10 +39,10 @@ Gem::Specification.new do |spec|
# 'activesupport', rails_versions
# 'i18n,
# 'tzinfo'
- # 'minitest'
+ spec.add_development_dependency 'minitest', ['~> 5.0', '< 5.11']
# 'thread_safe'
- spec.add_runtime_dependency 'jsonapi-renderer', ['>= 0.1.1.beta1', '< 0.2']
+ spec.add_runtime_dependency 'jsonapi-renderer', ['>= 0.1.1.beta1', '< 0.3']
spec.add_runtime_dependency 'case_transform', '>= 0.2'
spec.add_development_dependency 'activerecord', rails_versions
@@ -54,10 +54,9 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'kaminari', ' ~> 0.16.3'
spec.add_development_dependency 'will_paginate', '~> 3.0', '>= 3.0.7'
- spec.add_development_dependency 'bundler', '~> 1.6'
- spec.add_development_dependency 'simplecov', '~> 0.11'
+ spec.add_development_dependency 'bundler'
spec.add_development_dependency 'timecop', '~> 0.7'
- spec.add_development_dependency 'grape', ['>= 0.13', '< 0.19.1']
+ spec.add_development_dependency 'grape', '>= 0.13'
spec.add_development_dependency 'json_schema'
- spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0']
+ spec.add_development_dependency 'rake', '>= 10.0'
end
diff --git a/appveyor.yml b/appveyor.yml
index 7ecfa13ad..0901a78d8 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -4,20 +4,22 @@ skip_tags: true
environment:
JRUBY_OPTS: "--dev -J-Xmx1024M --debug"
+ RAILS_VERSION: 5.2
matrix:
- - ruby_version: "Ruby21"
- - ruby_version: "Ruby21-x64"
+ - ruby_version: "Ruby23"
+ - ruby_version: "Ruby23-x64"
cache:
- vendor/bundle
install:
- SET PATH=C:\%ruby_version%\bin;%PATH%
- - gem update --system
- - gem uninstall bundler -a -x
- - gem install bundler -v 1.13.7
+ - gem uninstall bundler -x
+ - # gem update --system 2.7.9
+ - gem install bundler -v '1.17.3'
- bundle env
- - bundle install --path=vendor/bundle --retry=3 --jobs=3
+ - bundle check || bundle install --path=vendor/bundle --retry=3 --jobs=3
+ - bundle clean --force
before_test:
- ruby -v
diff --git a/docs/STYLE.md b/docs/STYLE.md
index ccd75dd40..236c5138f 100644
--- a/docs/STYLE.md
+++ b/docs/STYLE.md
@@ -18,8 +18,6 @@
- [Improve code quality](https://codeclimate.com/github/rails-api/active_model_serializers/code?sort=smell_count&sort_direction=desc).
-- [Improve amount of code exercised by tests](https://codeclimate.com/github/rails-api/active_model_serializers/coverage?sort=covered_percent&sort_direction=asc).
-
- [Fix RuboCop (Style) TODOS](https://github.com/rails-api/active_model_serializers/blob/master/.rubocop_todo.yml).
- Delete and offsense, run `rake rubocop` (or possibly `rake rubocop:auto_correct`),
and [submit a PR](CONTRIBUTING.md#submitting-a-pull-request-pr).
diff --git a/docs/general/adapters.md b/docs/general/adapters.md
index 84fc4e627..39d85f6b4 100644
--- a/docs/general/adapters.md
+++ b/docs/general/adapters.md
@@ -36,6 +36,12 @@ The `Attributes` adapter does not include a root key. It is just the serialized
Use either the `JSON` or `JSON API` adapters if you want the response document to have a root key.
+***IMPORTANT***: Adapter configuration has *no effect* on a serializer instance
+being used directly. That is, `UserSerializer.new(user).as_json` will *always*
+behave as if the adapter were the 'Attributes' adapter. See [Outside Controller
+Usage](../howto/outside_controller_use.md) for more details on recommended
+usage.
+
## Built in Adapters
### Attributes - Default
diff --git a/docs/general/configuration_options.md b/docs/general/configuration_options.md
index 83f8890d7..0f9b2aaee 100644
--- a/docs/general/configuration_options.md
+++ b/docs/general/configuration_options.md
@@ -57,6 +57,7 @@ still prefer the render option `:key_transform` over this setting.
application, setting `config.key_transform` to `:unaltered` will provide a performance boost.*
##### default_includes
+
What relationships to serialize by default. Default: `'*'`, which includes one level of related
objects. See [includes](adapters.md#included) for more info.
@@ -110,6 +111,18 @@ ActiveModelSerializers.config.serializer_lookup_chain.unshift(
See [lookup_chain.rb](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/lookup_chain.rb) for further explanations and examples.
+#### use_sha1_digests
+
+Determines which hashing algorithm to use internally when caching serializers.
+
+Possible values:
+
+- `true`
+- `false` (default)
+
+When `true`, ActiveModelSerializers will use SHA1. Otherwise, it will default to using MD5.
+Be warned that changing this value may result in serializer caches being invalidated.
+
## JSON API
##### jsonapi_resource_type
@@ -162,6 +175,21 @@ Default: `{}`.
*Used when `jsonapi_include_toplevel_object` is `true`*
+##### jsonapi_use_foreign_key_on_belongs_to_relationship
+
+When true, the relationship will determine its resource object identifier
+without calling the association or its serializer. This can be useful when calling
+the association object is triggering unnecessary queries.
+
+For example, if a `comment` belongs to a `post`, and the comment
+uses the foreign key `post_id`, we can determine the resource object
+identifier `id` as `comment.post_id` and the `type` from the association options.
+Or quite simply, it behaves as `belongs_to :post, type: :posts, foreign_key: :post_id`.
+
+Note: This option has *no effect* on polymorphic associations as we cannot reliably
+determine the associated object's type without instantiating it.
+
+Default: `false`.
## Hooks
diff --git a/docs/general/logging.md b/docs/general/logging.md
index 321bf5d8b..f5c34dd17 100644
--- a/docs/general/logging.md
+++ b/docs/general/logging.md
@@ -16,6 +16,5 @@ ActiveModelSerializers.logger = Logger.new(STDOUT)
You can also disable the logger, just put this in `config/initializers/active_model_serializers.rb`:
```ruby
-require 'active_model_serializers'
-ActiveSupport::Notifications.unsubscribe(ActiveModelSerializers::Logging::RENDER_EVENT)
+ActiveModelSerializers.logger = Logger.new(IO::NULL)
```
diff --git a/docs/general/rendering.md b/docs/general/rendering.md
index af2d886f5..2a7626fb5 100644
--- a/docs/general/rendering.md
+++ b/docs/general/rendering.md
@@ -290,4 +290,4 @@ See [Usage outside of a controller](../howto/outside_controller_use.md#serializi
## Pagination
-See [How to add pagination links](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md).
+See [How to add pagination links](../howto/add_pagination_links.md).
diff --git a/docs/general/serializers.md b/docs/general/serializers.md
index 5b23ba0f4..f7f0ccc2f 100644
--- a/docs/general/serializers.md
+++ b/docs/general/serializers.md
@@ -65,7 +65,7 @@ Where:
- `virtual_value:`
- `polymorphic:` defines if polymorphic relation type should be nested in serialized association.
- `type:` the resource type as used by JSON:API, especially on a `belongs_to` relationship.
- - `class_name:` used to determine `type` when `type` not given
+ - `class_name:` the (String) model name used to determine `type`, when `type` is not given. e.g. `class_name: "Comment"` would imply the type `comments`
- `foreign_key:` used by JSON:API on a `belongs_to` relationship to avoid unnecessarily loading the association object.
- `namespace:` used when looking up the serializer and `serializer` is not given. Falls back to the parent serializer's `:namespace` instance options, which, when present, comes from the render options. See [Rendering#namespace](rendering.md#namespace] for more details.
- optional: `&block` is a context that returns the association's attributes.
@@ -81,6 +81,7 @@ e.g.
```ruby
has_one :bio
has_one :blog, key: :site
+has_one :blog, class_name: "Blog"
has_one :maker, virtual_value: { id: 1 }
has_one :blog do |serializer|
@@ -114,6 +115,7 @@ e.g.
has_many :comments
has_many :comments, key: :reviews
has_many :comments, serializer: CommentPreviewSerializer
+has_many :comments, class_name: "Comment"
has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]
has_many :comments, key: :last_comments do
last(1)
@@ -127,6 +129,7 @@ e.g.
```ruby
belongs_to :author, serializer: AuthorPreviewSerializer
belongs_to :author, key: :writer
+belongs_to :author, class_name: "Author"
belongs_to :post
belongs_to :blog
def blog
@@ -235,6 +238,15 @@ link :other, 'https://example.com/resource'
link(:posts) { link_author_posts_url(object) }
```
+Just like attributes, links also support conditions in options
+```ruby
+link(:secret, if: :internal?) { object.secret_link }
+
+def internal?
+ instance_options[:context] == :internal
+end
+```
+
#### #object
The object being serialized.
@@ -294,6 +306,8 @@ end
Whether you write the method as above or as `object.comments.where(created_by: scope)`
is a matter of preference (assuming `scope_name` has been set).
+Keep in mind that the scope can be set to any available controller reference. This can be utilized to provide access to any other data scopes or presentation helpers.
+
##### Controller Authorization Context
In the controller, the scope/scope_name options are equal to
@@ -311,7 +325,7 @@ current authorization scope when you call `render :json`.
called on every request. This was [also a problem](https://github.com/rails-api/active_model_serializers/pull/1252#issuecomment-159810477)
in [`0.9`](https://github.com/rails-api/active_model_serializers/tree/0-9-stable#customizing-scope).
-We can change the scope from `current_user` to `view_context`.
+We can change the scope from `current_user` to `view_context`, which is included in subclasses of `ActionController::Base`.
```diff
class SomeController < ActionController::Base
@@ -379,6 +393,7 @@ class PostsController < ActionController::Base
end
end
```
+Note that any controller reference which provides the desired scope is acceptable, such as another controller method for loading a different resource or reference to helpers. For example, `ActionController::API` does not include `ActionView::ViewContext`, and would need a different reference for passing any helpers into a serializer via `serialization_scope`.
#### #read_attribute_for_serialization(key)
@@ -437,7 +452,7 @@ serializer classes.
## More Info
-For more information, see [the Serializer class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer.rb)
+For more information, see [the Serializer class on GitHub](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/lib/active_model/serializer.rb)
## Overriding association methods
@@ -471,7 +486,7 @@ the `ActiveModel::Serializer.serializer_for` method to return a serializer class
```ruby
class MySerializer < ActiveModel::Serializer
def self.serializer_for(model, options)
- return SparseAdminSerializer if model.class == 'Admin'
+ return SparseAdminSerializer if model.class.name == 'Admin'
super
end
diff --git a/docs/howto/add_root_key.md b/docs/howto/add_root_key.md
index 82a4ab6fc..6d8c3269c 100644
--- a/docs/howto/add_root_key.md
+++ b/docs/howto/add_root_key.md
@@ -18,6 +18,13 @@ In order to add the root key you need to use the ```JSON``` Adapter, you can cha
ActiveModelSerializers.config.adapter = :json
```
+Note that adapter configuration has no effect on a serializer that is called
+directly, e.g. in a serializer unit test. Instead, something like
+`UserSerializer.new(user).as_json` will *always* behave as if the adapter were
+the 'Attributes' adapter. See [Outside Controller
+Usage](../howto/outside_controller_use.md) for more details on recommended
+usage.
+
You can also specify a class as adapter, as long as it complies with the ActiveModelSerializers adapters interface.
It will add the root key to all your serialized endpoints.
diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb
index ea84c6743..904c758cc 100644
--- a/lib/action_controller/serialization.rb
+++ b/lib/action_controller/serialization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_support/core_ext/class/attribute'
require 'active_model_serializers/serialization_context'
@@ -21,7 +23,15 @@ def serialization_scope(scope)
end
def namespace_for_serializer
- @namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
+ @namespace_for_serializer ||= namespace_for_class(self.class) unless namespace_for_class(self.class) == Object
+ end
+
+ def namespace_for_class(klass)
+ if Module.method_defined?(:module_parent)
+ klass.module_parent
+ else
+ klass.parent
+ end
end
def serialization_scope
diff --git a/lib/active_model/serializable_resource.rb b/lib/active_model/serializable_resource.rb
index 0e1c8e2d2..75a8d505b 100644
--- a/lib/active_model/serializable_resource.rb
+++ b/lib/active_model/serializable_resource.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'set'
module ActiveModel
diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb
index 9d00e6fbf..45e929ed5 100644
--- a/lib/active_model/serializer.rb
+++ b/lib/active_model/serializer.rb
@@ -1,4 +1,5 @@
-require 'thread_safe'
+# frozen_string_literal: true
+
require 'jsonapi/include_directive'
require 'active_model/serializer/collection_serializer'
require 'active_model/serializer/array_serializer'
@@ -18,16 +19,17 @@ class Serializer
# @see #serializable_hash for more details on these valid keys.
SERIALIZABLE_HASH_VALID_KEYS = [:only, :except, :methods, :include, :root].freeze
extend ActiveSupport::Autoload
- autoload :Adapter
- autoload :Null
- autoload :Attribute
- autoload :Association
- autoload :Reflection
- autoload :SingularReflection
- autoload :CollectionReflection
- autoload :BelongsToReflection
- autoload :HasOneReflection
- autoload :HasManyReflection
+ eager_autoload do
+ autoload :Adapter
+ autoload :Null
+ autoload :Attribute
+ autoload :Link
+ autoload :Association
+ autoload :Reflection
+ autoload :BelongsToReflection
+ autoload :HasOneReflection
+ autoload :HasManyReflection
+ end
include ActiveSupport::Configurable
include Caching
@@ -70,7 +72,7 @@ def self.serializer_lookup_chain_for(klass, namespace = nil)
# Used to cache serializer name => serializer class
# when looked up by Serializer.get_serializer_for.
def self.serializers_cache
- @serializers_cache ||= ThreadSafe::Cache.new
+ @serializers_cache ||= Concurrent::Map.new
end
# @api private
@@ -91,7 +93,7 @@ def self.get_serializer_for(klass, namespace = nil)
if serializer_class
serializer_class
elsif klass.superclass
- get_serializer_for(klass.superclass)
+ get_serializer_for(klass.superclass, namespace)
else
nil # No serializer found
end
@@ -142,7 +144,10 @@ def config.array_serializer
# Make JSON API top-level jsonapi member opt-in
# ref: http://jsonapi.org/format/#document-top-level
config.jsonapi_include_toplevel_object = false
+ config.jsonapi_use_foreign_key_on_belongs_to_relationship = false
config.include_data_default = true
+ # Raise ActiveModel::Serializer::CollectionSerializer::CannotInferRootKeyError when cannot infer root key from collection type
+ config.raise_cannot_infer_root_key_error = true
# For configuring how serializers are found.
# This should be an array of procs.
@@ -274,9 +279,14 @@ def self.associate(reflection)
# link(:self) { "http://example.com/resource/#{object.id}" }
# @example
# link :resource, "http://example.com/resource"
+ # @example
+ # link(:callback, if: :internal?), { "http://example.com/callback" }
#
- def self.link(name, value = nil, &block)
- _links[name] = block || value
+ def self.link(name, *args, &block)
+ options = args.extract_options!
+ # For compatibility with the use case of passing link directly as string argument
+ # without block, we are creating a wrapping block
+ _links[name] = Link.new(name, options, block || ->(_serializer) { args.first })
end
# Set the JSON API meta attribute of a serializer.
@@ -337,10 +347,10 @@ def attributes(requested_attrs = nil, reload = false)
# @return [Enumerator]
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
include_slice ||= include_directive
- return Enumerator.new unless object
+ return Enumerator.new {} unless object
Enumerator.new do |y|
- self.class._reflections.each do |key, reflection|
+ (self.instance_reflections ||= self.class._reflections.deep_dup).each do |key, reflection|
next if reflection.excluded?(self)
next unless include_directive.key?(key)
@@ -356,6 +366,10 @@ def associations(include_directive = ActiveModelSerializers.default_include_dire
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
adapter_options ||= {}
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
+
+ fieldset = adapter_options[:fieldset]
+ options[:fields] = fieldset.fields_for(json_key) if fieldset
+
resource = attributes_hash(adapter_options, options, adapter_instance)
relationships = associations_hash(adapter_options, options, adapter_instance)
resource.merge(relationships)
@@ -370,7 +384,12 @@ def as_json(adapter_opts = nil)
# Used by adapter as resource root.
def json_key
- root || _type || object.class.model_name.to_s.underscore
+ root || _type ||
+ begin
+ object.class.model_name.to_s.underscore
+ rescue ArgumentError
+ 'anonymous_object'
+ end
end
def read_attribute_for_serialization(attr)
@@ -404,6 +423,6 @@ def associations_hash(adapter_options, options, adapter_instance)
protected
- attr_accessor :instance_options
+ attr_accessor :instance_options, :instance_reflections
end
end
diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb
index 6b5f30ca7..8a8ee4192 100644
--- a/lib/active_model/serializer/adapter.rb
+++ b/lib/active_model/serializer/adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model_serializers/adapter'
require 'active_model_serializers/deprecate'
diff --git a/lib/active_model/serializer/adapter/attributes.rb b/lib/active_model/serializer/adapter/attributes.rb
index e04e5fd8c..6d7602a20 100644
--- a/lib/active_model/serializer/adapter/attributes.rb
+++ b/lib/active_model/serializer/adapter/attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Adapter
diff --git a/lib/active_model/serializer/adapter/base.rb b/lib/active_model/serializer/adapter/base.rb
index 013a9705a..34c39540a 100644
--- a/lib/active_model/serializer/adapter/base.rb
+++ b/lib/active_model/serializer/adapter/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Adapter
diff --git a/lib/active_model/serializer/adapter/json.rb b/lib/active_model/serializer/adapter/json.rb
index 1998a4c65..404b0d8d6 100644
--- a/lib/active_model/serializer/adapter/json.rb
+++ b/lib/active_model/serializer/adapter/json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Adapter
diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb
index 13777cdc7..2a1186b2e 100644
--- a/lib/active_model/serializer/adapter/json_api.rb
+++ b/lib/active_model/serializer/adapter/json_api.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Adapter
diff --git a/lib/active_model/serializer/adapter/null.rb b/lib/active_model/serializer/adapter/null.rb
index 906953d16..12f3ee85e 100644
--- a/lib/active_model/serializer/adapter/null.rb
+++ b/lib/active_model/serializer/adapter/null.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Adapter
diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb
index 2e768deb4..93b3beb8f 100644
--- a/lib/active_model/serializer/array_serializer.rb
+++ b/lib/active_model/serializer/array_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model/serializer/collection_serializer'
module ActiveModel
diff --git a/lib/active_model/serializer/association.rb b/lib/active_model/serializer/association.rb
index 7ce82316d..7aeee338a 100644
--- a/lib/active_model/serializer/association.rb
+++ b/lib/active_model/serializer/association.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model/serializer/lazy_association'
module ActiveModel
diff --git a/lib/active_model/serializer/attribute.rb b/lib/active_model/serializer/attribute.rb
index d3e006faa..f405ceb55 100644
--- a/lib/active_model/serializer/attribute.rb
+++ b/lib/active_model/serializer/attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model/serializer/field'
module ActiveModel
diff --git a/lib/active_model/serializer/belongs_to_reflection.rb b/lib/active_model/serializer/belongs_to_reflection.rb
index 04bbc6fc5..b299e87b0 100644
--- a/lib/active_model/serializer/belongs_to_reflection.rb
+++ b/lib/active_model/serializer/belongs_to_reflection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
# @api private
diff --git a/lib/active_model/serializer/collection_serializer.rb b/lib/active_model/serializer/collection_serializer.rb
index 44b806a17..d60d331a6 100644
--- a/lib/active_model/serializer/collection_serializer.rb
+++ b/lib/active_model/serializer/collection_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
class CollectionSerializer
@@ -19,11 +21,10 @@ def success?
# @api private
def serializable_hash(adapter_options, options, adapter_instance)
- include_directive = ActiveModel::Serializer.include_directive_from_options(adapter_options)
- adapter_options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, include_directive)
- adapter_opts = adapter_options.merge(include_directive: include_directive)
+ options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
+ options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, options[:include_directive])
serializers.map do |serializer|
- serializer.serializable_hash(adapter_opts, options, adapter_instance)
+ serializer.serializable_hash(adapter_options, options, adapter_instance)
end
end
@@ -46,7 +47,12 @@ def json_key
# 3. get from collection name, if a named collection
key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil
# 4. key may be nil for empty collection and no serializer option
- key && key.pluralize
+ key &&= key.pluralize
+ if raise_cannot_infer_root_key_error?
+ # 5. fail if the key cannot be determined
+ key || fail(CannotInferRootKeyError, 'Cannot infer root key from collection type. Please specify the root or each_serializer option, or render a JSON String')
+ end
+ key
end
# rubocop:enable Metrics/CyclomaticComplexity
@@ -57,12 +63,18 @@ def paginated?
object.respond_to?(:size)
end
+ class CannotInferRootKeyError < StandardError; end
+
protected
attr_reader :serializers, :options
private
+ def raise_cannot_infer_root_key_error?
+ ActiveModelSerializers.config.raise_cannot_infer_root_key_error
+ end
+
def serializers_from_resources
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
object.map do |resource|
diff --git a/lib/active_model/serializer/concerns/caching.rb b/lib/active_model/serializer/concerns/caching.rb
index 2a030b682..cdcd56852 100644
--- a/lib/active_model/serializer/concerns/caching.rb
+++ b/lib/active_model/serializer/concerns/caching.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
UndefinedCacheKey = Class.new(StandardError)
@@ -54,7 +56,8 @@ def _cache_digest
def digest_caller_file(caller_line)
serializer_file_path = caller_line[CALLER_FILE]
serializer_file_contents = IO.read(serializer_file_path)
- Digest::MD5.hexdigest(serializer_file_contents)
+ algorithm = ActiveModelSerializers.config.use_sha1_digests ? Digest::SHA1 : Digest::MD5
+ algorithm.hexdigest(serializer_file_contents)
rescue TypeError, Errno::ENOENT
warn <<-EOF.strip_heredoc
Cannot digest non-existent file: '#{caller_line}'.
@@ -281,7 +284,9 @@ def expand_cache_key(parts)
# Use object's cache_key if available, else derive a key from the object
# Pass the `key` option to the `cache` declaration or override this method to customize the cache key
def object_cache_key
- if object.respond_to?(:cache_key)
+ if object.respond_to?(:cache_key_with_version)
+ object.cache_key_with_version
+ elsif object.respond_to?(:cache_key)
object.cache_key
elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key]))
object_time_safe = object.updated_at
diff --git a/lib/active_model/serializer/error_serializer.rb b/lib/active_model/serializer/error_serializer.rb
index d0e708091..6c6a8cdbd 100644
--- a/lib/active_model/serializer/error_serializer.rb
+++ b/lib/active_model/serializer/error_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
class ErrorSerializer < ActiveModel::Serializer
diff --git a/lib/active_model/serializer/errors_serializer.rb b/lib/active_model/serializer/errors_serializer.rb
index 1fd924d54..34fee1d22 100644
--- a/lib/active_model/serializer/errors_serializer.rb
+++ b/lib/active_model/serializer/errors_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model/serializer/error_serializer'
module ActiveModel
diff --git a/lib/active_model/serializer/field.rb b/lib/active_model/serializer/field.rb
index 6299b0990..32973bbd4 100644
--- a/lib/active_model/serializer/field.rb
+++ b/lib/active_model/serializer/field.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
# Holds all the meta-data about a field (i.e. attribute or association) as it was
diff --git a/lib/active_model/serializer/fieldset.rb b/lib/active_model/serializer/fieldset.rb
index efa3187c7..78609a4e5 100644
--- a/lib/active_model/serializer/fieldset.rb
+++ b/lib/active_model/serializer/fieldset.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
class Fieldset
@@ -10,7 +12,7 @@ def fields
end
def fields_for(type)
- fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
+ fields[type.to_s.singularize.to_sym] || fields[type.to_s.pluralize.to_sym]
end
protected
diff --git a/lib/active_model/serializer/has_many_reflection.rb b/lib/active_model/serializer/has_many_reflection.rb
index 99f6f63cc..33d48d280 100644
--- a/lib/active_model/serializer/has_many_reflection.rb
+++ b/lib/active_model/serializer/has_many_reflection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
# @api private
diff --git a/lib/active_model/serializer/has_one_reflection.rb b/lib/active_model/serializer/has_one_reflection.rb
index a385009bc..51e1523f1 100644
--- a/lib/active_model/serializer/has_one_reflection.rb
+++ b/lib/active_model/serializer/has_one_reflection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
# @api private
diff --git a/lib/active_model/serializer/lazy_association.rb b/lib/active_model/serializer/lazy_association.rb
index 8c4dad612..ed7bd65c2 100644
--- a/lib/active_model/serializer/lazy_association.rb
+++ b/lib/active_model/serializer/lazy_association.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
# @api private
@@ -7,11 +9,12 @@ class Serializer
delegate :collection?, to: :reflection
def reflection_options
- @reflection_options ||= reflection.options.dup.reject { |k, _| !REFLECTION_OPTIONS.include?(k) }
+ @reflection_options ||= reflection.options.select { |k, _| REFLECTION_OPTIONS.include?(k) }
end
def object
- @object ||= reflection.value(
+ return @object if defined?(@object)
+ @object = reflection.value(
association_options.fetch(:parent_serializer),
association_options.fetch(:include_slice)
)
@@ -76,6 +79,7 @@ def instantiate_serializer(object)
serializer_options[:serializer_context_class] = association_options.fetch(:parent_serializer).class
serializer = reflection_options.fetch(:serializer, nil)
serializer_options[:serializer] = serializer if serializer
+ serializer_options[:namespace] = reflection_options[:namespace] if reflection_options[:namespace]
serializer_class.new(object, serializer_options)
end
diff --git a/lib/active_model/serializer/link.rb b/lib/active_model/serializer/link.rb
new file mode 100644
index 000000000..c54eb9121
--- /dev/null
+++ b/lib/active_model/serializer/link.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'active_model/serializer/field'
+
+module ActiveModel
+ class Serializer
+ # Holds all the data about a serializer link
+ #
+ # @example
+ # class PostSerializer < ActiveModel::Serializer
+ # link :callback, if: :internal? do
+ # object.callback_link
+ # end
+ #
+ # def internal?
+ # instance_options[:internal] == true
+ # end
+ # end
+ #
+ class Link < Field
+ end
+ end
+end
diff --git a/lib/active_model/serializer/lint.rb b/lib/active_model/serializer/lint.rb
index c40cebeb1..8c4ef8fcc 100644
--- a/lib/active_model/serializer/lint.rb
+++ b/lib/active_model/serializer/lint.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
module Lint
diff --git a/lib/active_model/serializer/null.rb b/lib/active_model/serializer/null.rb
index 818bbbfa2..d36fca6e7 100644
--- a/lib/active_model/serializer/null.rb
+++ b/lib/active_model/serializer/null.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
class Null < Serializer
diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb
index 2e5cc2a16..170ea7713 100644
--- a/lib/active_model/serializer/reflection.rb
+++ b/lib/active_model/serializer/reflection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model/serializer/field'
require 'active_model/serializer/association'
@@ -85,8 +87,8 @@ def initialize(*)
# meta ids: ids
# end
# end
- def link(name, value = nil)
- options[:links][name] = block_given? ? Proc.new : value
+ def link(name, value = nil, &block)
+ options[:links][name] = block_given? ? block : value
:nil
end
@@ -100,8 +102,8 @@ def link(name, value = nil)
# href object.blog.id.to_s
# meta(id: object.blog.id)
# end
- def meta(value = nil)
- options[:meta] = block_given? ? Proc.new : value
+ def meta(value = nil, &block)
+ options[:meta] = block_given? ? block : value
:nil
end
@@ -140,7 +142,7 @@ def collection?
def include_data?(include_slice)
include_data_setting = options[:include_data_setting]
case include_data_setting
- when :if_sideloaded then include_slice.key?(name)
+ when :if_sideloaded then include_slice.key?(options.fetch(:key, name))
when true then true
when false then false
else fail ArgumentError, "Unknown include_data_setting '#{include_data_setting.inspect}'"
@@ -151,6 +153,9 @@ def include_data?(include_slice)
# @yield [ActiveModel::Serializer]
# @return [:nil, associated resource or resource collection]
def value(serializer, include_slice)
+ # NOTE(BF): This method isn't thread-safe because the _reflections class attribute is not thread-safe
+ # Therefore, when we build associations from reflections, we dup the entire reflection instance.
+ # Better solutions much appreciated!
@object = serializer.object
@scope = serializer.scope
diff --git a/lib/active_model/serializer/version.rb b/lib/active_model/serializer/version.rb
index e692240a3..96ba7a425 100644
--- a/lib/active_model/serializer/version.rb
+++ b/lib/active_model/serializer/version.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
- VERSION = '0.10.6'.freeze
+ VERSION = '0.10.13'.freeze
end
end
diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb
index 18cdd9f70..da9f9a040 100644
--- a/lib/active_model_serializers.rb
+++ b/lib/active_model_serializers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_model'
require 'active_support'
require 'active_support/core_ext/object/with_options'
@@ -5,16 +7,19 @@
require 'active_support/json'
module ActiveModelSerializers
extend ActiveSupport::Autoload
- autoload :Model
- autoload :Callbacks
- autoload :Deserialization
- autoload :SerializableResource
- autoload :Logging
- autoload :Test
- autoload :Adapter
- autoload :JsonPointer
- autoload :Deprecate
- autoload :LookupChain
+ eager_autoload do
+ autoload :Model
+ autoload :Callbacks
+ autoload :SerializableResource
+ autoload :SerializationContext
+ autoload :Logging
+ autoload :Test
+ autoload :Adapter
+ autoload :JsonPointer
+ autoload :Deprecate
+ autoload :LookupChain
+ autoload :Deserialization
+ end
class << self; attr_accessor :logger; end
self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
@@ -46,8 +51,13 @@ def self.silence_warnings
$VERBOSE = original_verbose
end
+ def self.eager_load!
+ super
+ ActiveModel::Serializer.eager_load!
+ end
+
require 'active_model/serializer/version'
require 'active_model/serializer'
require 'active_model/serializable_resource'
- require 'active_model_serializers/railtie' if defined?(::Rails)
+ require 'active_model_serializers/railtie' if defined?(::Rails::Railtie)
end
diff --git a/lib/active_model_serializers/adapter.rb b/lib/active_model_serializers/adapter.rb
index 98caab44f..274e5fee7 100644
--- a/lib/active_model_serializers/adapter.rb
+++ b/lib/active_model_serializers/adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
UnknownAdapterError = Class.new(ArgumentError)
@@ -35,7 +37,7 @@ def adapter_map
# @return [Array] list of adapter names
def adapters
- adapter_map.keys.sort
+ adapter_map.keys.sort!
end
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
diff --git a/lib/active_model_serializers/adapter/attributes.rb b/lib/active_model_serializers/adapter/attributes.rb
index 79ca7b5ff..ff9fedd64 100644
--- a/lib/active_model_serializers/adapter/attributes.rb
+++ b/lib/active_model_serializers/adapter/attributes.rb
@@ -1,6 +1,16 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class Attributes < Base
+ def initialize(*)
+ super
+
+ fields = instance_options.delete(:fields)
+ fieldset = fields_to_fieldset(fields)
+ instance_options[:fieldset] = ActiveModel::Serializer::Fieldset.new(fieldset) if fieldset
+ end
+
def serializable_hash(options = nil)
options = serialization_options(options)
options[:fields] ||= instance_options[:fields]
@@ -8,6 +18,25 @@ def serializable_hash(options = nil)
self.class.transform_key_casing!(serialized_hash, instance_options)
end
+
+ private
+
+ def fields_to_fieldset(fields)
+ return if fields.nil?
+
+ resource_fields = []
+ relationship_fields = {}
+
+ fields.each do |field|
+ case field
+ when Symbol, String then resource_fields << field
+ when Hash then relationship_fields.merge!(field)
+ else fail ArgumentError, "Unknown conversion of fields to fieldset: '#{field.inspect}' in '#{fields.inspect}'"
+ end
+ end
+
+ relationship_fields.merge!(serializer.json_key.to_sym => resource_fields)
+ end
end
end
end
diff --git a/lib/active_model_serializers/adapter/base.rb b/lib/active_model_serializers/adapter/base.rb
index 851583285..9d5f4b6a1 100644
--- a/lib/active_model_serializers/adapter/base.rb
+++ b/lib/active_model_serializers/adapter/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'case_transform'
module ActiveModelSerializers
diff --git a/lib/active_model_serializers/adapter/json.rb b/lib/active_model_serializers/adapter/json.rb
index 423cfb9fb..78a4a5518 100644
--- a/lib/active_model_serializers/adapter/json.rb
+++ b/lib/active_model_serializers/adapter/json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class Json < Base
diff --git a/lib/active_model_serializers/adapter/json_api.rb b/lib/active_model_serializers/adapter/json_api.rb
index b225416be..187319ed5 100644
--- a/lib/active_model_serializers/adapter/json_api.rb
+++ b/lib/active_model_serializers/adapter/json_api.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# {http://jsonapi.org/format/ JSON API specification}
# rubocop:disable Style/AsciiComments
# TODO: implement!
@@ -22,14 +24,16 @@ module ActiveModelSerializers
module Adapter
class JsonApi < Base
extend ActiveSupport::Autoload
- autoload :Jsonapi
- autoload :ResourceIdentifier
- autoload :Relationship
- autoload :Link
- autoload :PaginationLinks
- autoload :Meta
- autoload :Error
- autoload :Deserialization
+ eager_autoload do
+ autoload :Jsonapi
+ autoload :ResourceIdentifier
+ autoload :Link
+ autoload :PaginationLinks
+ autoload :Meta
+ autoload :Error
+ autoload :Deserialization
+ autoload :Relationship
+ end
def self.default_key_transform
:dash
@@ -49,7 +53,8 @@ def self.fragment_cache(cached_hash, non_cached_hash, root = true)
def initialize(serializer, options = {})
super
@include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
- @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
+ option_fields = options.delete(:fields)
+ @fieldset = ActiveModel::Serializer::Fieldset.new(option_fields) if option_fields
end
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
@@ -344,7 +349,8 @@ def data_for(serializer, include_slice)
data.tap do |resource_object|
next if resource_object.nil?
# NOTE(BF): the attributes are cached above, separately from the relationships, below.
- requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
+ requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
+ requested_associations = requested_fields || '*'
relationships = relationships_for(serializer, requested_associations, include_slice)
resource_object[:relationships] = relationships if relationships.any?
end
@@ -480,7 +486,8 @@ def relationships_for(serializer, requested_associations, include_slice)
# }.reject! {|_,v| v.nil? }
def links_for(serializer)
serializer._links.each_with_object({}) do |(name, value), hash|
- result = Link.new(serializer, value).as_json
+ next if value.excluded?(serializer)
+ result = Link.new(serializer, value.block).as_json
hash[name] = result if result
end
end
diff --git a/lib/active_model_serializers/adapter/json_api/deserialization.rb b/lib/active_model_serializers/adapter/json_api/deserialization.rb
index b79125ac4..1af20d94a 100644
--- a/lib/active_model_serializers/adapter/json_api/deserialization.rb
+++ b/lib/active_model_serializers/adapter/json_api/deserialization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi
@@ -189,7 +191,7 @@ def parse_relationship(assoc_name, assoc_data, options)
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
if polymorphic
- hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'] : nil
+ hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil
end
hash
diff --git a/lib/active_model_serializers/adapter/json_api/error.rb b/lib/active_model_serializers/adapter/json_api/error.rb
index c7b18716c..8f1d32b5b 100644
--- a/lib/active_model_serializers/adapter/json_api/error.rb
+++ b/lib/active_model_serializers/adapter/json_api/error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi < Base
diff --git a/lib/active_model_serializers/adapter/json_api/jsonapi.rb b/lib/active_model_serializers/adapter/json_api/jsonapi.rb
index e94578af6..cb24f0273 100644
--- a/lib/active_model_serializers/adapter/json_api/jsonapi.rb
+++ b/lib/active_model_serializers/adapter/json_api/jsonapi.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi < Base
diff --git a/lib/active_model_serializers/adapter/json_api/link.rb b/lib/active_model_serializers/adapter/json_api/link.rb
index 64e15071a..6fa9f5299 100644
--- a/lib/active_model_serializers/adapter/json_api/link.rb
+++ b/lib/active_model_serializers/adapter/json_api/link.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi
diff --git a/lib/active_model_serializers/adapter/json_api/meta.rb b/lib/active_model_serializers/adapter/json_api/meta.rb
index d889b3eb8..02cf20989 100644
--- a/lib/active_model_serializers/adapter/json_api/meta.rb
+++ b/lib/active_model_serializers/adapter/json_api/meta.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi
diff --git a/lib/active_model_serializers/adapter/json_api/pagination_links.rb b/lib/active_model_serializers/adapter/json_api/pagination_links.rb
index b15f5ba68..a41153a6e 100644
--- a/lib/active_model_serializers/adapter/json_api/pagination_links.rb
+++ b/lib/active_model_serializers/adapter/json_api/pagination_links.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi < Base
@@ -15,17 +17,18 @@ def initialize(collection, adapter_options)
JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext.
Please pass a ':serialization_context' option or
override CollectionSerializer#paginated? to return 'false'.
- EOF
+ EOF
end
end
def as_json
- per_page = collection.try(:per_page) || collection.try(:limit_value) || collection.size
- pages_from.each_with_object({}) do |(key, value), hash|
- params = query_parameters.merge(page: { number: value, size: per_page }).to_query
-
- hash[key] = "#{url(adapter_options)}?#{params}"
- end
+ {
+ self: location_url,
+ first: first_page_url,
+ prev: prev_page_url,
+ next: next_page_url,
+ last: last_page_url
+ }
end
protected
@@ -34,25 +37,43 @@ def as_json
private
- def pages_from
- return {} if collection.total_pages <= FIRST_PAGE
+ def location_url
+ url_for_page(collection.current_page)
+ end
- {}.tap do |pages|
- pages[:self] = collection.current_page
+ def first_page_url
+ url_for_page(1)
+ end
- unless collection.current_page == FIRST_PAGE
- pages[:first] = FIRST_PAGE
- pages[:prev] = collection.current_page - FIRST_PAGE
- end
+ def last_page_url
+ if collection.total_pages == 0
+ url_for_page(FIRST_PAGE)
+ else
+ url_for_page(collection.total_pages)
+ end
+ end
- unless collection.current_page == collection.total_pages
- pages[:next] = collection.current_page + FIRST_PAGE
- pages[:last] = collection.total_pages
- end
+ def prev_page_url
+ return nil if collection.current_page == FIRST_PAGE
+ if collection.current_page > collection.total_pages
+ return url_for_page(collection.total_pages)
end
+ url_for_page(collection.current_page - FIRST_PAGE)
end
- def url(options)
+ def next_page_url
+ return nil if collection.total_pages == 0 ||
+ collection.current_page >= collection.total_pages
+ url_for_page(collection.next_page)
+ end
+
+ def url_for_page(number)
+ params = query_parameters.dup
+ params[:page] = { size: per_page, number: number }
+ "#{url(adapter_options)}?#{params.to_query}"
+ end
+
+ def url(options = {})
@url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
end
@@ -63,6 +84,10 @@ def request_url
def query_parameters
@query_parameters ||= context.query_parameters
end
+
+ def per_page
+ @per_page ||= collection.try(:per_page) || collection.try(:limit_value) || collection.size
+ end
end
end
end
diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb
index 5d7399a35..2879e3eb4 100644
--- a/lib/active_model_serializers/adapter/json_api/relationship.rb
+++ b/lib/active_model_serializers/adapter/json_api/relationship.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi
@@ -43,10 +45,16 @@ def data_for(association)
end
def data_for_one(association)
- if association.belongs_to? &&
- parent_serializer.object.respond_to?(association.reflection.foreign_key)
- id = parent_serializer.object.send(association.reflection.foreign_key)
- type = association.reflection.type.to_s
+ if belongs_to_id_on_self?(association)
+ id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key)
+ type =
+ if association.polymorphic?
+ # We can't infer resource type for polymorphic relationships from the serializer.
+ # We can ONLY know a polymorphic resource type by inspecting each resource.
+ association.lazy_association.serializer.json_key
+ else
+ association.reflection.type.to_s
+ end
ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
else
# TODO(BF): Process relationship without evaluating lazy_association
@@ -86,6 +94,12 @@ def meta_for(association)
meta = association.meta
meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
end
+
+ def belongs_to_id_on_self?(association)
+ parent_serializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship &&
+ association.belongs_to? &&
+ parent_serializer.object.respond_to?(association.reflection.foreign_key)
+ end
end
end
end
diff --git a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb
index 3a235f2be..e321914ea 100644
--- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb
+++ b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb
@@ -1,33 +1,37 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class JsonApi
class ResourceIdentifier
- def self.type_for(class_name, serializer_type = nil, transform_options = {})
- if serializer_type
- raw_type = serializer_type
- else
- inflection =
- if ActiveModelSerializers.config.jsonapi_resource_type == :singular
- :singularize
- else
- :pluralize
- end
-
- raw_type = class_name.underscore
- raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
- raw_type
- .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator)
- raw_type
- end
+ def self.type_for(serializer, serializer_type = nil, transform_options = {})
+ raw_type = serializer_type ? serializer_type : raw_type_from_serializer_object(serializer.object)
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
end
def self.for_type_with_id(type, id, options)
- return nil if id.blank?
- {
- id: id.to_s,
- type: type_for(:no_class_needed, type, options)
- }
+ type = inflect_type(type)
+ type = type_for(:no_class_needed, type, options)
+ if id.blank?
+ nil
+ else
+ { id: id.to_s, type: type }
+ end
+ end
+
+ def self.raw_type_from_serializer_object(object)
+ class_name = object.class.name # should use model_name
+ raw_type = class_name.underscore
+ raw_type = inflect_type(raw_type)
+ raw_type
+ .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator)
+ raw_type
+ end
+
+ def self.inflect_type(type)
+ singularize = ActiveModelSerializers.config.jsonapi_resource_type == :singular
+ inflection = singularize ? :singularize : :pluralize
+ ActiveSupport::Inflector.public_send(inflection, type)
end
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
@@ -37,8 +41,11 @@ def initialize(serializer, options)
end
def as_json
- return nil if id.blank?
- { id: id, type: type }
+ if id.blank?
+ { type: type }
+ else
+ { id: id.to_s, type: type }
+ end
end
protected
@@ -48,7 +55,8 @@ def as_json
private
def type_for(serializer, transform_options)
- self.class.type_for(serializer.object.class.name, serializer._type, transform_options)
+ serializer_type = serializer._type
+ self.class.type_for(serializer, serializer_type, transform_options)
end
def id_for(serializer)
diff --git a/lib/active_model_serializers/adapter/null.rb b/lib/active_model_serializers/adapter/null.rb
index 9e5faf5cb..307d0e157 100644
--- a/lib/active_model_serializers/adapter/null.rb
+++ b/lib/active_model_serializers/adapter/null.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Adapter
class Null < Base
diff --git a/lib/active_model_serializers/callbacks.rb b/lib/active_model_serializers/callbacks.rb
index 71237e4a6..a7c9b6676 100644
--- a/lib/active_model_serializers/callbacks.rb
+++ b/lib/active_model_serializers/callbacks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Adapted from
# https://github.com/rails/rails/blob/7f18ea14c8/activejob/lib/active_job/callbacks.rb
require 'active_support/callbacks'
diff --git a/lib/active_model_serializers/deprecate.rb b/lib/active_model_serializers/deprecate.rb
index e173321d3..012ef623f 100644
--- a/lib/active_model_serializers/deprecate.rb
+++ b/lib/active_model_serializers/deprecate.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Provides a single method +deprecate+ to be used to declare when
# something is going away.
diff --git a/lib/active_model_serializers/deserialization.rb b/lib/active_model_serializers/deserialization.rb
index 878dd98d1..0ac675364 100644
--- a/lib/active_model_serializers/deserialization.rb
+++ b/lib/active_model_serializers/deserialization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Deserialization
module_function
diff --git a/lib/active_model_serializers/json_pointer.rb b/lib/active_model_serializers/json_pointer.rb
index a262f3b28..a72b9daf1 100644
--- a/lib/active_model_serializers/json_pointer.rb
+++ b/lib/active_model_serializers/json_pointer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module JsonPointer
module_function
diff --git a/lib/active_model_serializers/logging.rb b/lib/active_model_serializers/logging.rb
index 943e937e1..ad79743b4 100644
--- a/lib/active_model_serializers/logging.rb
+++ b/lib/active_model_serializers/logging.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# ActiveModelSerializers::Logging
#
diff --git a/lib/active_model_serializers/lookup_chain.rb b/lib/active_model_serializers/lookup_chain.rb
index 25db8e138..0c99a4521 100644
--- a/lib/active_model_serializers/lookup_chain.rb
+++ b/lib/active_model_serializers/lookup_chain.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module LookupChain
# Standard appending of Serializer to the resource name.
diff --git a/lib/active_model_serializers/model.rb b/lib/active_model_serializers/model.rb
index 2ff3d60c5..646042792 100644
--- a/lib/active_model_serializers/model.rb
+++ b/lib/active_model_serializers/model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ActiveModelSerializers::Model is a convenient superclass for making your models
# from Plain-Old Ruby Objects (PORO). It also serves as a reference implementation
# that satisfies ActiveModel::Serializer::Lint::Tests.
diff --git a/lib/active_model_serializers/railtie.rb b/lib/active_model_serializers/railtie.rb
index d6843c9c2..64a67c2d5 100644
--- a/lib/active_model_serializers/railtie.rb
+++ b/lib/active_model_serializers/railtie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rails/railtie'
require 'action_controller'
require 'action_controller/railtie'
@@ -5,6 +7,8 @@
module ActiveModelSerializers
class Railtie < Rails::Railtie
+ config.eager_load_namespaces << ActiveModelSerializers
+
config.to_prepare do
ActiveModel::Serializer.serializers_cache.clear
end
diff --git a/lib/active_model_serializers/register_jsonapi_renderer.rb b/lib/active_model_serializers/register_jsonapi_renderer.rb
index 715c6ab3d..cd67e252c 100644
--- a/lib/active_model_serializers/register_jsonapi_renderer.rb
+++ b/lib/active_model_serializers/register_jsonapi_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238,
# the JSON API media type will have its own format/renderer.
#
diff --git a/lib/active_model_serializers/serializable_resource.rb b/lib/active_model_serializers/serializable_resource.rb
index f67cf2385..eb9431d20 100644
--- a/lib/active_model_serializers/serializable_resource.rb
+++ b/lib/active_model_serializers/serializable_resource.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'set'
module ActiveModelSerializers
@@ -14,8 +16,8 @@ class SerializableResource
# @return the serializable_resource, ready for #as_json/#to_json/#serializable_hash.
def initialize(resource, options = {})
@resource = resource
- @adapter_opts, @serializer_opts =
- options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
+ @adapter_opts = options.select { |k, _| ADAPTER_OPTION_KEYS.include? k }
+ @serializer_opts = options.reject { |k, _| ADAPTER_OPTION_KEYS.include? k }
end
def serialization_scope=(scope)
diff --git a/lib/active_model_serializers/serialization_context.rb b/lib/active_model_serializers/serialization_context.rb
index 9ef604f2e..a171e5f76 100644
--- a/lib/active_model_serializers/serialization_context.rb
+++ b/lib/active_model_serializers/serialization_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_support/core_ext/array/extract_options'
module ActiveModelSerializers
class SerializationContext
diff --git a/lib/active_model_serializers/test.rb b/lib/active_model_serializers/test.rb
index bec452ec1..86d96aef7 100644
--- a/lib/active_model_serializers/test.rb
+++ b/lib/active_model_serializers/test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Test
extend ActiveSupport::Autoload
diff --git a/lib/active_model_serializers/test/schema.rb b/lib/active_model_serializers/test/schema.rb
index a00015869..a924277fa 100644
--- a/lib/active_model_serializers/test/schema.rb
+++ b/lib/active_model_serializers/test/schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModelSerializers
module Test
module Schema
diff --git a/lib/active_model_serializers/test/serializer.rb b/lib/active_model_serializers/test/serializer.rb
index dc812c557..519720900 100644
--- a/lib/active_model_serializers/test/serializer.rb
+++ b/lib/active_model_serializers/test/serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'set'
module ActiveModelSerializers
module Test
diff --git a/lib/generators/rails/resource_override.rb b/lib/generators/rails/resource_override.rb
index 5177a6369..8820624a7 100644
--- a/lib/generators/rails/resource_override.rb
+++ b/lib/generators/rails/resource_override.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rails/generators'
require 'rails/generators/rails/resource/resource_generator'
diff --git a/lib/generators/rails/serializer_generator.rb b/lib/generators/rails/serializer_generator.rb
index e670d5cf6..7cb0d8c2a 100644
--- a/lib/generators/rails/serializer_generator.rb
+++ b/lib/generators/rails/serializer_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class SerializerGenerator < NamedBase
diff --git a/lib/grape/active_model_serializers.rb b/lib/grape/active_model_serializers.rb
index 8dc7a314a..0c0e69abe 100644
--- a/lib/grape/active_model_serializers.rb
+++ b/lib/grape/active_model_serializers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# To add Grape support, require 'grape/active_model_serializers' in the base of your Grape endpoints
# Then add 'include Grape::ActiveModelSerializers' to enable the formatter and helpers
require 'active_model_serializers'
diff --git a/lib/grape/formatters/active_model_serializers.rb b/lib/grape/formatters/active_model_serializers.rb
index 534c5babf..98a1d7bee 100644
--- a/lib/grape/formatters/active_model_serializers.rb
+++ b/lib/grape/formatters/active_model_serializers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers'
#
# Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options],
diff --git a/lib/grape/helpers/active_model_serializers.rb b/lib/grape/helpers/active_model_serializers.rb
index afbdab85a..fe100e528 100644
--- a/lib/grape/helpers/active_model_serializers.rb
+++ b/lib/grape/helpers/active_model_serializers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers
module Grape
diff --git a/lib/tasks/rubocop.rake b/lib/tasks/rubocop.rake
index 5c9a1242f..396c83319 100644
--- a/lib/tasks/rubocop.rake
+++ b/lib/tasks/rubocop.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require 'rubocop'
require 'rubocop/rake_task'
diff --git a/test/action_controller/adapter_selector_test.rb b/test/action_controller/adapter_selector_test.rb
index 3373de7c0..783e52ee3 100644
--- a/test/action_controller/adapter_selector_test.rb
+++ b/test/action_controller/adapter_selector_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/explicit_serializer_test.rb b/test/action_controller/explicit_serializer_test.rb
index a23b6f6b9..2d39e7b68 100644
--- a/test/action_controller/explicit_serializer_test.rb
+++ b/test/action_controller/explicit_serializer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
@@ -69,13 +71,13 @@ def render_using_explicit_each_serializer
def test_render_using_explicit_serializer
get :render_using_explicit_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal '{"name":"Name 1"}', @response.body
end
def test_render_array_using_explicit_serializer
get :render_array_using_explicit_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
expected = [
{ 'name' => 'Name 1' },
@@ -87,7 +89,7 @@ def test_render_array_using_explicit_serializer
def test_render_array_using_implicit_serializer
get :render_array_using_implicit_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
expected = [
{ 'name' => 'Name 1' },
diff --git a/test/action_controller/json/include_test.rb b/test/action_controller/json/include_test.rb
index 1fc8863e5..d4268721a 100644
--- a/test/action_controller/json/include_test.rb
+++ b/test/action_controller/json/include_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/json_api/deserialization_test.rb b/test/action_controller/json_api/deserialization_test.rb
index 025f857b7..18f754dc4 100644
--- a/test/action_controller/json_api/deserialization_test.rb
+++ b/test/action_controller/json_api/deserialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
@@ -45,7 +47,7 @@ def test_deserialization_of_relationship_only_object
response = JSON.parse(@response.body)
expected = {
'restriction_for_id' => '67',
- 'restriction_for_type' => 'discounts',
+ 'restriction_for_type' => 'Discount',
'restricted_to_id' => nil,
'restricted_to_type' => nil
}
diff --git a/test/action_controller/json_api/errors_test.rb b/test/action_controller/json_api/errors_test.rb
index 6da3c9ada..7c983ef51 100644
--- a/test/action_controller/json_api/errors_test.rb
+++ b/test/action_controller/json_api/errors_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/json_api/fields_test.rb b/test/action_controller/json_api/fields_test.rb
index af87ad39a..ed20eed06 100644
--- a/test/action_controller/json_api/fields_test.rb
+++ b/test/action_controller/json_api/fields_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/json_api/linked_test.rb b/test/action_controller/json_api/linked_test.rb
index 120197681..69d8974d7 100644
--- a/test/action_controller/json_api/linked_test.rb
+++ b/test/action_controller/json_api/linked_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
@@ -80,7 +82,7 @@ def render_collection_without_include
def render_collection_with_include
setup_post
- render json: [@post], adapter: :json_api, include: 'author, comments'
+ render json: [@post], adapter: :json_api, include: 'author,comments'
end
end
diff --git a/test/action_controller/json_api/pagination_test.rb b/test/action_controller/json_api/pagination_test.rb
index 0af086b7c..17f14bcdc 100644
--- a/test/action_controller/json_api/pagination_test.rb
+++ b/test/action_controller/json_api/pagination_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require 'will_paginate/array'
require 'kaminari'
@@ -58,8 +60,10 @@ def test_render_pagination_links_with_will_paginate
assert_equal expected_links, response['links']
end
- def test_render_only_last_and_next_pagination_links
+ def test_render_only_first_last_and_next_pagination_links
expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
+ 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
+ 'prev' => nil,
'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2",
'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2" }
get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 } }
@@ -78,17 +82,21 @@ def test_render_pagination_links_with_kaminari
assert_equal expected_links, response['links']
end
- def test_render_only_prev_and_first_pagination_links
+ def test_render_only_prev_first_and_last_pagination_links
expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1",
'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1",
- 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1" }
+ 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1",
+ 'next' => nil,
+ 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" }
get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 } }
response = JSON.parse(@response.body)
assert_equal expected_links, response['links']
end
- def test_render_only_last_and_next_pagination_links_with_additional_params
+ def test_render_only_first_last_and_next_pagination_links_with_additional_params
expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2&teste=additional",
+ 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2&teste=additional",
+ 'prev' => nil,
'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional",
'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional" }
get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 }, teste: 'additional' }
@@ -96,10 +104,12 @@ def test_render_only_last_and_next_pagination_links_with_additional_params
assert_equal expected_links, response['links']
end
- def test_render_only_prev_and_first_pagination_links_with_additional_params
+ def test_render_only_prev_first_and_last_pagination_links_with_additional_params
expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1&teste=additional",
'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1&teste=additional",
- 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1&teste=additional" }
+ 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1&teste=additional",
+ 'next' => nil,
+ 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1&teste=additional" }
get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 }, teste: 'additional' }
response = JSON.parse(@response.body)
assert_equal expected_links, response['links']
diff --git a/test/action_controller/json_api/transform_test.rb b/test/action_controller/json_api/transform_test.rb
index 69212f324..ffd1edc97 100644
--- a/test/action_controller/json_api/transform_test.rb
+++ b/test/action_controller/json_api/transform_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/lookup_proc_test.rb b/test/action_controller/lookup_proc_test.rb
index 4d2ad0b10..1f7c7d864 100644
--- a/test/action_controller/lookup_proc_test.rb
+++ b/test/action_controller/lookup_proc_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
diff --git a/test/action_controller/namespace_lookup_test.rb b/test/action_controller/namespace_lookup_test.rb
index b5c8f496d..2fe961cfd 100644
--- a/test/action_controller/namespace_lookup_test.rb
+++ b/test/action_controller/namespace_lookup_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
@@ -123,7 +125,12 @@ def namespace_set_by_request_headers
tests Api::V3::LookupTestController
setup do
- @test_namespace = self.class.parent
+ @test_namespace =
+ if Module.method_defined?(:module_parent)
+ self.class.module_parent
+ else
+ self.class.parent
+ end
end
test 'uses request headers to determine the namespace' do
diff --git a/test/action_controller/serialization_scope_name_test.rb b/test/action_controller/serialization_scope_name_test.rb
index 3d767d049..32a04a58d 100644
--- a/test/action_controller/serialization_scope_name_test.rb
+++ b/test/action_controller/serialization_scope_name_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module SerializationScopeTesting
diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb
index dfd72b42e..2286309b4 100644
--- a/test/action_controller/serialization_test.rb
+++ b/test/action_controller/serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActionController
@@ -5,6 +7,7 @@ module Serialization
class ImplicitSerializerTest < ActionController::TestCase
class ImplicitSerializationTestController < ActionController::Base
include SerializationTesting
+
def render_using_implicit_serializer
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
render json: @profile
@@ -73,8 +76,12 @@ def render_json_array_object_without_serializer
render json: [{ error: 'Result is Invalid' }]
end
+ # HACK: to prevent the resetting of instance variables after each request in Rails 7
+ # see https://github.com/rails/rails/pull/43735
+ def clear_instance_variables_between_requests; end
+
def update_and_render_object_with_cache_enabled
- @post.updated_at = Time.zone.now
+ @post.updated_at = Time.zone.now # requires hack above to prevent `NoMethodError: undefined method `updated_at=' for nil:NilClass`
generate_cached_serializer(@post)
render json: @post
@@ -153,7 +160,7 @@ def test_render_using_implicit_serializer
description: 'Description 1'
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -172,7 +179,7 @@ def test_render_using_default_root
}
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -181,7 +188,7 @@ def test_render_array_using_custom_root
get :render_array_using_custom_root
end
expected = { custom_root: [{ name: 'Name 1', description: 'Description 1' }] }
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -191,7 +198,7 @@ def test_render_array_that_is_empty_using_custom_root
end
expected = { custom_root: [] }
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -201,14 +208,14 @@ def test_render_object_using_custom_root
end
expected = { custom_root: { name: 'Name 1', description: 'Description 1' } }
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
def test_render_json_object_without_serializer
get :render_json_object_without_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
expected_body = { error: 'Result is Invalid' }
assert_equal expected_body.to_json, @response.body
end
@@ -216,14 +223,14 @@ def test_render_json_object_without_serializer
def test_render_json_array_object_without_serializer
get :render_json_array_object_without_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
expected_body = [{ error: 'Result is Invalid' }]
assert_equal expected_body.to_json, @response.body
end
def test_render_array_using_implicit_serializer
get :render_array_using_implicit_serializer
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
expected = [
{
@@ -259,7 +266,7 @@ def test_render_array_using_implicit_serializer_and_meta
}
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -282,7 +289,7 @@ def test_render_array_using_implicit_serializer_and_links
}
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
end
@@ -311,7 +318,7 @@ def test_render_with_cache_enable
Timecop.freeze(Time.zone.now) do
get :render_object_with_cache_enabled
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected.to_json, @response.body
get :render_changed_object_with_cache_enabled
@@ -347,7 +354,7 @@ def test_render_with_cache_enable_and_expired
}
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
actual = @response.body
expected = expected.to_json
if ENV['APPVEYOR'] && actual != expected
@@ -362,7 +369,7 @@ def test_render_with_fragment_only_cache_enable
get :render_fragment_changed_object_with_only_cache_enabled
response = JSON.parse(@response.body)
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal 'ZOMG A ROLE', response['name']
assert_equal 'HUEHUEBRBR', response['description']
end
@@ -372,7 +379,7 @@ def test_render_with_fragment_except_cache_enable
get :render_fragment_changed_object_with_except_cache_enabled
response = JSON.parse(@response.body)
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal 5, response['rating']
assert_equal 'lol', response['content']
end
@@ -393,7 +400,7 @@ def test_render_fragment_changed_object_with_relationship
}
}
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
assert_equal expected_return, response
end
end
@@ -424,7 +431,7 @@ def test_cache_expiration_on_update
get :update_and_render_object_with_cache_enabled
- assert_equal 'application/json', @response.content_type
+ assert_match(%r{\Aapplication/json}, @response.content_type)
actual = @response.body
expected = expected.to_json
if ENV['APPVEYOR'] && actual != expected
@@ -457,13 +464,19 @@ def use_adapter?
end
def test_render_event_is_emitted
- subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |name|
- @name = name
+ subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |subscribed_event|
+ @subscribed_event = subscribed_event
end
get :render_using_implicit_serializer
- assert_equal 'render.active_model_serializers', @name
+ subscribed_event_name =
+ if @subscribed_event.is_a?(String)
+ @subscribed_event
+ else
+ @subscribed_event.name # is a ActiveSupport::Notifications::Event
+ end
+ assert_equal 'render.active_model_serializers', subscribed_event_name
ensure
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
end
diff --git a/test/active_model_serializers/adapter_for_test.rb b/test/active_model_serializers/adapter_for_test.rb
index 1439b987c..19466dba5 100644
--- a/test/active_model_serializers/adapter_for_test.rb
+++ b/test/active_model_serializers/adapter_for_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/active_model_serializers/json_pointer_test.rb b/test/active_model_serializers/json_pointer_test.rb
index 60619ee6e..e31e5aac7 100644
--- a/test/active_model_serializers/json_pointer_test.rb
+++ b/test/active_model_serializers/json_pointer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/active_model_serializers/logging_test.rb b/test/active_model_serializers/logging_test.rb
index 95e616827..ee4343934 100644
--- a/test/active_model_serializers/logging_test.rb
+++ b/test/active_model_serializers/logging_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/active_model_serializers/model_test.rb b/test/active_model_serializers/model_test.rb
index 6a8a29afb..a6723fa9e 100644
--- a/test/active_model_serializers/model_test.rb
+++ b/test/active_model_serializers/model_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/active_model_serializers/railtie_test_isolated.rb b/test/active_model_serializers/railtie_test_isolated.rb
index 1044fc8b9..5e4e2085a 100644
--- a/test/active_model_serializers/railtie_test_isolated.rb
+++ b/test/active_model_serializers/railtie_test_isolated.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Execute this test in isolation
require 'support/isolated_unit'
diff --git a/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb b/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb
index 30542408f..a5460aa38 100644
--- a/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb
+++ b/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'support/isolated_unit'
require 'minitest/mock'
require 'action_dispatch'
diff --git a/test/active_model_serializers/serialization_context_test_isolated.rb b/test/active_model_serializers/serialization_context_test_isolated.rb
index 5720e84a1..065d75eee 100644
--- a/test/active_model_serializers/serialization_context_test_isolated.rb
+++ b/test/active_model_serializers/serialization_context_test_isolated.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Execute this test in isolation
require 'support/isolated_unit'
require 'minitest/mock'
diff --git a/test/active_model_serializers/test/schema_test.rb b/test/active_model_serializers/test/schema_test.rb
index 0fe497d78..4ff7ba5e3 100644
--- a/test/active_model_serializers/test/schema_test.rb
+++ b/test/active_model_serializers/test/schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/active_model_serializers/test/serializer_test.rb b/test/active_model_serializers/test/serializer_test.rb
index 38dc60ba1..d64eb7d38 100644
--- a/test/active_model_serializers/test/serializer_test.rb
+++ b/test/active_model_serializers/test/serializer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/active_record_test.rb b/test/active_record_test.rb
index 5bb941a46..2538a5d9b 100644
--- a/test/active_record_test.rb
+++ b/test/active_record_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
class ActiveRecordTest < ActiveSupport::TestCase
diff --git a/test/adapter/attributes_test.rb b/test/adapter/attributes_test.rb
index e60019f50..547f7791f 100644
--- a/test/adapter/attributes_test.rb
+++ b/test/adapter/attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/deprecation_test.rb b/test/adapter/deprecation_test.rb
index ea858caa4..23adc0269 100644
--- a/test/adapter/deprecation_test.rb
+++ b/test/adapter/deprecation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
class Serializer
diff --git a/test/adapter/json/belongs_to_test.rb b/test/adapter/json/belongs_to_test.rb
index 0f096f0b3..83efa3442 100644
--- a/test/adapter/json/belongs_to_test.rb
+++ b/test/adapter/json/belongs_to_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json/collection_test.rb b/test/adapter/json/collection_test.rb
index 8deb40500..cf488905d 100644
--- a/test/adapter/json/collection_test.rb
+++ b/test/adapter/json/collection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json/fields_test.rb b/test/adapter/json/fields_test.rb
new file mode 100644
index 000000000..5e1f570aa
--- /dev/null
+++ b/test/adapter/json/fields_test.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+module ActiveModelSerializers
+ module Adapter
+ class Json
+ class FieldsTest < ActiveSupport::TestCase
+ class Post < ::Model
+ attributes :title, :body
+ associations :author, :comments
+ end
+ class Author < ::Model
+ attributes :name, :birthday
+ end
+ class Comment < ::Model
+ attributes :title, :body
+ associations :author, :post
+ end
+
+ class PostSerializer < ActiveModel::Serializer
+ type 'post'
+ attributes :title, :body
+ belongs_to :author
+ has_many :comments
+ end
+
+ class AuthorSerializer < ActiveModel::Serializer
+ attributes :name, :birthday
+ end
+
+ class CommentSerializer < ActiveModel::Serializer
+ type 'comment'
+ attributes :title, :body
+ belongs_to :author
+ end
+
+ def setup
+ @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990')
+ @comment1 = Comment.new(id: 7, body: 'cool', author: @author)
+ @comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
+ @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
+ author: @author, comments: [@comment1, @comment2])
+ @comment1.post = @post
+ @comment2.post = @post
+ end
+
+ def test_fields_attributes
+ fields = [:title]
+ hash = serializable(@post, adapter: :json, fields: fields, include: []).serializable_hash
+ expected = { title: 'Title 1' }
+ assert_equal(expected, hash[:post])
+ end
+
+ def test_fields_included
+ fields = [:title, { comments: [:body] }]
+ hash = serializable(@post, adapter: :json, include: [:comments], fields: fields).serializable_hash
+ expected = [{ body: @comment1.body }, { body: @comment2.body }]
+
+ assert_equal(expected, hash[:post][:comments])
+ end
+ end
+ end
+ end
+end
diff --git a/test/adapter/json/has_many_test.rb b/test/adapter/json/has_many_test.rb
index feeec93c3..0599b1397 100644
--- a/test/adapter/json/has_many_test.rb
+++ b/test/adapter/json/has_many_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json/transform_test.rb b/test/adapter/json/transform_test.rb
index c102b5af1..5035c2d70 100644
--- a/test/adapter/json/transform_test.rb
+++ b/test/adapter/json/transform_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
@@ -54,9 +56,8 @@ def test_transform_serialization_ctx_overrides_global_config
def test_transform_undefined
mock_request(:blam)
- result = nil
assert_raises NoMethodError do
- result = @adapter.serializable_hash
+ @adapter.serializable_hash
end
end
diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb
index ded83ab5c..aaed447b6 100644
--- a/test/adapter/json_api/belongs_to_test.rb
+++ b/test/adapter/json_api/belongs_to_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb
index e60a824e8..44c9b16fa 100644
--- a/test/adapter/json_api/collection_test.rb
+++ b/test/adapter/json_api/collection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/errors_test.rb b/test/adapter/json_api/errors_test.rb
index cae7a5a6c..35ba855eb 100644
--- a/test/adapter/json_api/errors_test.rb
+++ b/test/adapter/json_api/errors_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/fields_test.rb b/test/adapter/json_api/fields_test.rb
index 852283187..3d3ff716d 100644
--- a/test/adapter/json_api/fields_test.rb
+++ b/test/adapter/json_api/fields_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/has_many_embed_ids_test.rb b/test/adapter/json_api/has_many_embed_ids_test.rb
deleted file mode 100644
index e016de284..000000000
--- a/test/adapter/json_api/has_many_embed_ids_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'test_helper'
-
-module ActiveModelSerializers
- module Adapter
- class JsonApi
- class HasManyEmbedIdsTest < ActiveSupport::TestCase
- def setup
- @author = Author.new(name: 'Steve K.')
- @author.bio = nil
- @author.roles = nil
- @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
- @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
- @author.posts = [@first_post, @second_post]
- @first_post.author = @author
- @second_post.author = @author
- @first_post.comments = []
- @second_post.comments = []
- @blog = Blog.new(id: 23, name: 'AMS Blog')
- @first_post.blog = @blog
- @second_post.blog = nil
-
- @serializer = AuthorSerializer.new(@author)
- @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
- end
-
- def test_includes_comment_ids
- expected = {
- data: [
- { type: 'posts', id: '1' },
- { type: 'posts', id: '2' }
- ]
- }
-
- assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts])
- end
-
- def test_no_includes_linked_comments
- assert_nil @adapter.serializable_hash[:linked]
- end
- end
- end
- end
-end
diff --git a/test/adapter/json_api/has_many_explicit_serializer_test.rb b/test/adapter/json_api/has_many_explicit_serializer_test.rb
index f598bc9b0..cb54756f2 100644
--- a/test/adapter/json_api/has_many_explicit_serializer_test.rb
+++ b/test/adapter/json_api/has_many_explicit_serializer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb
index a9fa9ac92..54cdf2e01 100644
--- a/test/adapter/json_api/has_many_test.rb
+++ b/test/adapter/json_api/has_many_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/has_one_test.rb b/test/adapter/json_api/has_one_test.rb
index eb505a0de..ce82f7918 100644
--- a/test/adapter/json_api/has_one_test.rb
+++ b/test/adapter/json_api/has_one_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/include_data_if_sideloaded_test.rb b/test/adapter/json_api/include_data_if_sideloaded_test.rb
index c0da94886..7c9d4221a 100644
--- a/test/adapter/json_api/include_data_if_sideloaded_test.rb
+++ b/test/adapter/json_api/include_data_if_sideloaded_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
@@ -6,7 +8,7 @@ module Adapter
class JsonApi
class IncludeParamTest < ActiveSupport::TestCase
IncludeParamAuthor = Class.new(::Model) do
- associations :tags, :posts
+ associations :tags, :posts, :roles
end
class CustomCommentLoader
@@ -51,14 +53,19 @@ class IncludeParamAuthorSerializer < ActiveModel::Serializer
include_data :if_sideloaded
IncludeParamAuthorSerializer.comment_loader.all
end
+ has_many :roles, key: :granted_roles do
+ include_data :if_sideloaded
+ end
end
def setup
IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new
@tag = Tag.new(id: 1337, name: 'mytag')
+ @role = Role.new(id: 1337, name: 'myrole')
@author = IncludeParamAuthor.new(
id: 1337,
- tags: [@tag]
+ tags: [@tag],
+ roles: [@role]
)
end
@@ -105,6 +112,31 @@ def test_sideloads_included
assert_equal(expected, hash[:included])
end
+ def test_sideloads_included_when_using_key
+ expected = [
+ {
+ id: '1337',
+ type: 'roles',
+ attributes: {
+ name: 'myrole',
+ description: nil,
+ slug: 'myrole-1337'
+ },
+ relationships: {
+ author: { data: nil }
+ }
+ }
+ ]
+
+ hash = result(include: :granted_roles)
+ assert_equal(expected, hash[:included])
+ end
+
+ def test_sideloads_not_included_when_using_name_when_key_defined
+ hash = result(include: :roles)
+ assert_nil(hash[:included])
+ end
+
def test_nested_relationship
expected = {
data: [
diff --git a/test/adapter/json_api/json_api_test.rb b/test/adapter/json_api/json_api_test.rb
index cb2ce909a..4db3231fe 100644
--- a/test/adapter/json_api/json_api_test.rb
+++ b/test/adapter/json_api/json_api_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb
index 0d9c69b6b..d154cad47 100644
--- a/test/adapter/json_api/linked_test.rb
+++ b/test/adapter/json_api/linked_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
class NestedPost < ::Model; associations :nested_posts end
diff --git a/test/adapter/json_api/links_test.rb b/test/adapter/json_api/links_test.rb
index ffbfa303e..edf6cbc70 100644
--- a/test/adapter/json_api/links_test.rb
+++ b/test/adapter/json_api/links_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
@@ -17,7 +19,21 @@ class LinkAuthorSerializer < ActiveModel::Serializer
link :yet_another do
"http://example.com/resource/#{object.id}"
end
+ link :conditional1, if: -> { instance_truth } do
+ "http://example.com/conditional1/#{object.id}"
+ end
+ link :conditional2, if: :instance_falsey do
+ "http://example.com/conditional2/#{object.id}"
+ end
link(:nil) { nil }
+
+ def instance_truth
+ true
+ end
+
+ def instance_falsey
+ false
+ end
end
def setup
@@ -85,7 +101,8 @@ def test_resource_links
:"link-authors" => 'http://example.com/link_authors',
resource: 'http://example.com/resource',
posts: 'http://example.com/link_authors/1337/posts',
- :"yet-another" => 'http://example.com/resource/1337'
+ :"yet-another" => 'http://example.com/resource/1337',
+ conditional1: 'http://example.com/conditional1/1337'
}
assert_equal(expected, hash[:data][:links])
end
diff --git a/test/adapter/json_api/pagination_links_test.rb b/test/adapter/json_api/pagination_links_test.rb
index 736ea2fe2..be1f475a8 100644
--- a/test/adapter/json_api/pagination_links_test.rb
+++ b/test/adapter/json_api/pagination_links_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require 'will_paginate/array'
require 'kaminari'
@@ -54,6 +56,16 @@ def data
}
end
+ def empty_collection_links
+ {
+ self: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
+ first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
+ prev: nil,
+ next: nil,
+ last: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2"
+ }
+ end
+
def links
{
links: {
@@ -71,7 +83,21 @@ def last_page_links
links: {
self: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2",
first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
- prev: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2"
+ prev: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2",
+ next: nil,
+ last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2"
+ }
+ }
+ end
+
+ def greater_than_last_page_links
+ {
+ links: {
+ self: "#{URI}?page%5Bnumber%5D=4&page%5Bsize%5D=2",
+ first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2",
+ prev: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2",
+ next: nil,
+ last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2"
}
}
end
@@ -108,10 +134,17 @@ def expected_response_with_last_page_pagination_links
end
end
- def expected_response_with_no_data_pagination_links
+ def expected_response_with_greater_than_last_page_pagination_links
{}.tap do |hash|
hash[:data] = []
- hash[:links] = {}
+ hash.merge! greater_than_last_page_links
+ end
+ end
+
+ def expected_response_with_empty_collection_pagination_links
+ {}.tap do |hash|
+ hash[:data] = []
+ hash.merge! links: empty_collection_links
end
end
@@ -127,6 +160,18 @@ def test_pagination_links_using_will_paginate
assert_equal expected_response_with_pagination_links, adapter.serializable_hash
end
+ def test_pagination_links_invalid_current_page_using_kaminari
+ adapter = load_adapter(using_kaminari(4), mock_request)
+
+ assert_equal expected_response_with_greater_than_last_page_pagination_links, adapter.serializable_hash
+ end
+
+ def test_pagination_links_invalid_current_page_using_will_paginate
+ adapter = load_adapter(using_will_paginate(4), mock_request)
+
+ assert_equal expected_response_with_greater_than_last_page_pagination_links, adapter.serializable_hash
+ end
+
def test_pagination_links_with_additional_params
adapter = load_adapter(using_will_paginate, mock_request(test: 'test'))
@@ -139,7 +184,7 @@ def test_pagination_links_when_zero_results_kaminari
adapter = load_adapter(using_kaminari(1), mock_request)
- assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash
+ assert_equal expected_response_with_empty_collection_pagination_links, adapter.serializable_hash
end
def test_pagination_links_when_zero_results_will_paginate
@@ -147,7 +192,7 @@ def test_pagination_links_when_zero_results_will_paginate
adapter = load_adapter(using_will_paginate(1), mock_request)
- assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash
+ assert_equal expected_response_with_empty_collection_pagination_links, adapter.serializable_hash
end
def test_last_page_pagination_links_using_kaminari
@@ -171,10 +216,11 @@ def test_not_showing_pagination_links
def test_raises_descriptive_error_when_serialization_context_unset
render_options = { adapter: :json_api }
adapter = serializable(using_kaminari, render_options)
- exception = assert_raises do
+ exception_class = ActiveModelSerializers::Adapter::JsonApi::PaginationLinks::MissingSerializationContextError
+
+ exception = assert_raises(exception_class) do
adapter.as_json
end
- exception_class = ActiveModelSerializers::Adapter::JsonApi::PaginationLinks::MissingSerializationContextError
assert_equal exception_class, exception.class
assert_match(/CollectionSerializer#paginated\?/, exception.message)
end
diff --git a/test/adapter/json_api/parse_test.rb b/test/adapter/json_api/parse_test.rb
index bee79c8c1..d2b562190 100644
--- a/test/adapter/json_api/parse_test.rb
+++ b/test/adapter/json_api/parse_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
module Adapter
@@ -125,7 +127,7 @@ def test_polymorphic
src: 'http://example.com/images/productivity.png',
author_id: nil,
photographer_id: '9',
- photographer_type: 'people',
+ photographer_type: 'Person',
comment_ids: %w(1 2)
}
assert_equal(expected, parsed_hash)
diff --git a/test/adapter/json_api/relationship_test.rb b/test/adapter/json_api/relationship_test.rb
index cfd5be85e..38a67fe22 100644
--- a/test/adapter/json_api/relationship_test.rb
+++ b/test/adapter/json_api/relationship_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
@@ -33,6 +35,24 @@ def test_relationship_with_nil_model
assert_equal(expected, actual)
end
+ def test_relationship_with_nil_model_and_belongs_to_id_on_self
+ original_config = ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship
+ ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
+
+ expected = { data: nil }
+
+ model_attributes = { blog: nil }
+ relationship_name = :blog
+ model = new_model(model_attributes)
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
+ belongs_to :blog
+ end
+
+ assert_equal(expected, actual)
+ ensure
+ ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_config
+ end
+
def test_relationship_with_data_array
expected = {
data: [
diff --git a/test/adapter/json_api/resource_identifier_test.rb b/test/adapter/json_api/resource_identifier_test.rb
deleted file mode 100644
index 62b7d93b3..000000000
--- a/test/adapter/json_api/resource_identifier_test.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'test_helper'
-
-module ActiveModelSerializers
- module Adapter
- class JsonApi
- class ResourceIdentifierTest < ActiveSupport::TestCase
- class WithDefinedTypeSerializer < ActiveModel::Serializer
- type 'with_defined_type'
- end
-
- class WithDefinedIdSerializer < ActiveModel::Serializer
- def id
- 'special_id'
- end
- end
-
- class FragmentedSerializer < ActiveModel::Serializer
- cache only: :id
-
- def id
- 'special_id'
- end
- end
-
- setup do
- @model = Author.new(id: 1, name: 'Steve K.')
- ActionController::Base.cache_store.clear
- end
-
- def test_defined_type
- test_type(WithDefinedTypeSerializer, 'with-defined-type')
- end
-
- def test_singular_type
- test_type_inflection(AuthorSerializer, 'author', :singular)
- end
-
- def test_plural_type
- test_type_inflection(AuthorSerializer, 'authors', :plural)
- end
-
- def test_type_with_namespace
- Object.const_set(:Admin, Module.new)
- model = Class.new(::Model)
- Admin.const_set(:PowerUser, model)
- serializer = Class.new(ActiveModel::Serializer)
- Admin.const_set(:PowerUserSerializer, serializer)
- with_namespace_separator '--' do
- admin_user = Admin::PowerUser.new
- serializer = Admin::PowerUserSerializer.new(admin_user)
- expected = {
- id: admin_user.id,
- type: 'admin--power-users'
- }
-
- identifier = ResourceIdentifier.new(serializer, {})
- actual = identifier.as_json
- assert_equal(expected, actual)
- end
- end
-
- def test_id_defined_on_object
- test_id(AuthorSerializer, @model.id.to_s)
- end
-
- def test_id_defined_on_serializer
- test_id(WithDefinedIdSerializer, 'special_id')
- end
-
- def test_id_defined_on_fragmented
- test_id(FragmentedSerializer, 'special_id')
- end
-
- private
-
- def test_type_inflection(serializer_class, expected_type, inflection)
- original_inflection = ActiveModelSerializers.config.jsonapi_resource_type
- ActiveModelSerializers.config.jsonapi_resource_type = inflection
- test_type(serializer_class, expected_type)
- ensure
- ActiveModelSerializers.config.jsonapi_resource_type = original_inflection
- end
-
- def test_type(serializer_class, expected_type)
- serializer = serializer_class.new(@model)
- resource_identifier = ResourceIdentifier.new(serializer, nil)
- expected = {
- id: @model.id.to_s,
- type: expected_type
- }
-
- assert_equal(expected, resource_identifier.as_json)
- end
-
- def test_id(serializer_class, id)
- serializer = serializer_class.new(@model)
- resource_identifier = ResourceIdentifier.new(serializer, nil)
- inflection = ActiveModelSerializers.config.jsonapi_resource_type
- type = @model.class.model_name.send(inflection)
- expected = {
- id: id,
- type: type
- }
-
- assert_equal(expected, resource_identifier.as_json)
- end
- end
- end
- end
-end
diff --git a/test/adapter/json_api/resource_meta_test.rb b/test/adapter/json_api/resource_meta_test.rb
index fa281f30b..73f668f3a 100644
--- a/test/adapter/json_api/resource_meta_test.rb
+++ b/test/adapter/json_api/resource_meta_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/adapter/json_api/toplevel_jsonapi_test.rb b/test/adapter/json_api/toplevel_jsonapi_test.rb
index 7b0357e52..30e04b1eb 100644
--- a/test/adapter/json_api/toplevel_jsonapi_test.rb
+++ b/test/adapter/json_api/toplevel_jsonapi_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/transform_test.rb b/test/adapter/json_api/transform_test.rb
index 887ec835f..f121b217a 100644
--- a/test/adapter/json_api/transform_test.rb
+++ b/test/adapter/json_api/transform_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/json_api/type_test.rb b/test/adapter/json_api/type_test.rb
index 40b84cf2b..56f7efcbc 100644
--- a/test/adapter/json_api/type_test.rb
+++ b/test/adapter/json_api/type_test.rb
@@ -1,60 +1,193 @@
+# frozen_string_literal: true
+
require 'test_helper'
-module ActiveModel
- class Serializer
- module Adapter
- class JsonApi
- class TypeTest < ActiveSupport::TestCase
- class StringTypeSerializer < ActiveModel::Serializer
- attribute :name
- type 'profile'
+module ActiveModelSerializers
+ module Adapter
+ class JsonApi
+ class TypeTest < ActiveSupport::TestCase
+ class StringTypeSerializer < ActiveModel::Serializer
+ attribute :name
+ type 'profile'
+ end
+
+ class SymbolTypeSerializer < ActiveModel::Serializer
+ attribute :name
+ type :profile
+ end
+
+ setup do
+ @author = Author.new(id: 1, name: 'Steve K.')
+ end
+
+ def test_config_plural
+ with_jsonapi_inflection :plural do
+ assert_type(@author, 'authors')
end
+ end
- class SymbolTypeSerializer < ActiveModel::Serializer
- attribute :name
- type :profile
+ def test_config_singular
+ with_jsonapi_inflection :singular do
+ assert_type(@author, 'author')
end
+ end
+
+ def test_explicit_string_type_value
+ assert_type(@author, 'profile', serializer: StringTypeSerializer)
+ end
+
+ def test_explicit_symbol_type_value
+ assert_type(@author, 'profile', serializer: SymbolTypeSerializer)
+ end
+
+ private
+
+ def assert_type(resource, expected_type, opts = {})
+ opts = opts.reverse_merge(adapter: :json_api)
+ hash = serializable(resource, opts).serializable_hash
+ assert_equal(expected_type, hash.fetch(:data).fetch(:type))
+ end
+ end
+ class ResourceIdentifierTest < ActiveSupport::TestCase
+ class WithDefinedTypeSerializer < ActiveModel::Serializer
+ type 'with_defined_types'
+ end
+
+ class WithDefinedIdSerializer < ActiveModel::Serializer
+ def id
+ 'special_id'
+ end
+ end
+
+ class FragmentedSerializer < ActiveModel::Serializer
+ cache only: :id
+
+ def id
+ 'special_id'
+ end
+ end
- setup do
- @author = Author.new(id: 1, name: 'Steve K.')
+ setup do
+ @model = Author.new(id: 1, name: 'Steve K.')
+ ActionController::Base.cache_store.clear
+ end
+
+ def test_defined_type
+ actual = with_jsonapi_inflection :plural do
+ actual_resource_identifier_object(WithDefinedTypeSerializer, @model)
end
+ expected = { id: expected_model_id(@model), type: 'with-defined-types' }
+ assert_equal actual, expected
+ end
- def test_config_plural
- with_jsonapi_resource_type :plural do
- assert_type(@author, 'authors')
- end
+ def test_defined_type_not_inflected
+ actual = with_jsonapi_inflection :singular do
+ actual_resource_identifier_object(WithDefinedTypeSerializer, @model)
end
+ expected = { id: expected_model_id(@model), type: 'with-defined-types' }
+ assert_equal actual, expected
+ end
- def test_config_singular
- with_jsonapi_resource_type :singular do
- assert_type(@author, 'author')
- end
+ def test_singular_type
+ actual = with_jsonapi_inflection :singular do
+ actual_resource_identifier_object(AuthorSerializer, @model)
end
+ expected = { id: expected_model_id(@model), type: 'author' }
+ assert_equal actual, expected
+ end
- def test_explicit_string_type_value
- assert_type(@author, 'profile', serializer: StringTypeSerializer)
+ def test_plural_type
+ actual = with_jsonapi_inflection :plural do
+ actual_resource_identifier_object(AuthorSerializer, @model)
end
+ expected = { id: expected_model_id(@model), type: 'authors' }
+ assert_equal actual, expected
+ end
+
+ def test_type_with_namespace
+ Object.const_set(:Admin, Module.new)
+ model = Class.new(::Model)
+ Admin.const_set(:PowerUser, model)
+ serializer = Class.new(ActiveModel::Serializer)
+ Admin.const_set(:PowerUserSerializer, serializer)
+ with_namespace_separator '--' do
+ admin_user = Admin::PowerUser.new
+ serializer = Admin::PowerUserSerializer.new(admin_user)
+ expected = {
+ id: admin_user.id,
+ type: 'admin--power-users'
+ }
- def test_explicit_symbol_type_value
- assert_type(@author, 'profile', serializer: SymbolTypeSerializer)
+ identifier = ResourceIdentifier.new(serializer, {})
+ actual = identifier.as_json
+ assert_equal(expected, actual)
end
+ end
+
+ def test_id_defined_on_object
+ actual = actual_resource_identifier_object(AuthorSerializer, @model)
+ expected = { id: @model.id.to_s, type: expected_model_type(@model) }
+ assert_equal actual, expected
+ end
- private
+ def test_blank_id
+ model = Author.new(id: nil, name: 'Steve K.')
+ actual = actual_resource_identifier_object(AuthorSerializer, model)
+ expected = { type: expected_model_type(model) }
+ assert_equal actual, expected
+ end
+
+ def test_for_type_with_id
+ id = 1
+ actual = ResourceIdentifier.for_type_with_id('admin_user', id, {})
+ expected = { id: '1', type: 'admin-users' }
+ assert_equal actual, expected
+ end
- def assert_type(resource, expected_type, opts = {})
- opts = opts.reverse_merge(adapter: :json_api)
- hash = serializable(resource, opts).serializable_hash
- assert_equal(expected_type, hash.fetch(:data).fetch(:type))
+ def test_for_type_with_id_given_blank_id
+ id = ''
+ actual = ResourceIdentifier.for_type_with_id('admin_user', id, {})
+ assert_nil actual
+ end
+
+ def test_for_type_with_id_inflected
+ id = 2
+ actual = with_jsonapi_inflection :singular do
+ ResourceIdentifier.for_type_with_id('admin_users', id, {})
end
+ expected = { id: '2', type: 'admin-user' }
+ assert_equal actual, expected
+ end
+
+ def test_id_defined_on_serializer
+ actual = actual_resource_identifier_object(WithDefinedIdSerializer, @model)
+ expected = { id: 'special_id', type: expected_model_type(@model) }
+ assert_equal actual, expected
+ end
+
+ def test_id_defined_on_fragmented
+ actual = actual_resource_identifier_object(FragmentedSerializer, @model)
+ expected = { id: 'special_id', type: expected_model_type(@model) }
+ assert_equal actual, expected
+ end
+
+ private
+
+ def actual_resource_identifier_object(serializer_class, model)
+ serializer = serializer_class.new(model)
+ resource_identifier = ResourceIdentifier.new(serializer, nil)
+ resource_identifier.as_json
+ end
- def with_jsonapi_resource_type(inflection)
- old_inflection = ActiveModelSerializers.config.jsonapi_resource_type
- ActiveModelSerializers.config.jsonapi_resource_type = inflection
- yield
- ensure
- ActiveModelSerializers.config.jsonapi_resource_type = old_inflection
+ def expected_model_type(model, inflection = ActiveModelSerializers.config.jsonapi_resource_type)
+ with_jsonapi_inflection inflection do
+ model.class.model_name.send(inflection)
end
end
+
+ def expected_model_id(model)
+ model.id.to_s
+ end
end
end
end
diff --git a/test/adapter/json_test.rb b/test/adapter/json_test.rb
index f7f178f88..ebd40e68d 100644
--- a/test/adapter/json_test.rb
+++ b/test/adapter/json_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/null_test.rb b/test/adapter/null_test.rb
index 4e701db10..139f17db8 100644
--- a/test/adapter/null_test.rb
+++ b/test/adapter/null_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/adapter/polymorphic_test.rb b/test/adapter/polymorphic_test.rb
index 87d5ff51f..48bcd979e 100644
--- a/test/adapter/polymorphic_test.rb
+++ b/test/adapter/polymorphic_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
@@ -165,6 +167,53 @@ def test_json_api_serialization
assert_equal(expected, serialization(@picture, :json_api))
end
+
+ def test_json_api_serialization_with_polymorphic_belongs_to
+ expected = {
+ data: {
+ id: '1',
+ type: 'poly-tags',
+ attributes: { phrase: 'foo' },
+ relationships: {
+ :"object-tags" => {
+ data: [
+ { id: '1', type: 'object-tags' },
+ { id: '5', type: 'object-tags' }
+ ]
+ }
+ }
+ },
+ included: [
+ {
+ id: '1',
+ type: 'object-tags',
+ relationships: {
+ taggable: {
+ data: { id: '42', type: 'employees' }
+ }
+ }
+ },
+ {
+ id: '42',
+ type: 'employees'
+ },
+ {
+ id: '5',
+ type: 'object-tags',
+ relationships: {
+ taggable: {
+ data: { id: '1', type: 'pictures' }
+ }
+ }
+ },
+ {
+ id: '1',
+ type: 'pictures'
+ }
+ ]
+ }
+ assert_equal(expected, tag_serialization(:json_api))
+ end
end
end
end
diff --git a/test/adapter_test.rb b/test/adapter_test.rb
index c1b00d726..f88dc157a 100644
--- a/test/adapter_test.rb
+++ b/test/adapter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/array_serializer_test.rb b/test/array_serializer_test.rb
index 2ad55324e..498737c6d 100644
--- a/test/array_serializer_test.rb
+++ b/test/array_serializer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require_relative 'collection_serializer_test'
diff --git a/test/benchmark/app.rb b/test/benchmark/app.rb
index c39e9b4e8..cd7a41ef7 100644
--- a/test/benchmark/app.rb
+++ b/test/benchmark/app.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# https://github.com/rails-api/active_model_serializers/pull/872
# approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against
require 'bundler/setup'
diff --git a/test/benchmark/benchmarking_support.rb b/test/benchmark/benchmarking_support.rb
index dd27f6c5f..7e3adaadc 100644
--- a/test/benchmark/benchmarking_support.rb
+++ b/test/benchmark/benchmarking_support.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'benchmark/ips'
require 'json'
diff --git a/test/benchmark/bm_active_record.rb b/test/benchmark/bm_active_record.rb
index 0837e266d..5ee6fbfdf 100644
--- a/test/benchmark/bm_active_record.rb
+++ b/test/benchmark/bm_active_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './benchmarking_support'
require_relative './app'
diff --git a/test/benchmark/bm_adapter.rb b/test/benchmark/bm_adapter.rb
index c8bae66a5..d81971078 100644
--- a/test/benchmark/bm_adapter.rb
+++ b/test/benchmark/bm_adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './benchmarking_support'
require_relative './app'
diff --git a/test/benchmark/bm_caching.rb b/test/benchmark/bm_caching.rb
index ae3ad798c..52aa9de20 100644
--- a/test/benchmark/bm_caching.rb
+++ b/test/benchmark/bm_caching.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './benchmarking_support'
require_relative './app'
diff --git a/test/benchmark/bm_lookup_chain.rb b/test/benchmark/bm_lookup_chain.rb
index 3b32727f5..813d606f8 100644
--- a/test/benchmark/bm_lookup_chain.rb
+++ b/test/benchmark/bm_lookup_chain.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './benchmarking_support'
require_relative './app'
diff --git a/test/benchmark/bm_transform.rb b/test/benchmark/bm_transform.rb
index 97c655c01..b6046973c 100644
--- a/test/benchmark/bm_transform.rb
+++ b/test/benchmark/bm_transform.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './benchmarking_support'
require_relative './app'
diff --git a/test/benchmark/config.ru b/test/benchmark/config.ru
index 908eb28c4..7b0654bbd 100644
--- a/test/benchmark/config.ru
+++ b/test/benchmark/config.ru
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require File.expand_path(['..', 'app'].join(File::SEPARATOR), __FILE__)
run Rails.application
diff --git a/test/benchmark/controllers.rb b/test/benchmark/controllers.rb
index 81108445b..9829fc267 100644
--- a/test/benchmark/controllers.rb
+++ b/test/benchmark/controllers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PrimaryResourceController < ActionController::Base
PRIMARY_RESOURCE =
begin
diff --git a/test/benchmark/fixtures.rb b/test/benchmark/fixtures.rb
index c91e102d4..9d5f6606d 100644
--- a/test/benchmark/fixtures.rb
+++ b/test/benchmark/fixtures.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.configuration.serializers = []
class HasOneRelationshipSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name
diff --git a/test/cache_test.rb b/test/cache_test.rb
index f09589314..112e366cb 100644
--- a/test/cache_test.rb
+++ b/test/cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require 'tmpdir'
require 'tempfile'
@@ -53,6 +55,11 @@ class AuthorSerializer < ActiveModel::Serializer
has_many :roles
has_one :bio
end
+ class AuthorSerializerWithCache < ActiveModel::Serializer
+ cache
+
+ attributes :name
+ end
class Blog < ::Model
attributes :name
@@ -144,6 +151,67 @@ class InheritedRoleSerializer < RoleSerializer
@blog_serializer = BlogSerializer.new(@blog)
end
+ def test_expiring_of_cache_at_update_of_record
+ original_cache_versioning = :none
+
+ if ARModels::Author.respond_to?(:cache_versioning)
+ original_cache_versioning = ARModels::Author.cache_versioning
+ ARModels::Author.cache_versioning = true
+ end
+
+ author = ARModels::Author.create(name: 'Foo')
+ author_json = AuthorSerializerWithCache.new(author).as_json
+
+ assert_equal 'Foo', author_json[:name]
+
+ author.update(name: 'Bar')
+ author_json = AuthorSerializerWithCache.new(author).as_json
+
+ expected = 'Bar'
+ actual = author_json[:name]
+ if ENV['APPVEYOR'] && actual != expected
+ skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)')
+ else
+ assert_equal expected, actual
+ end
+ ensure
+ ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none
+ end
+
+ def test_cache_expiration_in_collection_on_update_of_record
+ original_cache_versioning = :none
+
+ if ARModels::Author.respond_to?(:cache_versioning)
+ original_cache_versioning = ARModels::Author.cache_versioning
+ ARModels::Author.cache_versioning = true
+ end
+
+ foo = 'Foo'
+ foo2 = 'Foo2'
+ author = ARModels::Author.create(name: foo)
+ author2 = ARModels::Author.create(name: foo2)
+ author_collection = [author, author, author2]
+
+ collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache)
+ actual = collection_json
+ expected = [{ name: foo }, { name: foo }, { name: foo2 }]
+ if ENV['APPVEYOR'] && actual != expected
+ skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)')
+ else
+ assert_equal expected, actual
+ end
+
+ bar = 'Bar'
+ Timecop.travel(10.seconds.from_now) do
+ author.update!(name: bar)
+
+ collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache)
+ assert_equal [{ name: bar }, { name: bar }, { name: foo2 }], collection_json
+ end
+ ensure
+ ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none
+ end
+
def test_explicit_cache_store
default_store = Class.new(ActiveModel::Serializer) do
cache
@@ -378,17 +446,41 @@ def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attribut
# rubocop:enable Metrics/AbcSize
def test_uses_file_digest_in_cache_key
+ reset_cache_digest(@blog_serializer)
render_object_with_cache(@blog)
file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read)
key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}"
assert_equal(@blog_serializer.attributes, cache_store.fetch(key))
end
+ def test_uses_sha1_digest_in_cache_key_when_configured
+ reset_cache_digest(@blog_serializer)
+ previous_use_sha1_digests = ActiveModelSerializers.config.use_sha1_digests
+ ActiveModelSerializers.config.use_sha1_digests = true
+ render_object_with_cache(@blog)
+ file_digest = Digest::SHA1.hexdigest(File.open(__FILE__).read)
+ key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}"
+ assert_equal(@blog_serializer.attributes, cache_store.fetch(key))
+ ensure
+ ActiveModelSerializers.config.use_sha1_digests = previous_use_sha1_digests
+ end
+
def test_cache_digest_definition
+ reset_cache_digest(@post_serializer)
file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read)
assert_equal(file_digest, @post_serializer.class._cache_digest)
end
+ def test_cache_sha1_digest_definition
+ reset_cache_digest(@post_serializer)
+ previous_use_sha1_digests = ActiveModelSerializers.config.use_sha1_digests
+ ActiveModelSerializers.config.use_sha1_digests = true
+ file_digest = Digest::SHA1.hexdigest(File.open(__FILE__).read)
+ assert_equal(file_digest, @post_serializer.class._cache_digest)
+ ensure
+ ActiveModelSerializers.config.use_sha1_digests = previous_use_sha1_digests
+ end
+
def test_object_cache_keys
serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment])
include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true)
@@ -415,7 +507,7 @@ def test_fetch_attributes_from_cache
adapter_options = {}
adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options)
serializers.serializable_hash(adapter_options, options, adapter_instance)
- cached_attributes = adapter_options.fetch(:cached_attributes).with_indifferent_access
+ cached_attributes = options.fetch(:cached_attributes).with_indifferent_access
include_directive = ActiveModelSerializers.default_include_directive
manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive).with_indifferent_access
@@ -446,9 +538,9 @@ def test_cache_read_multi_with_fragment_cache_enabled
serializers.serializable_hash(adapter_options, options, adapter_instance)
# Should find something with read_multi now
- adapter_options = {}
+ options = {}
serializers.serializable_hash(adapter_options, options, adapter_instance)
- cached_attributes = adapter_options.fetch(:cached_attributes)
+ cached_attributes = options.fetch(:cached_attributes)
include_directive = ActiveModelSerializers.default_include_directive
manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive)
@@ -647,5 +739,10 @@ def render_object_with_cache(obj, options = {})
def adapter
@serializable_resource.adapter
end
+
+ def reset_cache_digest(serializer)
+ return unless serializer.class.instance_variable_defined?(:@_cache_digest)
+ serializer.class.remove_instance_variable(:@_cache_digest)
+ end
end
end
diff --git a/test/collection_serializer_test.rb b/test/collection_serializer_test.rb
index cdbebb158..856cbed7a 100644
--- a/test/collection_serializer_test.rb
+++ b/test/collection_serializer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
@@ -20,6 +22,8 @@ class MessagesSerializer < ActiveModel::Serializer
type 'messages'
end
+ class NonTypeSerializer < ActiveModel::Serializer; end
+
def setup
@singular_model = SingularModel.new
@has_many_model = HasManyModel.new
@@ -93,12 +97,34 @@ def test_json_key_with_resource_with_nil_name_and_no_serializers
resource = []
resource.define_singleton_method(:name) { nil }
serializer = collection_serializer.new(resource)
- assert_nil serializer.json_key
+ assert_raise ActiveModel::Serializer::CollectionSerializer::CannotInferRootKeyError do
+ serializer.json_key
+ end
end
def test_json_key_with_resource_without_name_and_no_serializers
serializer = collection_serializer.new([])
+ assert_raise ActiveModel::Serializer::CollectionSerializer::CannotInferRootKeyError do
+ serializer.json_key
+ end
+ end
+
+ def test_json_key_with_empty_resources_with_non_type_serializer
+ resource = []
+ serializer = collection_serializer.new(resource, serializer: NonTypeSerializer)
+ assert_raise ActiveModel::Serializer::CollectionSerializer::CannotInferRootKeyError do
+ serializer.json_key
+ end
+ end
+
+ def test_json_key_with_empty_resources_with_non_type_serializer_when_raise_cannot_infer_root_key_error_is_false
+ previous_raise_cannot_infer_root_key_error = ActiveModelSerializers.config.raise_cannot_infer_root_key_error
+ ActiveModelSerializers.config.raise_cannot_infer_root_key_error = false
+ resource = []
+ serializer = collection_serializer.new(resource, serializer: NonTypeSerializer)
assert_nil serializer.json_key
+ ensure
+ ActiveModelSerializers.config.raise_cannot_infer_root_key_error = previous_raise_cannot_infer_root_key_error
end
def test_json_key_with_empty_resources_with_serializer
diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb
index 9dc3830da..132a43b16 100644
--- a/test/fixtures/active_record.rb
+++ b/test/fixtures/active_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
@@ -17,28 +19,28 @@
t.text :contents
t.references :author
t.references :post
- t.timestamp null: false
+ t.timestamps null: false
end
create_table :employees, force: true do |t|
t.string :name
t.string :email
- t.timestamp null: false
+ t.timestamps null: false
end
create_table :object_tags, force: true do |t|
t.string :poly_tag_id
t.string :taggable_type
t.string :taggable_id
- t.timestamp null: false
+ t.timestamps null: false
end
create_table :poly_tags, force: true do |t|
t.string :phrase
- t.timestamp null: false
+ t.timestamps null: false
end
create_table :pictures, force: true do |t|
t.string :title
t.string :imageable_type
t.string :imageable_id
- t.timestamp null: false
+ t.timestamps null: false
end
end
@@ -89,7 +91,7 @@ class ObjectTag < ActiveRecord::Base
end
class PolymorphicObjectTagSerializer < ActiveModel::Serializer
attributes :id
- has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
+ belongs_to :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
end
class PolyTag < ActiveRecord::Base
@@ -109,5 +111,5 @@ class PolymorphicHasManySerializer < ActiveModel::Serializer
end
class PolymorphicBelongsToSerializer < ActiveModel::Serializer
attributes :id, :title
- has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
+ belongs_to :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
end
diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb
index 6245ad23d..d45c257f7 100644
--- a/test/fixtures/poro.rb
+++ b/test/fixtures/poro.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Model < ActiveModelSerializers::Model
rand(2).zero? && derive_attributes_from_names_and_fix_accessors
diff --git a/test/generators/scaffold_controller_generator_test.rb b/test/generators/scaffold_controller_generator_test.rb
index 183bb4f6f..797cc5327 100644
--- a/test/generators/scaffold_controller_generator_test.rb
+++ b/test/generators/scaffold_controller_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require 'generators/rails/resource_override'
diff --git a/test/generators/serializer_generator_test.rb b/test/generators/serializer_generator_test.rb
index eef4a41e1..5a966e0a7 100644
--- a/test/generators/serializer_generator_test.rb
+++ b/test/generators/serializer_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require 'generators/rails/resource_override'
require 'generators/rails/serializer_generator'
diff --git a/test/grape_test.rb b/test/grape_test.rb
deleted file mode 100644
index 4851e57a7..000000000
--- a/test/grape_test.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-require 'test_helper'
-TestHelper.silence_warnings do
- require 'grape'
-end
-require 'grape/active_model_serializers'
-require 'kaminari'
-require 'kaminari/hooks'
-::Kaminari::Hooks.init
-
-module ActiveModelSerializers
- class GrapeTest < ActiveSupport::TestCase
- include Rack::Test::Methods
- module Models
- def self.model1
- ARModels::Post.new(id: 1, title: 'Dummy Title', body: 'Lorem Ipsum')
- end
-
- def self.model2
- ARModels::Post.new(id: 2, title: 'Second Dummy Title', body: 'Second Lorem Ipsum')
- end
-
- def self.all
- @all ||=
- begin
- model1.save!
- model2.save!
- ARModels::Post.all
- end
- end
-
- def self.reset_all
- ARModels::Post.delete_all
- @all = nil
- end
-
- def self.collection_per
- 2
- end
-
- def self.collection
- @collection ||=
- begin
- Kaminari.paginate_array(
- [
- Profile.new(id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1'),
- Profile.new(id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2'),
- Profile.new(id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3'),
- Profile.new(id: 4, name: 'Name 4', description: 'Description 4', comments: 'Comments 4'),
- Profile.new(id: 5, name: 'Name 5', description: 'Description 5', comments: 'Comments 5')
- ]
- ).page(1).per(collection_per)
- end
- end
- end
-
- class GrapeTest < Grape::API
- format :json
- TestHelper.silence_warnings do
- include Grape::ActiveModelSerializers
- end
-
- def self.resources(*)
- TestHelper.silence_warnings do
- super
- end
- end
-
- resources :grape do
- get '/render' do
- render Models.model1
- end
-
- get '/render_with_json_api' do
- post = Models.model1
- render post, meta: { page: 1, total_pages: 2 }, adapter: :json_api
- end
-
- get '/render_array_with_json_api' do
- posts = Models.all
- render posts, adapter: :json_api
- end
-
- get '/render_collection_with_json_api' do
- posts = Models.collection
- render posts, adapter: :json_api
- end
-
- get '/render_with_implicit_formatter' do
- Models.model1
- end
-
- get '/render_array_with_implicit_formatter' do
- Models.all
- end
-
- get '/render_collection_with_implicit_formatter' do
- Models.collection
- end
- end
- end
-
- def app
- Grape::Middleware::Globals.new(GrapeTest.new)
- end
-
- extend Minitest::Assertions
- def self.run_one_method(*)
- _, stderr = capture_io do
- super
- end
- fail Minitest::Assertion, stderr if stderr !~ /grape/
- end
-
- def test_formatter_returns_json
- get '/grape/render'
-
- post = Models.model1
- serializable_resource = serializable(post)
-
- assert last_response.ok?
- assert_equal serializable_resource.to_json, last_response.body
- end
-
- def test_render_helper_passes_through_options_correctly
- get '/grape/render_with_json_api'
-
- post = Models.model1
- serializable_resource = serializable(post, serializer: ARModels::PostSerializer, adapter: :json_api, meta: { page: 1, total_pages: 2 })
-
- assert last_response.ok?
- assert_equal serializable_resource.to_json, last_response.body
- end
-
- def test_formatter_handles_arrays
- get '/grape/render_array_with_json_api'
-
- posts = Models.all
- serializable_resource = serializable(posts, adapter: :json_api)
-
- assert last_response.ok?
- assert_equal serializable_resource.to_json, last_response.body
- ensure
- Models.reset_all
- end
-
- def test_formatter_handles_collections
- get '/grape/render_collection_with_json_api'
- assert last_response.ok?
-
- representation = JSON.parse(last_response.body)
- assert representation.include?('data')
- assert representation['data'].count == Models.collection_per
- assert representation.include?('links')
- assert representation['links'].count > 0
- end
-
- def test_implicit_formatter
- post = Models.model1
- serializable_resource = serializable(post, adapter: :json_api)
-
- with_adapter :json_api do
- get '/grape/render_with_implicit_formatter'
- end
-
- assert last_response.ok?
- assert_equal serializable_resource.to_json, last_response.body
- end
-
- def test_implicit_formatter_handles_arrays
- posts = Models.all
- serializable_resource = serializable(posts, adapter: :json_api)
-
- with_adapter :json_api do
- get '/grape/render_array_with_implicit_formatter'
- end
-
- assert last_response.ok?
- assert_equal serializable_resource.to_json, last_response.body
- ensure
- Models.reset_all
- end
-
- def test_implicit_formatter_handles_collections
- with_adapter :json_api do
- get '/grape/render_collection_with_implicit_formatter'
- end
-
- representation = JSON.parse(last_response.body)
- assert last_response.ok?
- assert representation.include?('data')
- assert representation['data'].count == Models.collection_per
- assert representation.include?('links')
- assert representation['links'].count > 0
- end
- end
-end
diff --git a/test/lint_test.rb b/test/lint_test.rb
index d404ccec1..b1d328dc5 100644
--- a/test/lint_test.rb
+++ b/test/lint_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/logger_test.rb b/test/logger_test.rb
index a15227bb0..30272006e 100644
--- a/test/logger_test.rb
+++ b/test/logger_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/poro_test.rb b/test/poro_test.rb
index e5fba8587..f1a57b199 100644
--- a/test/poro_test.rb
+++ b/test/poro_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
class PoroTest < ActiveSupport::TestCase
diff --git a/test/serializable_resource_test.rb b/test/serializable_resource_test.rb
index ab12bc27b..f996048f3 100644
--- a/test/serializable_resource_test.rb
+++ b/test/serializable_resource_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModelSerializers
diff --git a/test/serializers/association_macros_test.rb b/test/serializers/association_macros_test.rb
index 3d5f05c5f..b640997de 100644
--- a/test/serializers/association_macros_test.rb
+++ b/test/serializers/association_macros_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb
index c1b164b8d..a86b7a3f4 100644
--- a/test/serializers/associations_test.rb
+++ b/test/serializers/associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
class Serializer
@@ -159,12 +161,56 @@ def blog_id
end
end
- actual = serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json
+ actual =
+ begin
+ original_option = BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship
+ BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
+ serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json
+ ensure
+ BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option
+ end
expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } }
assert_equal expected, actual
end
+ class ExternalBlog < Blog
+ attributes :external_id
+ end
+ class BelongsToExternalBlogModel < ::Model
+ attributes :id, :title, :external_blog_id
+ associations :external_blog
+ end
+ class BelongsToExternalBlogModelSerializer < ActiveModel::Serializer
+ type :posts
+ belongs_to :external_blog
+
+ def external_blog_id
+ object.external_blog.external_id
+ end
+ end
+
+ def test_belongs_to_allows_id_overwriting
+ attributes = {
+ id: 1,
+ title: 'Title',
+ external_blog: ExternalBlog.new(id: 5, external_id: 6)
+ }
+ post = BelongsToExternalBlogModel.new(attributes)
+
+ actual =
+ begin
+ original_option = BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship
+ BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
+ serializable(post, adapter: :json_api, serializer: BelongsToExternalBlogModelSerializer).as_json
+ ensure
+ BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option
+ end
+ expected = { data: { id: '1', type: 'posts', relationships: { :'external-blog' => { data: { id: '6', type: 'external-blogs' } } } } }
+
+ assert_equal expected, actual
+ end
+
class InlineAssociationTestPostSerializer < ActiveModel::Serializer
has_many :comments
has_many :comments, key: :last_comments do
@@ -241,6 +287,56 @@ def test_associations_namespaced_resources
end
end
+ class AssociationsNamespacedSerializersTest < ActiveSupport::TestCase
+ class Post < ::Model
+ associations :comments, :author, :description
+
+ def latest_comments
+ comments[0..3]
+ end
+ end
+ class Comment < ::Model; end
+ class Author < ::Model; end
+ class Description < ::Model; end
+
+ class ResourceNamespace
+ class PostSerializer < ActiveModel::Serializer
+ has_many :comments, namespace: ResourceNamespace
+ has_many :latest_comments, namespace: ResourceNamespace
+ belongs_to :author, namespace: ResourceNamespace
+ has_one :description, namespace: ResourceNamespace
+ end
+ class CommentSerializer < ActiveModel::Serializer; end
+ class AuthorSerializer < ActiveModel::Serializer; end
+ class DescriptionSerializer < ActiveModel::Serializer; end
+ end
+
+ def setup
+ @comment = Comment.new
+ @author = Author.new
+ @description = Description.new
+ @post = Post.new(comments: [@comment],
+ author: @author,
+ description: @description)
+ @post_serializer = ResourceNamespace::PostSerializer.new(@post)
+ end
+
+ def test_associations_namespaced_serializers
+ @post_serializer.associations.each do |association|
+ case association.key
+ when :comments, :latest_comments
+ assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first)
+ when :author
+ assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer)
+ when :description
+ assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer)
+ else
+ flunk "Unknown association: #{key}"
+ end
+ end
+ end
+ end
+
class NestedSerializersTest < ActiveSupport::TestCase
class Post < ::Model
associations :comments, :author, :description
diff --git a/test/serializers/attribute_test.rb b/test/serializers/attribute_test.rb
index 608898c3e..41386ea59 100644
--- a/test/serializers/attribute_test.rb
+++ b/test/serializers/attribute_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/attributes_test.rb b/test/serializers/attributes_test.rb
index fb792b26f..82ed7b1e6 100644
--- a/test/serializers/attributes_test.rb
+++ b/test/serializers/attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/caching_configuration_test_isolated.rb b/test/serializers/caching_configuration_test_isolated.rb
index b5698dd1a..326225a4d 100644
--- a/test/serializers/caching_configuration_test_isolated.rb
+++ b/test/serializers/caching_configuration_test_isolated.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Execute this test in isolation
require 'support/isolated_unit'
diff --git a/test/serializers/configuration_test.rb b/test/serializers/configuration_test.rb
index 2c5f922f4..1ac70dbb2 100644
--- a/test/serializers/configuration_test.rb
+++ b/test/serializers/configuration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/fieldset_test.rb b/test/serializers/fieldset_test.rb
index 5b99d57a6..781696340 100644
--- a/test/serializers/fieldset_test.rb
+++ b/test/serializers/fieldset_test.rb
@@ -1,13 +1,25 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
class Serializer
class FieldsetTest < ActiveSupport::TestCase
+ def setup
+ @fieldset = ActiveModel::Serializer::Fieldset.new('post' => %w(id title), 'comment' => ['body'])
+ end
+
def test_fieldset_with_hash
- fieldset = ActiveModel::Serializer::Fieldset.new('post' => %w(id title), 'comment' => ['body'])
expected = { post: [:id, :title], comment: [:body] }
- assert_equal(expected, fieldset.fields)
+ assert_equal(expected, @fieldset.fields)
+ end
+
+ def test_fields_for_accepts_string_or_symbol
+ expected = [:id, :title]
+
+ assert_equal(expected, @fieldset.fields_for(:post))
+ assert_equal(expected, @fieldset.fields_for('post'))
end
end
end
diff --git a/test/serializers/meta_test.rb b/test/serializers/meta_test.rb
index 642093215..1ece1db46 100644
--- a/test/serializers/meta_test.rb
+++ b/test/serializers/meta_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/options_test.rb b/test/serializers/options_test.rb
index 009388e35..6e1b34596 100644
--- a/test/serializers/options_test.rb
+++ b/test/serializers/options_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/read_attribute_for_serialization_test.rb b/test/serializers/read_attribute_for_serialization_test.rb
index 02911c0e6..6095bf256 100644
--- a/test/serializers/read_attribute_for_serialization_test.rb
+++ b/test/serializers/read_attribute_for_serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/reflection_test.rb b/test/serializers/reflection_test.rb
index 11cb154be..bb90e81c8 100644
--- a/test/serializers/reflection_test.rb
+++ b/test/serializers/reflection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
class Serializer
@@ -423,5 +425,57 @@ def test_mutating_reflection_block_is_not_thread_safe
end
# rubocop:enable Metrics/AbcSize
end
+ class ThreadedReflectionTest < ActiveSupport::TestCase
+ class Post < ::Model
+ attributes :id, :title, :body
+ associations :comments
+ end
+ class Comment < ::Model
+ attributes :id, :body
+ associations :post
+ end
+ class CommentSerializer < ActiveModel::Serializer
+ type 'comment'
+ attributes :id, :body
+ has_one :post
+ end
+ class PostSerializer < ActiveModel::Serializer
+ type 'post'
+ attributes :id, :title, :body
+ has_many :comments, serializer: CommentSerializer do
+ sleep 0.1
+ object.comments
+ end
+ end
+
+ # per https://github.com/rails-api/active_model_serializers/issues/2270
+ def test_concurrent_serialization
+ post1 = Post.new(id: 1, title: 'Post 1 Title', body: 'Post 1 Body')
+ post1.comments = [Comment.new(id: 1, body: 'Comment on Post 1', post: post1)]
+ post2 = Post.new(id: 2, title: 'Post 2 Title', body: 'Post 2 Body')
+ post2.comments = [Comment.new(id: 2, body: 'Comment on Post 2', post: post2)]
+ serialized_posts = {
+ first: Set.new,
+ second: Set.new
+ }
+ t1 = Thread.new do
+ 10.times do
+ serialized_posts[:first] << PostSerializer.new(post1, {}).to_json
+ end
+ end
+ t2 = Thread.new do
+ 10.times do
+ serialized_posts[:second] << PostSerializer.new(post2, {}).to_json
+ end
+ end
+ t1.join
+ t2.join
+ expected_first_post_serialization = '{"id":1,"title":"Post 1 Title","body":"Post 1 Body","comments":[{"id":1,"body":"Comment on Post 1"}]}'
+ expected_second_post_serialization = '{"id":2,"title":"Post 2 Title","body":"Post 2 Body","comments":[{"id":2,"body":"Comment on Post 2"}]}'
+
+ assert_equal [expected_second_post_serialization], serialized_posts[:second].to_a
+ assert_equal [expected_first_post_serialization], serialized_posts[:first].to_a
+ end
+ end
end
end
diff --git a/test/serializers/root_test.rb b/test/serializers/root_test.rb
index 5bd4cdc3b..50c019758 100644
--- a/test/serializers/root_test.rb
+++ b/test/serializers/root_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/serialization_test.rb b/test/serializers/serialization_test.rb
index 3c1884e62..21ec0dcc8 100644
--- a/test/serializers/serialization_test.rb
+++ b/test/serializers/serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveModel
class Serializer
class SerializationTest < ActiveSupport::TestCase
diff --git a/test/serializers/serializer_for_test.rb b/test/serializers/serializer_for_test.rb
index 9f6917081..551fb8d7a 100644
--- a/test/serializers/serializer_for_test.rb
+++ b/test/serializers/serializer_for_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
diff --git a/test/serializers/serializer_for_with_namespace_test.rb b/test/serializers/serializer_for_with_namespace_test.rb
index 5c6e3e5e9..f0f84528d 100644
--- a/test/serializers/serializer_for_with_namespace_test.rb
+++ b/test/serializers/serializer_for_with_namespace_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
module ActiveModel
@@ -7,6 +9,7 @@ class Book < ::Model
attributes :title, :author_name
associations :publisher, :pages
end
+ class Ebook < Book; end
class Page < ::Model; attributes :number, :text end
class Publisher < ::Model; attributes :name end
@@ -32,12 +35,11 @@ class PublisherSerializer < ActiveModel::Serializer
class BookSerializer < ActiveModel::Serializer
attributes :title, :author_name
end
+
test 'resource without a namespace' do
book = Book.new(title: 'A Post', author_name: 'hello')
- # TODO: this should be able to pull up this serializer without explicitly specifying the serializer
- # currently, with no options, it still uses the Api::V3 serializer
- result = ActiveModelSerializers::SerializableResource.new(book, serializer: BookSerializer).serializable_hash
+ result = ActiveModelSerializers::SerializableResource.new(book).serializable_hash
expected = { title: 'A Post', author_name: 'hello' }
assert_equal expected, result
@@ -83,6 +85,11 @@ class BookSerializer < ActiveModel::Serializer
}
assert_equal expected, result
end
+
+ test 'follows inheritance with a namespace' do
+ serializer = ActiveModel::Serializer.serializer_for(Ebook.new, namespace: Api::V3)
+ assert_equal Api::V3::BookSerializer, serializer
+ end
end
end
end
diff --git a/test/support/isolated_unit.rb b/test/support/isolated_unit.rb
index 26948d4aa..b83e0b296 100644
--- a/test/support/isolated_unit.rb
+++ b/test/support/isolated_unit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# https://github.com/rails/rails/blob/v5.0.0.beta1/railties/test/isolation/abstract_unit.rb
# Usage Example:
@@ -54,7 +56,7 @@ def make_basic_app
require 'rails'
require 'action_controller/railtie'
- @app = Class.new(Rails::Application) do
+ app = Class.new(Rails::Application) do
config.eager_load = false
config.session_store :cookie_store, key: '_myapp_session'
config.active_support.deprecation = :log
@@ -66,9 +68,12 @@ def make_basic_app
fake_logger = Logger.new(nil)
config.logger = fake_logger
Rails.application.routes.default_url_options = { host: 'example.com' }
+ config.hosts << 'www.example.com' if Rails.version >= '6.0'
end
- @app.respond_to?(:secrets) && @app.secrets.secret_key_base = '3b7cd727ee24e8444053437c36cc66c4'
+ def app.name; 'IsolatedRailsApp'; end # rubocop:disable Style/SingleLineMethods
+ app.respond_to?(:secrets) && app.secrets.secret_key_base = '3b7cd727ee24e8444053437c36cc66c4'
+ @app = app
yield @app if block_given?
@app.initialize!
end
diff --git a/test/support/rails5_shims.rb b/test/support/rails5_shims.rb
index 03a036da6..55de76e96 100644
--- a/test/support/rails5_shims.rb
+++ b/test/support/rails5_shims.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails5Shims
module ControllerTests
# https://github.com/rails/rails/blob/b217354/actionpack/lib/action_controller/test_case.rb
diff --git a/test/support/rails_app.rb b/test/support/rails_app.rb
index 43324b78c..26cc770e0 100644
--- a/test/support/rails_app.rb
+++ b/test/support/rails_app.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'support/isolated_unit'
module ActiveModelSerializers
RailsApplication = TestHelpers::Generation.make_basic_app do |app|
@@ -7,7 +9,13 @@ module ActiveModelSerializers
config.action_controller.perform_caching = true
config.action_controller.cache_store = :memory_store
+ if Rails::VERSION::MAJOR >= 6
+ config.action_dispatch.return_only_media_type_on_content_type = true
+ end
+
config.filter_parameters += [:password]
+
+ config.hosts << 'www.example.com' if Rails.version >= '6.0'
end
app.routes.default_url_options = { host: 'example.com' }
diff --git a/test/support/ruby_2_6_rails_4_2_patch.rb b/test/support/ruby_2_6_rails_4_2_patch.rb
new file mode 100644
index 000000000..768e94305
--- /dev/null
+++ b/test/support/ruby_2_6_rails_4_2_patch.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+if RUBY_VERSION >= '2.6.0'
+ if Rails::VERSION::MAJOR < 5
+ module ActionController
+ class TestResponse < ActionDispatch::TestResponse
+ def recycle!
+ # HACK: to avoid MonitorMixin double-initialize error:
+ @mon_mutex_owner_object_id = nil
+ @mon_mutex = nil
+ initialize
+ end
+ end
+ end
+ else
+ msg = 'Monkeypatch for ActionController::TestResponse not needed for '\
+ 'Rails 5+. We can drop this patch once we drop support for Rails < 5. '\
+ "Current Rails version: #{ENV['RAILS_VERSION']}"
+
+ puts
+ puts "\033[33m **** #{msg} **** \033[0m"
+ puts
+ end
+end
diff --git a/test/support/serialization_testing.rb b/test/support/serialization_testing.rb
index 524a32976..ada7fa3bf 100644
--- a/test/support/serialization_testing.rb
+++ b/test/support/serialization_testing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SerializationTesting
def config
ActiveModelSerializers.config
@@ -18,11 +20,11 @@ def with_namespace_separator(separator)
end
def with_prepended_lookup(lookup_proc)
- original_lookup = ActiveModelSerializers.config.serializer_lookup_cahin
+ original_lookup = ActiveModelSerializers.config.serializer_lookup_chain
ActiveModelSerializers.config.serializer_lookup_chain.unshift lookup_proc
yield
ensure
- ActiveModelSerializers.config.serializer_lookup_cahin = original_lookup
+ ActiveModelSerializers.config.serializer_lookup_chain = original_lookup
end
# Aliased as :with_configured_adapter to clarify that
@@ -47,6 +49,14 @@ def with_config(hash)
ActiveModelSerializers.config.replace(old_config)
end
+ def with_jsonapi_inflection(inflection)
+ original_inflection = ActiveModelSerializers.config.jsonapi_resource_type
+ ActiveModelSerializers.config.jsonapi_resource_type = inflection
+ yield
+ ensure
+ ActiveModelSerializers.config.jsonapi_resource_type = original_inflection
+ end
+
def with_serializer_lookup_disabled
original_serializer_lookup = ActiveModelSerializers.config.serializer_lookup_enabled
ActiveModelSerializers.config.serializer_lookup_enabled = false
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 294fa33c4..7d8ca63e3 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,14 +1,9 @@
+# frozen_string_literal: true
+
# Configure Rails Environment
ENV['RAILS_ENV'] = 'test'
require 'bundler/setup'
-begin
- require 'simplecov'
- AppCoverage.start
-rescue LoadError
- STDERR.puts 'Running without SimpleCov'
-end
-
require 'pry'
require 'timecop'
require 'rails'
@@ -54,6 +49,8 @@ def silence_warnings
require 'support/rails_app'
+require 'support/ruby_2_6_rails_4_2_patch'
+
# require "rails/test_help"
require 'support/serialization_testing'