mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Clear out master
This commit is contained in:
parent
dff621e174
commit
0ef6ac30fc
105
.rubocop.yml
105
.rubocop.yml
@ -1,105 +0,0 @@
|
|||||||
AllCops:
|
|
||||||
TargetRubyVersion: 2.1
|
|
||||||
Exclude:
|
|
||||||
- !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/
|
|
||||||
DisplayCopNames: true
|
|
||||||
DisplayStyleGuide: true
|
|
||||||
# https://github.com/bbatsov/rubocop/blob/master/manual/caching.md
|
|
||||||
# https://github.com/bbatsov/rubocop/blob/e8680418b351491e111a18cf5b453fc07a3c5239/config/default.yml#L60-L77
|
|
||||||
UseCache: true
|
|
||||||
CacheRootDirectory: tmp
|
|
||||||
|
|
||||||
Rails:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/NestedMethodDefinition:
|
|
||||||
Enabled: false
|
|
||||||
Exclude:
|
|
||||||
- test/action_controller/serialization_test.rb
|
|
||||||
|
|
||||||
Style/Alias:
|
|
||||||
EnforcedStyle: prefer_alias
|
|
||||||
|
|
||||||
Style/StringLiterals:
|
|
||||||
EnforcedStyle: single_quotes
|
|
||||||
|
|
||||||
Metrics/AbcSize:
|
|
||||||
Max: 35 # TODO: Lower to 15
|
|
||||||
|
|
||||||
Metrics/ClassLength:
|
|
||||||
Max: 261 # TODO: Lower to 100
|
|
||||||
Exclude:
|
|
||||||
- test/**/*.rb
|
|
||||||
|
|
||||||
Metrics/CyclomaticComplexity:
|
|
||||||
Max: 7 # TODO: Lower to 6
|
|
||||||
|
|
||||||
Metrics/LineLength:
|
|
||||||
Max: 251 # TODO: Lower to 80
|
|
||||||
|
|
||||||
Metrics/MethodLength:
|
|
||||||
Max: 106 # TODO: Lower to 10
|
|
||||||
|
|
||||||
Metrics/PerceivedComplexity:
|
|
||||||
Max: 9 # TODO: Lower to 7
|
|
||||||
|
|
||||||
Style/AlignParameters:
|
|
||||||
EnforcedStyle: with_fixed_indentation
|
|
||||||
|
|
||||||
Style/ClassAndModuleChildren:
|
|
||||||
EnforcedStyle: nested
|
|
||||||
|
|
||||||
Style/Documentation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/MissingElse:
|
|
||||||
Enabled: true
|
|
||||||
EnforcedStyle: case
|
|
||||||
|
|
||||||
Style/EmptyElse:
|
|
||||||
EnforcedStyle: empty
|
|
||||||
|
|
||||||
Style/MultilineOperationIndentation:
|
|
||||||
EnforcedStyle: indented
|
|
||||||
|
|
||||||
Style/BlockDelimiters:
|
|
||||||
Enabled: true
|
|
||||||
EnforcedStyle: line_count_based
|
|
||||||
|
|
||||||
Style/SignalException:
|
|
||||||
EnforcedStyle: semantic
|
|
||||||
|
|
||||||
Style/TrailingCommaInLiteral:
|
|
||||||
EnforcedStyleForMultiline: no_comma
|
|
||||||
|
|
||||||
Style/ConditionalAssignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/DotPosition:
|
|
||||||
EnforcedStyle: leading
|
|
||||||
|
|
||||||
########## test_helper.rb sanity
|
|
||||||
Style/EndBlock:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
|
|
||||||
Style/SpecialGlobalVars:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
|
|
||||||
Style/GlobalVars:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
|
|
||||||
Style/AndOr:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
- 'lib/active_model/serializer/lint.rb'
|
|
||||||
|
|
||||||
Style/Not:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
|
|
||||||
Style/ClassCheck:
|
|
||||||
Exclude:
|
|
||||||
- test/test_helper.rb
|
|
||||||
110
.simplecov
110
.simplecov
@ -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
|
|
||||||
47
.travis.yml
47
.travis.yml
@ -2,54 +2,9 @@ language: ruby
|
|||||||
|
|
||||||
sudo: false
|
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:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- vendor/bundle
|
- vendor/bundle
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- bundle exec rake ci
|
- true
|
||||||
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
|
|
||||||
|
|||||||
92
CHANGELOG-0-08.md
Normal file
92
CHANGELOG-0-08.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
## 0.08.x
|
||||||
|
|
||||||
|
### v0.8.3 (2014/12/10 14:45 +00:00)
|
||||||
|
- [#753](https://github.com/rails-api/active_model_serializers/pull/753) Test against Ruby 2.2 on Travis CI (@tricknotes)
|
||||||
|
- [#745](https://github.com/rails-api/active_model_serializers/pull/745) Missing a word (@jockee)
|
||||||
|
|
||||||
|
### v0.8.2 (2014/09/01 21:00 +00:00)
|
||||||
|
- [#612](https://github.com/rails-api/active_model_serializers/pull/612) Feature/adapter (@bolshakov)
|
||||||
|
* adds adapters pattern
|
||||||
|
- [#615](https://github.com/rails-api/active_model_serializers/pull/615) Rails does not support const_defined? in development mode (@tpitale)
|
||||||
|
- [#613](https://github.com/rails-api/active_model_serializers/pull/613) README: typo fix on attributes (@spk)
|
||||||
|
- [#614](https://github.com/rails-api/active_model_serializers/pull/614) Fix rails 4.0.x build. (@arthurnn)
|
||||||
|
- [#610](https://github.com/rails-api/active_model_serializers/pull/610) ArraySerializer (@bolshakov)
|
||||||
|
- [#607](https://github.com/rails-api/active_model_serializers/pull/607) ruby syntax highlights (@zigomir)
|
||||||
|
- [#602](https://github.com/rails-api/active_model_serializers/pull/602) Add DSL for associations (@JordanFaust)
|
||||||
|
|
||||||
|
### 0.8.1 (May 6, 2013)
|
||||||
|
|
||||||
|
* Fix bug whereby a serializer using 'options' would blow up.
|
||||||
|
|
||||||
|
### 0.8.0 (May 5, 2013)
|
||||||
|
|
||||||
|
* Attributes can now have optional types.
|
||||||
|
|
||||||
|
* A new DefaultSerializer ensures that POROs behave the same way as ActiveModels.
|
||||||
|
|
||||||
|
* If you wish to override ActiveRecord::Base#to_Json, you can now require
|
||||||
|
'active_record/serializer_override'. We don't recommend you do this, but
|
||||||
|
many users do, so we've left it optional.
|
||||||
|
|
||||||
|
* Fixed a bug where ActionController wouldn't always have MimeResponds.
|
||||||
|
|
||||||
|
* An optinal caching feature allows you to cache JSON & hashes that AMS uses.
|
||||||
|
Adding 'cached true' to your Serializers will turn on this cache.
|
||||||
|
|
||||||
|
* URL helpers used inside of Engines now work properly.
|
||||||
|
|
||||||
|
* Serializers now can filter attributes with `only` and `except`:
|
||||||
|
|
||||||
|
```
|
||||||
|
UserSerializer.new(user, only: [:first_name, :last_name])
|
||||||
|
UserSerializer.new(user, except: :first_name)
|
||||||
|
```
|
||||||
|
|
||||||
|
* Basic Mongoid support. We now include our mixins in the right place.
|
||||||
|
|
||||||
|
* On Ruby 1.8, we now generate an `id` method that properly serializes `id`
|
||||||
|
columns. See issue #127 for more.
|
||||||
|
|
||||||
|
* Add an alias for `scope` method to be the name of the context. By default
|
||||||
|
this is `current_user`. The name is automatically set when using
|
||||||
|
`serialization_scope` in the controller.
|
||||||
|
|
||||||
|
* Pass through serialization options (such as `:include`) when a model
|
||||||
|
has no serializer defined.
|
||||||
|
|
||||||
|
## [0.7.0 (March 6, 2013)](https://github.com/rails-api/active_model_serializers/commit/fabdc621ff97fbeca317f6301973dd4564b9e695)
|
||||||
|
|
||||||
|
* ```embed_key``` option to allow embedding by attributes other than IDs
|
||||||
|
* Fix rendering nil with custom serializer
|
||||||
|
* Fix global ```self.root = false```
|
||||||
|
* Add support for specifying the serializer for an association as a String
|
||||||
|
* Able to specify keys on the attributes method
|
||||||
|
* Serializer Reloading via ActiveSupport::DescendantsTracker
|
||||||
|
* Reduce double map to once; Fixes datamapper eager loading.
|
||||||
|
|
||||||
|
## 0.6.0 (October 22, 2012)
|
||||||
|
|
||||||
|
* Serialize sets properly
|
||||||
|
* Add root option to ArraySerializer
|
||||||
|
* Support polymorphic associations
|
||||||
|
* Support :each_serializer in ArraySerializer
|
||||||
|
* Add `scope` method to easily access the scope in the serializer
|
||||||
|
* Fix regression with Rails 3.2.6; add Rails 4 support
|
||||||
|
* Allow serialization_scope to be disabled with serialization_scope nil
|
||||||
|
* Array serializer should support pure ruby objects besides serializers
|
||||||
|
|
||||||
|
## 0.05.x
|
||||||
|
|
||||||
|
### [0.5.2 (June 5, 2012)](https://github.com/rails-api/active_model_serializers/commit/615afd125c260432d456dc8be845867cf87ea118#diff-0c5c12f311d3b54734fff06069efd2ac)
|
||||||
|
|
||||||
|
### [0.5.1 (May 23, 2012)](https://github.com/rails-api/active_model_serializers/commit/00194ec0e41831802fcbf893a34c0bb0853ebe14#diff-0c5c12f311d3b54734fff06069efd2ac)
|
||||||
|
|
||||||
|
### [0.5.0 (May 16, 2012)](https://github.com/rails-api/active_model_serializers/commit/33d4842dcd35c7167b0b33fc0abcf00fb2c92286)
|
||||||
|
|
||||||
|
* First tagged version
|
||||||
|
* Changes generators to always generate an ApplicationSerializer
|
||||||
|
|
||||||
|
## [0.1.0 (December 21, 2011)](https://github.com/rails-api/active_model_serializers/commit/1e0c9ef93b96c640381575dcd30be07ac946818b)
|
||||||
|
|
||||||
|
## First Commit as [Rails Serializers 0.0.1](https://github.com/rails-api/active_model_serializers/commit/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e)
|
||||||
|
(December 1, 2011).
|
||||||
74
CHANGELOG-0-09.md
Normal file
74
CHANGELOG-0-09.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
## 0.09.x
|
||||||
|
|
||||||
|
### v0.9.3 (2015/01/21 20:29 +00:00)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- [#774](https://github.com/rails-api/active_model_serializers/pull/774) Fix nested include attributes (@nhocki)
|
||||||
|
- [#771](https://github.com/rails-api/active_model_serializers/pull/771) Make linked resource type names consistent with root names (@sweatypitts)
|
||||||
|
- [#696](https://github.com/rails-api/active_model_serializers/pull/696) Explicitly set serializer for associations (@ggordon)
|
||||||
|
- [#700](https://github.com/rails-api/active_model_serializers/pull/700) sparse fieldsets (@arenoir)
|
||||||
|
- [#768](https://github.com/rails-api/active_model_serializers/pull/768) Adds support for `meta` and `meta_key` attribute (@kurko)
|
||||||
|
|
||||||
|
### v0.9.1 (2014/12/04 11:54 +00:00)
|
||||||
|
- [#707](https://github.com/rails-api/active_model_serializers/pull/707) A Friendly Note on Which AMS Version to Use (@jherdman)
|
||||||
|
- [#730](https://github.com/rails-api/active_model_serializers/pull/730) Fixes nested has_many links in JSONAPI (@kurko)
|
||||||
|
- [#718](https://github.com/rails-api/active_model_serializers/pull/718) Allow overriding the adapter with render option (@ggordon)
|
||||||
|
- [#720](https://github.com/rails-api/active_model_serializers/pull/720) Rename attribute with :key (0.8.x compatibility) (@ggordon)
|
||||||
|
- [#728](https://github.com/rails-api/active_model_serializers/pull/728) Use type as key for linked resources (@kurko)
|
||||||
|
- [#729](https://github.com/rails-api/active_model_serializers/pull/729) Use the new beta build env on Travis (@joshk)
|
||||||
|
- [#703](https://github.com/rails-api/active_model_serializers/pull/703) Support serializer and each_serializer options in renderer (@ggordon, @mieko)
|
||||||
|
- [#727](https://github.com/rails-api/active_model_serializers/pull/727) Includes links inside of linked resources (@kurko)
|
||||||
|
- [#726](https://github.com/rails-api/active_model_serializers/pull/726) Bugfix: include nested has_many associations (@kurko)
|
||||||
|
- [#722](https://github.com/rails-api/active_model_serializers/pull/722) Fix infinite recursion (@ggordon)
|
||||||
|
- [#1](https://github.com/rails-api/active_model_serializers/pull/1) Allow for the implicit use of ArraySerializer when :each_serializer is specified (@mieko)
|
||||||
|
- [#692](https://github.com/rails-api/active_model_serializers/pull/692) Include 'linked' member for json-api collections (@ggordon)
|
||||||
|
- [#714](https://github.com/rails-api/active_model_serializers/pull/714) Define as_json instead of to_json (@guilleiguaran)
|
||||||
|
- [#710](https://github.com/rails-api/active_model_serializers/pull/710) JSON-API: Don't include linked section if associations are empty (@guilleiguaran)
|
||||||
|
- [#711](https://github.com/rails-api/active_model_serializers/pull/711) Fixes rbx gems bundling on TravisCI (@kurko)
|
||||||
|
- [#709](https://github.com/rails-api/active_model_serializers/pull/709) Add type key when association name is different than object type (@guilleiguaran)
|
||||||
|
- [#708](https://github.com/rails-api/active_model_serializers/pull/708) Handle correctly null associations (@guilleiguaran)
|
||||||
|
- [#691](https://github.com/rails-api/active_model_serializers/pull/691) Fix embed option for associations (@jacob-s-son)
|
||||||
|
- [#689](https://github.com/rails-api/active_model_serializers/pull/689) Fix support for custom root in JSON-API adapter (@guilleiguaran)
|
||||||
|
- [#685](https://github.com/rails-api/active_model_serializers/pull/685) Serialize ids as strings in JSON-API adapter (@guilleiguaran)
|
||||||
|
- [#684](https://github.com/rails-api/active_model_serializers/pull/684) Refactor adapters to implement support for array serialization (@guilleiguaran)
|
||||||
|
- [#682](https://github.com/rails-api/active_model_serializers/pull/682) Include root by default in JSON-API serializers (@guilleiguaran)
|
||||||
|
- [#625](https://github.com/rails-api/active_model_serializers/pull/625) Add DSL for urls (@JordanFaust)
|
||||||
|
- [#677](https://github.com/rails-api/active_model_serializers/pull/677) Add support for embed: :ids option for in associations (@guilleiguaran)
|
||||||
|
- [#681](https://github.com/rails-api/active_model_serializers/pull/681) Check superclasses for Serializers (@quainjn)
|
||||||
|
- [#680](https://github.com/rails-api/active_model_serializers/pull/680) Add support for root keys (@NullVoxPopuli)
|
||||||
|
- [#675](https://github.com/rails-api/active_model_serializers/pull/675) Support Rails 4.2.0 (@tricknotes)
|
||||||
|
- [#667](https://github.com/rails-api/active_model_serializers/pull/667) Require only activemodel instead of full rails (@guilleiguaran)
|
||||||
|
- [#653](https://github.com/rails-api/active_model_serializers/pull/653) Add "_test" suffix to JsonApi::HasManyTest filename. (@alexgenco)
|
||||||
|
- [#631](https://github.com/rails-api/active_model_serializers/pull/631) Update build badge URL (@craiglittle)
|
||||||
|
|
||||||
|
### 0.9.0.alpha1 - January 7, 2014
|
||||||
|
|
||||||
|
### 0.9.0.pre
|
||||||
|
|
||||||
|
* The following methods were removed
|
||||||
|
- Model#active\_model\_serializer
|
||||||
|
- Serializer#include!
|
||||||
|
- Serializer#include?
|
||||||
|
- Serializer#attr\_disabled=
|
||||||
|
- Serializer#cache
|
||||||
|
- Serializer#perform\_caching
|
||||||
|
- Serializer#schema (needs more discussion)
|
||||||
|
- Serializer#attribute
|
||||||
|
- Serializer#include\_#{name}? (filter method added)
|
||||||
|
- Serializer#attributes (took a hash)
|
||||||
|
|
||||||
|
* The following things were added
|
||||||
|
- Serializer#filter method
|
||||||
|
- CONFIG object
|
||||||
|
|
||||||
|
* Remove support for ruby 1.8 versions.
|
||||||
|
|
||||||
|
* Require rails >= 3.2.
|
||||||
|
|
||||||
|
* Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces.
|
||||||
|
|
||||||
|
* Added a "prefix" option in case you want to use a different version of serializer.
|
||||||
|
|
||||||
|
* Serializers default namespace can be set in `default_serializer_options` and inherited by associations.
|
||||||
|
|
||||||
|
* [Beginning of rewrite: c65d387705ec534db171712671ba7fcda4f49f68](https://github.com/rails-api/active_model_serializers/commit/c65d387705ec534db171712671ba7fcda4f49f68)
|
||||||
466
CHANGELOG-0-10.md
Normal file
466
CHANGELOG-0-10.md
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
## 0.10.x
|
||||||
|
|
||||||
|
### [0-10-stable (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.6...0-10-stable)
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
|
||||||
|
### [v0.10.6 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.5...v0.10.6)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- [#1857](https://github.com/rails-api/active_model_serializers/pull/1857) JSON:API does not load belongs_to relation to get identifier id. (@bf4)
|
||||||
|
- [#2119](https://github.com/rails-api/active_model_serializers/pull/2119) JSON:API returns null resource object identifier when 'id' is null. (@bf4)
|
||||||
|
- [#2093](https://github.com/rails-api/active_model_serializers/pull/2093) undef problematic Serializer methods: display, select. (@bf4)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
|
||||||
|
- [#2104](https://github.com/rails-api/active_model_serializers/pull/2104) Documentation for serializers and rendering. (@cassidycodes)
|
||||||
|
- [#2081](https://github.com/rails-api/active_model_serializers/pull/2081) Documentation for `include` option in adapters. (@charlie-wasp)
|
||||||
|
- [#2120](https://github.com/rails-api/active_model_serializers/pull/2120) Documentation for association options: foreign_key, type, class_name, namespace. (@bf4)
|
||||||
|
|
||||||
|
### [v0.10.5 (2017-03-07)](https://github.com/rails-api/active_model_serializers/compare/v0.10.4...v0.10.5)
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- [#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)
|
||||||
|
- [#2057](https://github.com/rails-api/active_model_serializers/pull/2057)
|
||||||
|
Update version constraint for jsonapi-renderer to `['>= 0.1.1.beta1', '< 0.2']`
|
||||||
|
(@jaredbeck)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
|
||||||
|
- [#2055](https://github.com/rails-api/active_model_serializers/pull/2055)
|
||||||
|
Replace deprecated dependency jsonapi with jsonapi-renderer. (@jaredbeck)
|
||||||
|
- [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) Make test attributes explicit. Tests have Model#associations. (@bf4)
|
||||||
|
- [#1981](https://github.com/rails-api/active_model_serializers/pull/1981) Fix relationship link documentation. (@groyoh)
|
||||||
|
- [#2035](https://github.com/rails-api/active_model_serializers/pull/2035) Document how to disable the logger. (@MSathieu)
|
||||||
|
- [#2039](https://github.com/rails-api/active_model_serializers/pull/2039) Documentation fixes. (@biow0lf)
|
||||||
|
|
||||||
|
### [v0.10.4 (2017-01-06)](https://github.com/rails-api/active_model_serializers/compare/v0.10.3...v0.10.4)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
|
||||||
|
- [#2005](https://github.com/rails-api/active_model_serializers/pull/2005) Update jsonapi runtime dependency to 0.1.1.beta6, support Ruby 2.4. (@kofronpi)
|
||||||
|
- [#1993](https://github.com/rails-api/active_model_serializers/pull/1993) Swap out KeyTransform for CaseTransform gem for the possibility of native extension use. (@NullVoxPopuli)
|
||||||
|
|
||||||
|
### [v0.10.3 (2016-11-21)](https://github.com/rails-api/active_model_serializers/compare/v0.10.2...v0.10.3)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- [#1973](https://github.com/rails-api/active_model_serializers/pull/1973) Fix namespace lookup for collections and has_many relationships (@groyoh)
|
||||||
|
- [#1887](https://github.com/rails-api/active_model_serializers/pull/1887) Make the comment reflect what the function does (@johnnymo87)
|
||||||
|
- [#1890](https://github.com/rails-api/active_model_serializers/issues/1890) Ensure generator inherits from ApplicationSerializer when available (@richmolj)
|
||||||
|
- [#1922](https://github.com/rails-api/active_model_serializers/pull/1922) Make railtie an optional dependency in runtime (@ggpasqualino)
|
||||||
|
- [#1930](https://github.com/rails-api/active_model_serializers/pull/1930) Ensure valid jsonapi when relationship has no links or data (@richmolj)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- [#1757](https://github.com/rails-api/active_model_serializers/pull/1757) Make serializer lookup chain configurable. (@NullVoxPopuli)
|
||||||
|
- [#1968](https://github.com/rails-api/active_model_serializers/pull/1968) (@NullVoxPopuli)
|
||||||
|
- Add controller namespace to default controller lookup
|
||||||
|
- Provide a `namespace` render option
|
||||||
|
- document how set the namespace in the controller for implicit lookup.
|
||||||
|
- [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli)
|
||||||
|
- Added `jsonapi_namespace_separator` config option.
|
||||||
|
- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee)
|
||||||
|
- [#1917](https://github.com/rails-api/active_model_serializers/pull/1917) Add `jsonapi_pagination_links_enabled` configuration option (@richmolj)
|
||||||
|
- [#1797](https://github.com/rails-api/active_model_serializers/pull/1797) Only include 'relationships' when sideloading (@richmolj)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- [#1833](https://github.com/rails-api/active_model_serializers/pull/1833) Remove relationship links if they are null (@groyoh)
|
||||||
|
- [#1881](https://github.com/rails-api/active_model_serializers/pull/1881) ActiveModelSerializers::Model correctly works with string keys (@yevhene)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
- [#1767](https://github.com/rails-api/active_model_serializers/pull/1767) Replace raising/rescuing `CollectionSerializer::NoSerializerError`,
|
||||||
|
throw/catch `:no_serializer`. (@bf4)
|
||||||
|
- [#1839](https://github.com/rails-api/active_model_serializers/pull/1839) `fields` tests demonstrating usage for both attributes and relationships. (@NullVoxPopuli)
|
||||||
|
- [#1812](https://github.com/rails-api/active_model_serializers/pull/1812) add a code of conduct (@corainchicago)
|
||||||
|
|
||||||
|
- [#1878](https://github.com/rails-api/active_model_serializers/pull/1878) Cache key generation for serializers now uses `ActiveSupport::Cache.expand_cache_key` instead of `Array#join` by default and is also overridable. This change should be backward-compatible. (@markiz)
|
||||||
|
|
||||||
|
- [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@cassidycodes)
|
||||||
|
- [#1909](https://github.com/rails-api/active_model_serializers/pull/1909) Add documentation for relationship links. (@vasilakisfil, @NullVoxPopuli)
|
||||||
|
- [#1959](https://github.com/rails-api/active_model_serializers/pull/1959) Add documentation for root. (@shunsuke227ono)
|
||||||
|
- [#1967](https://github.com/rails-api/active_model_serializers/pull/1967) Improve type method documentation. (@yukideluxe)
|
||||||
|
|
||||||
|
### [v0.10.2 (2016-07-05)](https://github.com/rails-api/active_model_serializers/compare/v0.10.1...v0.10.2)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- [#1814](https://github.com/rails-api/active_model_serializers/pull/1814) Ensuring read_multi works with fragment cache
|
||||||
|
- [#1848](https://github.com/rails-api/active_model_serializers/pull/1848) Redefine associations on inherited serializers. (@EhsanYousefi)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
- [#1808](https://github.com/rails-api/active_model_serializers/pull/1808) Adds documentation for `fields` option. (@luizkowalski)
|
||||||
|
|
||||||
|
### [v0.10.1 (2016-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0...v0.10.1)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- [#1668](https://github.com/rails-api/active_model_serializers/pull/1668) Exclude nil and empty links. (@sigmike)
|
||||||
|
- [#1426](https://github.com/rails-api/active_model_serializers/pull/1426) Add ActiveModelSerializers.config.default_includes (@empact)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- [#1754](https://github.com/rails-api/active_model_serializers/pull/1754) Fixes #1759, Grape integration, improves serialization_context
|
||||||
|
missing error message on pagination. Document overriding CollectionSerializer#paginated?. (@bf4)
|
||||||
|
Moved serialization_context creation to Grape formatter, so resource serialization works without explicit calls to the `render` helper method.
|
||||||
|
Added Grape collection tests. (@onomated)
|
||||||
|
- [#1287](https://github.com/rails-api/active_model_serializers/pull/1287) Pass `fields` options from adapter to serializer. (@vasilakisfil)
|
||||||
|
- [#1710](https://github.com/rails-api/active_model_serializers/pull/1710) Prevent association loading when `include_data` option
|
||||||
|
is set to `false`. (@groyoh)
|
||||||
|
- [#1747](https://github.com/rails-api/active_model_serializers/pull/1747) Improve jsonapi mime type registration for Rails 5 (@remear)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
- [#1734](https://github.com/rails-api/active_model_serializers/pull/1734) Adds documentation for conditional attribute (@lambda2)
|
||||||
|
- [#1685](https://github.com/rails-api/active_model_serializers/pull/1685) Replace `IncludeTree` with `IncludeDirective` from the jsonapi gem.
|
||||||
|
|
||||||
|
### [v0.10.0 (2016-05-17)](https://github.com/rails-api/active_model_serializers/compare/4a2d9853ba7...v0.10.0)
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
- [#1662](https://github.com/rails-api/active_model_serializers/pull/1662) Drop support for Rails 4.0 and Ruby 2.0.0. (@remear)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- [#1677](https://github.com/rails-api/active_model_serializers/pull/1677) Add `assert_schema`, `assert_request_schema`, `assert_request_response_schema`. (@bf4)
|
||||||
|
- [#1697](https://github.com/rails-api/active_model_serializers/pull/1697) Include actual exception message with custom exceptions;
|
||||||
|
`Test::Schema` exceptions are now `Minitest::Assertion`s. (@bf4)
|
||||||
|
- [#1699](https://github.com/rails-api/active_model_serializers/pull/1699) String/Lambda support for conditional attributes/associations (@mtsmfm)
|
||||||
|
- [#1687](https://github.com/rails-api/active_model_serializers/pull/1687) Only calculate `_cache_digest` (in `cache_key`) when `skip_digest` is false. (@bf4)
|
||||||
|
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
|
||||||
|
to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader)
|
||||||
|
- [#1726](https://github.com/rails-api/active_model_serializers/pull/1726) Adds polymorphic option to association definition which includes association type/nesting in serializer (@cgmckeever)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
- [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre)
|
||||||
|
- [#1730](https://github.com/rails-api/active_model_serializers/pull/1730) Adds documentation for overriding default serializer based on conditions (@groyoh/@cgmckeever)
|
||||||
|
|
||||||
|
### [v0.10.0.rc5 (2016-04-04)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc4...v0.10.0.rc5)
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Changed :dashed key transform to :dash. (@remear)
|
||||||
|
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear)
|
||||||
|
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
|
||||||
|
take precedence over `serialization_scope` in the controller.
|
||||||
|
Fix tests that required tearing down dynamic methods. (@bf4)
|
||||||
|
- [#1644](https://github.com/rails-api/active_model_serializers/pull/1644) Include adapter name in cache key so
|
||||||
|
that the same serializer can be cached per adapter. (@bf4 via #1346 by @kevintyll)
|
||||||
|
- [#1642](https://github.com/rails-api/active_model_serializers/pull/1642) Prefer object.cache_key over the generated
|
||||||
|
cache key. (@bf4 via #1346 by @kevintyll)
|
||||||
|
- [#1637](https://github.com/rails-api/active_model_serializers/pull/1637) Make references to 'ActionController::Base.cache_store' explicit
|
||||||
|
in order to avoid issues when application controllers inherit from 'ActionController::API'. (@ncuesta)
|
||||||
|
- [#1633](https://github.com/rails-api/active_model_serializers/pull/1633) Yield 'serializer' to serializer association blocks. (@bf4)
|
||||||
|
- [#1616](https://github.com/rails-api/active_model_serializers/pull/1616) SerializableResource handles no serializer like controller. (@bf4)
|
||||||
|
- [#1618](https://github.com/rails-api/active_model_serializers/issues/1618) Get collection root key for
|
||||||
|
empty collection from explicit serializer option, when possible. (@bf4)
|
||||||
|
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Provide key translation. (@remear)
|
||||||
|
- [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe
|
||||||
|
(using the Attributes adapter by default). (@bf4)
|
||||||
|
- [#1550](https://github.com/rails-api/active_model_serializers/pull/1550) Add
|
||||||
|
Rails url_helpers to `SerializationContext` for use in links. (@remear, @bf4)
|
||||||
|
- [#1004](https://github.com/rails-api/active_model_serializers/pull/1004) JSON API errors object implementation.
|
||||||
|
- Only implements `detail` and `source` as derived from `ActiveModel::Error`
|
||||||
|
- Provides checklist of remaining questions and remaining parts of the spec.
|
||||||
|
- [#1515](https://github.com/rails-api/active_model_serializers/pull/1515) Adds support for symbols to the
|
||||||
|
`ActiveModel::Serializer.type` method. (@groyoh)
|
||||||
|
- [#1504](https://github.com/rails-api/active_model_serializers/pull/1504) Adds the changes missing from #1454
|
||||||
|
and add more tests for resource identifier and relationship objects. Fix association block with link
|
||||||
|
returning `data: nil`.(@groyoh)
|
||||||
|
- [#1372](https://github.com/rails-api/active_model_serializers/pull/1372) Support
|
||||||
|
cache_store.read_multi. (@LcpMarvel)
|
||||||
|
- [#1018](https://github.com/rails-api/active_model_serializers/pull/1018) Add more tests and docs for top-level links. (@leandrocp)
|
||||||
|
- [#1454](https://github.com/rails-api/active_model_serializers/pull/1454) Add support for
|
||||||
|
relationship-level links and meta attributes. (@beauby)
|
||||||
|
- [#1340](https://github.com/rails-api/active_model_serializers/pull/1340) Add support for resource-level meta. (@beauby)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- [#1657](https://github.com/rails-api/active_model_serializers/pull/1657) Add missing missing require "active_support/json". (@andreaseger)
|
||||||
|
- [#1661](https://github.com/rails-api/active_model_serializers/pull/1661) Fixes `read_attribute_for_serialization` not
|
||||||
|
seeing methods defined in serialization superclass (#1653, #1658, #1660), introduced in #1650. (@bf4)
|
||||||
|
- [#1651](https://github.com/rails-api/active_model_serializers/pull/1651) Fix deserialization of nil relationships. (@NullVoxPopuli)
|
||||||
|
- [#1480](https://github.com/rails-api/active_model_serializers/pull/1480) Fix setting of cache_store from Rails configuration. (@bf4)
|
||||||
|
Fix unintentional mutating of value in memory cache store. (@groyoh)
|
||||||
|
- [#1622](https://github.com/rails-api/active_model_serializers/pull/1622) Fragment cache changed from per-record to per-serializer.
|
||||||
|
Now, two serializers that use the same model may be separately cached. (@lserman)
|
||||||
|
- [#1478](https://github.com/rails-api/active_model_serializers/pull/1478) Cache store will now be correctly set when serializers are
|
||||||
|
loaded *before* Rails initializes. (@bf4)
|
||||||
|
- [#1570](https://github.com/rails-api/active_model_serializers/pull/1570) Fixed pagination issue with last page size. (@bmorrall)
|
||||||
|
- [#1516](https://github.com/rails-api/active_model_serializers/pull/1516) No longer return a nil href when only
|
||||||
|
adding meta to a relationship link. (@groyoh)
|
||||||
|
- [#1458](https://github.com/rails-api/active_model_serializers/pull/1458) Preserve the serializer
|
||||||
|
type when fragment caching. (@bdmac)
|
||||||
|
- [#1477](https://github.com/rails-api/active_model_serializers/pull/1477) Fix `fragment_cached?`
|
||||||
|
method to check if caching. (@bdmac)
|
||||||
|
- [#1501](https://github.com/rails-api/active_model_serializers/pull/1501) Adds tests for SerializableResource::use_adapter?,doc typos (@domitian)
|
||||||
|
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
- [#1608](https://github.com/rails-api/active_model_serializers/pull/1608) Move SerializableResource to ActiveModelSerializers (@groyoh)
|
||||||
|
- [#1602](https://github.com/rails-api/active_model_serializers/pull/1602) Add output examples to Adapters docs (@remear)
|
||||||
|
- [#1557](https://github.com/rails-api/active_model_serializers/pull/1557) Update docs regarding overriding the root key (@Jwan622)
|
||||||
|
- [#1471](https://github.com/rails-api/active_model_serializers/pull/1471) [Cleanup] Serializer caching is its own concern. (@bf4)
|
||||||
|
- [#1482](https://github.com/rails-api/active_model_serializers/pull/1482) Document JSON API implementation defs and progress in class. (@bf4)
|
||||||
|
- [#1551](https://github.com/rails-api/active_model_serializers/pull/1551) Added codebeat badge (@korzonek)
|
||||||
|
- [#1527](https://github.com/rails-api/active_model_serializers/pull/1527) Refactor fragment cache class. (@groyoh)
|
||||||
|
- [#1560](https://github.com/rails-api/active_model_serializers/pull/1560) Update rubocop and address its warnings. (@bf4 @groyoh)
|
||||||
|
- [#1545](https://github.com/rails-api/active_model_serializers/pull/1545) Document how to pass arbitrary options to the
|
||||||
|
serializer (@CodedBeardedSignedTaylor)
|
||||||
|
- [#1496](https://github.com/rails-api/active_model_serializers/pull/1496) Run all branches against JRuby on CI (@nadavshatz)
|
||||||
|
- [#1559](https://github.com/rails-api/active_model_serializers/pull/1559) Add a deprecation DSL. (@bf4 @groyoh)
|
||||||
|
- [#1543](https://github.com/rails-api/active_model_serializers/pull/1543) Add the changes missing from #1535. (@groyoh)
|
||||||
|
- [#1535](https://github.com/rails-api/active_model_serializers/pull/1535) Move the adapter and adapter folder to
|
||||||
|
active_model_serializers folder and changes the module namespace. (@domitian @bf4)
|
||||||
|
- [#1497](https://github.com/rails-api/active_model_serializers/pull/1497) Add JRuby-9000 to appveyor.yml(@corainchicago)
|
||||||
|
- [#1420](https://github.com/rails-api/active_model_serializers/pull/1420) Adds tests and documentation for polymorphism(@marcgarreau)
|
||||||
|
|
||||||
|
|
||||||
|
### [v0.10.0.rc4 (2016-01-27)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc3...v0.10.0.rc4)
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
- [#1360](https://github.com/rails-api/active_model_serializers/pull/1360)
|
||||||
|
[#1369](https://github.com/rails-api/active_model_serializers/pull/1369) Drop support for Ruby 1.9.3 (@karaAJC, @maurogeorge)
|
||||||
|
- [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Remove Serializer#root_name (@beauby)
|
||||||
|
- [#1138](https://github.com/rails-api/active_model_serializers/pull/1138) Introduce Adapter::Base (@bf4)
|
||||||
|
* Adapters now inherit Adapter::Base. 'Adapter' is now a module, no longer a class.
|
||||||
|
* using a class as a namespace that you also inherit from is complicated and circular at times i.e.
|
||||||
|
buggy (see https://github.com/rails-api/active_model_serializers/pull/1177)
|
||||||
|
* The class methods on Adapter aren't necessarily related to the instance methods, they're more
|
||||||
|
Adapter functions.
|
||||||
|
* named `Base` because it's a Rails-ism.
|
||||||
|
* It helps to isolate and highlight what the Adapter interface actually is.
|
||||||
|
- [#1418](https://github.com/rails-api/active_model_serializers/pull/1418)
|
||||||
|
serialized collections now use the root option as is; now, only the
|
||||||
|
root derived from the serializer or object is always pluralized.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- [#1406](https://github.com/rails-api/active_model_serializers/pull/1406) Allow for custom dynamic values in JSON API links (@beauby)
|
||||||
|
- [#1270](https://github.com/rails-api/active_model_serializers/pull/1270) Adds `assert_response_schema` test helper (@maurogeorge)
|
||||||
|
- [#1099](https://github.com/rails-api/active_model_serializers/pull/1099) Adds `assert_serializer` test helper (@maurogeorge)
|
||||||
|
- [#1403](https://github.com/rails-api/active_model_serializers/pull/1403) Add support for if/unless on attributes/associations (@beauby)
|
||||||
|
- [#1248](https://github.com/rails-api/active_model_serializers/pull/1248) Experimental: Add support for JSON API deserialization (@beauby)
|
||||||
|
- [#1378](https://github.com/rails-api/active_model_serializers/pull/1378) Change association blocks
|
||||||
|
to be evaluated in *serializer* scope, rather than *association* scope. (@bf4)
|
||||||
|
* Syntax changes from e.g.
|
||||||
|
`has_many :titles do customers.pluck(:title) end` (in #1356) to
|
||||||
|
`has_many :titles do object.customers.pluck(:title) end`
|
||||||
|
- [#1356](https://github.com/rails-api/active_model_serializers/pull/1356) Add inline syntax for
|
||||||
|
attributes and associations (@bf4 @beauby @noahsilas)
|
||||||
|
* Allows defining attributes so that they don't conflict with existing methods. e.g. `attribute
|
||||||
|
:title do 'Mr. Topum Hat' end`
|
||||||
|
* Allows defining associations so that they don't conflict with existing methods. e.g. `has_many
|
||||||
|
:titles do customers.pluck(:title) end`
|
||||||
|
* Allows dynamic associations, as compared to compare to using
|
||||||
|
[`virtual_value`](https://github.com/rails-api/active_model_serializers/pull/1356#discussion_r47146466).
|
||||||
|
e.g. `has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]`
|
||||||
|
* Removes dynamically defined methods on the serializer
|
||||||
|
- [#1336](https://github.com/rails-api/active_model_serializers/pull/1336) Added support for Grape >= 0.13, < 1.0 (@johnhamelink)
|
||||||
|
- [#1322](https://github.com/rails-api/active_model_serializers/pull/1322) Instrumenting rendering of resources (@bf4, @maurogeorge)
|
||||||
|
- [#1291](https://github.com/rails-api/active_model_serializers/pull/1291) Add logging (@maurogeorge)
|
||||||
|
- [#1272](https://github.com/rails-api/active_model_serializers/pull/1272) Add PORO serializable base class: ActiveModelSerializers::Model (@bf4)
|
||||||
|
- [#1255](https://github.com/rails-api/active_model_serializers/pull/1255) Make more class attributes inheritable (@bf4)
|
||||||
|
- [#1249](https://github.com/rails-api/active_model_serializers/pull/1249) Inheritance of serializer inheriting the cache configuration(@Rodrigora)
|
||||||
|
- [#1247](https://github.com/rails-api/active_model_serializers/pull/1247) Add support for toplevel JSON API links (@beauby)
|
||||||
|
- [#1246](https://github.com/rails-api/active_model_serializers/pull/1246) Add support for resource-level JSON API links (@beauby)
|
||||||
|
- [#1225](https://github.com/rails-api/active_model_serializers/pull/1225) Better serializer lookup, use nested serializer when it exists (@beauby)
|
||||||
|
- [#1213](https://github.com/rails-api/active_model_serializers/pull/1213) `type` directive for serializer to control type field with json-api adapter (@youroff)
|
||||||
|
- [#1172](https://github.com/rails-api/active_model_serializers/pull/1172) Better serializer registration, get more than just the first module (@bf4)
|
||||||
|
- [#1158](https://github.com/rails-api/active_model_serializers/pull/1158) Add support for wildcards in `include` option (@beauby)
|
||||||
|
- [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested
|
||||||
|
associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby).
|
||||||
|
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
|
||||||
|
- [#1251](https://github.com/rails-api/active_model_serializers/pull/1251) Rename ArraySerializer to
|
||||||
|
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
|
||||||
|
- [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that,
|
||||||
|
when disabled, requires serializers to explicitly specified. (@trek)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- [#1352](https://github.com/rails-api/active_model_serializers/pull/1352) Fix generators; Isolate Rails-specifc code in Railties (@dgynn, @bf4)
|
||||||
|
- [#1384](https://github.com/rails-api/active_model_serializers/pull/1384)Fix database state leaking across tests (@bf4)
|
||||||
|
- [#1297](https://github.com/rails-api/active_model_serializers/pull/1297) Fix `fields` option to restrict relationships as well (@beauby)
|
||||||
|
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)
|
||||||
|
- [#1214](https://github.com/rails-api/active_model_serializers/pull/1214) retrieve the key from the reflection options when building associations (@NullVoxPopuli, @hut8)
|
||||||
|
- [#1358](https://github.com/rails-api/active_model_serializers/pull/1358) Handle serializer file paths with spaces (@rwstauner, @bf4)
|
||||||
|
- [#1195](https://github.com/rails-api/active_model_serializers/pull/1195) Fix id override (@beauby)
|
||||||
|
- [#1185](https://github.com/rails-api/active_model_serializers/pull/1185) Fix options passing in Json and Attributes adapters (@beauby)
|
||||||
|
|
||||||
|
Misc:
|
||||||
|
|
||||||
|
- [#1383](https://github.com/rails-api/active_model_serializers/pull/1383) Simplify reflections handling (@beauby)
|
||||||
|
- [#1370](https://github.com/rails-api/active_model_serializers/pull/1370) Simplify attributes handling via a mixin (@beauby)
|
||||||
|
- [#1301](https://github.com/rails-api/active_model_serializers/pull/1301) Mapping JSON API spec / schema to AMS (@bf4)
|
||||||
|
- [#1271](https://github.com/rails-api/active_model_serializers/pull/1271) Handle no serializer source file to digest (@bf4)
|
||||||
|
- [#1260](https://github.com/rails-api/active_model_serializers/pull/1260) Serialization and Cache Documentation (@bf4)
|
||||||
|
- [#1259](https://github.com/rails-api/active_model_serializers/pull/1259) Add more info to CONTRIBUTING (@bf4)
|
||||||
|
- [#1233](https://github.com/rails-api/active_model_serializers/pull/1233) Top-level meta and meta_key options no longer handled at serializer level (@beauby)
|
||||||
|
- [#1232](https://github.com/rails-api/active_model_serializers/pull/1232) fields option no longer handled at serializer level (@beauby)
|
||||||
|
- [#1220](https://github.com/rails-api/active_model_serializers/pull/1220) Remove empty rubocop.rake (@maurogeorge)
|
||||||
|
- [#1178](https://github.com/rails-api/active_model_serializers/pull/1178) env CAPTURE_STDERR=false lets devs see hard failures (@bf4)
|
||||||
|
- [#1177](https://github.com/rails-api/active_model_serializers/pull/1177) Remove Adapter autoloads in favor of require (@bf4)
|
||||||
|
- [#1117](https://github.com/rails-api/active_model_serializers/pull/1117) FlattenJson adapter no longer inherits Json adapter, renamed to Attributes (@bf4)
|
||||||
|
- [#1171](https://github.com/rails-api/active_model_serializers/pull/1171) add require statements to top of file (@shicholas)
|
||||||
|
- [#1167](https://github.com/rails-api/active_model_serializers/pull/1167) Delegate Serializer.attributes to Serializer.attribute (@bf4)
|
||||||
|
- [#1174](https://github.com/rails-api/active_model_serializers/pull/1174) Consistently refer to the 'JSON API' and the 'JsonApi' adapter (@bf4)
|
||||||
|
- [#1173](https://github.com/rails-api/active_model_serializers/pull/1173) Comment private accessor warnings (@bf4)
|
||||||
|
- [#1166](https://github.com/rails-api/active_model_serializers/pull/1166) Prefer methods over instance variables (@bf4)
|
||||||
|
- [#1168](https://github.com/rails-api/active_model_serializers/pull/1168) Fix appveyor failure cache not being expired (@bf4)
|
||||||
|
- [#1161](https://github.com/rails-api/active_model_serializers/pull/1161) Remove duplicate test helper (@bf4)
|
||||||
|
- [#1360](https://github.com/rails-api/active_model_serializers/pull/1360) Update CI to test 2.2.2 -> 2.2.3 (@karaAJC)
|
||||||
|
- [#1371](https://github.com/rails-api/active_model_serializers/pull/1371) Refactor, update, create documentation (@bf4)
|
||||||
|
|
||||||
|
### [v0.10.0.rc3 (2015-09-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc2...v0.10.0.rc3)
|
||||||
|
- [#1129](https://github.com/rails-api/active_model_serializers/pull/1129) Remove SerializableResource.serialize in favor of `.new` (@bf4)
|
||||||
|
- [#1155](https://github.com/rails-api/active_model_serializers/pull/1155) Outside controller use tutorial (@CodedBeardedSignedTaylor)
|
||||||
|
- [#1154](https://github.com/rails-api/active_model_serializers/pull/1154) Rubocop fixes for issues introduced by #1089 (@NullVoxPopuli)
|
||||||
|
- [#1089](https://github.com/rails-api/active_model_serializers/pull/1089) Add ActiveModelSerializers.logger with default null device (@bf4)
|
||||||
|
- [#1109](https://github.com/rails-api/active_model_serializers/pull/1109) Make better use of Minitest's lifecycle (@bf4)
|
||||||
|
- [#1144](https://github.com/rails-api/active_model_serializers/pull/1144) Fix Markdown to adapters documentation (@bacarini)
|
||||||
|
- [#1121](https://github.com/rails-api/active_model_serializers/pull/1121) Refactor `add_links` in JSONAPI adapter. (@beauby)
|
||||||
|
- [#1150](https://github.com/rails-api/active_model_serializers/pull/1150) Remove legacy method accidentally reintroduced in #1017 (@beauby)
|
||||||
|
- [#1149](https://github.com/rails-api/active_model_serializers/pull/1149) Update README with nested included association example. (@mattmueller)
|
||||||
|
- [#1110](https://github.com/rails-api/active_model_serializers/pull/1110) Add lint tests for AR models (@beauby)
|
||||||
|
- [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Extended format for JSONAPI `include` option (@beauby)
|
||||||
|
* adds extended format for `include` option to JsonApi adapter
|
||||||
|
- [#1142](https://github.com/rails-api/active_model_serializers/pull/1142) Updating wording on cache expiry in README (@leighhalliday)
|
||||||
|
- [#1140](https://github.com/rails-api/active_model_serializers/pull/1140) Fix typo in fieldset exception (@lautis)
|
||||||
|
- [#1132](https://github.com/rails-api/active_model_serializers/pull/1132) Get rid of unnecessary instance variables, and implied dependencies. (@beauby)
|
||||||
|
- [#1139](https://github.com/rails-api/active_model_serializers/pull/1139) Documentation for serializing resources without render (@PericlesTheo)
|
||||||
|
- [#1017](https://github.com/rails-api/active_model_serializers/pull/1017) Make Adapters registerable so they are not namespace-constrained (@bf4)
|
||||||
|
- [#1120](https://github.com/rails-api/active_model_serializers/pull/1120) Add windows platform to loading sqlite3 (@Eric-Guo)
|
||||||
|
- [#1123](https://github.com/rails-api/active_model_serializers/pull/1123) Remove url options (@bacarini)
|
||||||
|
- [#1093](https://github.com/rails-api/active_model_serializers/pull/1093) Factor `with_adapter` + force cache clear before each test. (@beauby)
|
||||||
|
- [#1095](https://github.com/rails-api/active_model_serializers/pull/1095) Add documentation about configuration options. (@beauby)
|
||||||
|
- [#1069](https://github.com/rails-api/active_model_serializers/pull/1069) Add test coverage; account for no artifacts on CI (@bf4)
|
||||||
|
- [#1103](https://github.com/rails-api/active_model_serializers/pull/1103) Move `id` and `json_api_type` methods from `Serializer` to `JsonApi`. (@beauby)
|
||||||
|
- [#1106](https://github.com/rails-api/active_model_serializers/pull/1106) Add Style enforcer (via Rubocop) (@bf4)
|
||||||
|
- [#1079](https://github.com/rails-api/active_model_serializers/pull/1079) Add ArraySerializer#object like Serializer (@bf4)
|
||||||
|
- [#1096](https://github.com/rails-api/active_model_serializers/pull/1096) Fix definition of serializer attributes with multiple calls to `attri… (@beauby)
|
||||||
|
- [#1105](https://github.com/rails-api/active_model_serializers/pull/1105) Add ActiveRecord-backed fixtures. (@beauby)
|
||||||
|
- [#1108](https://github.com/rails-api/active_model_serializers/pull/1108) Better lint (@bf4)
|
||||||
|
- [#1102](https://github.com/rails-api/active_model_serializers/pull/1102) Remove remains of `embed` option. (@beauby)
|
||||||
|
- [#1090](https://github.com/rails-api/active_model_serializers/pull/1090) Clarify AMS dependencies (@bf4)
|
||||||
|
- [#1081](https://github.com/rails-api/active_model_serializers/pull/1081) Add configuration option to set resource type to singular/plural (@beauby)
|
||||||
|
- [#1067](https://github.com/rails-api/active_model_serializers/pull/1067) Fix warnings (@bf4)
|
||||||
|
- [#1066](https://github.com/rails-api/active_model_serializers/pull/1066) Adding appveyor to the project (@joaomdmoura, @Eric-Guo, @bf4)
|
||||||
|
- [#1071](https://github.com/rails-api/active_model_serializers/pull/1071) Make testing suite running and pass in Windows (@Eric-Guo, @bf4)
|
||||||
|
- [#1041](https://github.com/rails-api/active_model_serializers/pull/1041) Adding pagination links (@bacarini)
|
||||||
|
* adds support for `pagination links` at top level of JsonApi adapter
|
||||||
|
- [#1063](https://github.com/rails-api/active_model_serializers/pull/1063) Lead by example: lint PORO model (@bf4)
|
||||||
|
- [#1](https://github.com/rails-api/active_model_serializers/pull/1) Test caller line parsing and digesting (@bf4)
|
||||||
|
- [#1048](https://github.com/rails-api/active_model_serializers/pull/1048) Let FlattenJson adapter decide it doesn't include meta (@bf4)
|
||||||
|
- [#1060](https://github.com/rails-api/active_model_serializers/pull/1060) Update fragment cache to support namespaced objects (@aaronlerch)
|
||||||
|
- [#1052](https://github.com/rails-api/active_model_serializers/pull/1052) Use underscored json_root when serializing a collection (@whatthewhat)
|
||||||
|
- [#1051](https://github.com/rails-api/active_model_serializers/pull/1051) Fix some invalid JSON in docs (@tjschuck)
|
||||||
|
- [#1049](https://github.com/rails-api/active_model_serializers/pull/1049) Fix incorrect s/options = {}/options ||= {} (@bf4)
|
||||||
|
- [#1037](https://github.com/rails-api/active_model_serializers/pull/1037) allow for type attribute (@lanej)
|
||||||
|
- [#1034](https://github.com/rails-api/active_model_serializers/pull/1034) allow id attribute to be overriden (@lanej)
|
||||||
|
- [#1035](https://github.com/rails-api/active_model_serializers/pull/1035) Fixed Comments highlight (@artLopez)
|
||||||
|
- [#1031](https://github.com/rails-api/active_model_serializers/pull/1031) Disallow to define multiple associations at once (@bolshakov)
|
||||||
|
- [#1032](https://github.com/rails-api/active_model_serializers/pull/1032) Wrap railtie requirement with rescue (@elliotlarson)
|
||||||
|
- [#1026](https://github.com/rails-api/active_model_serializers/pull/1026) Bump Version Number to 0.10.0.rc2 (@jfelchner)
|
||||||
|
- [#985](https://github.com/rails-api/active_model_serializers/pull/985) Associations implementation refactoring (@bolshakov)
|
||||||
|
- [#954](https://github.com/rails-api/active_model_serializers/pull/954) Encapsulate serialization in ActiveModel::SerializableResource (@bf4)
|
||||||
|
- [#972](https://github.com/rails-api/active_model_serializers/pull/972) Capture app warnings on test run (@bf4)
|
||||||
|
- [#1019](https://github.com/rails-api/active_model_serializers/pull/1019) Improve README.md (@baojjeu)
|
||||||
|
- [#998](https://github.com/rails-api/active_model_serializers/pull/998) Changing root to model class name (@joaomdmoura)
|
||||||
|
- [#1006](https://github.com/rails-api/active_model_serializers/pull/1006) Fix adapter inflection bug for api -> API (@bf4)
|
||||||
|
- [#1016](https://github.com/rails-api/active_model_serializers/pull/1016) require rails/railtie before subclassing Rails::Railtie (@bf4)
|
||||||
|
- [#1013](https://github.com/rails-api/active_model_serializers/pull/1013) Root option with empty array support (@vyrak, @mareczek)
|
||||||
|
- [#994](https://github.com/rails-api/active_model_serializers/pull/994) Starting Docs structure (@joaomdmoura)
|
||||||
|
- [#1007](https://github.com/rails-api/active_model_serializers/pull/1007) Bug fix for ArraySerializer json_key (@jiajiawang)
|
||||||
|
- [#1003](https://github.com/rails-api/active_model_serializers/pull/1003) Fix transient test failures (@Rodrigora)
|
||||||
|
- [#996](https://github.com/rails-api/active_model_serializers/pull/996) Add linter for serializable resource (@bf4)
|
||||||
|
- [#990](https://github.com/rails-api/active_model_serializers/pull/990) Adding json-api meta test (@joaomdmoura)
|
||||||
|
- [#984](https://github.com/rails-api/active_model_serializers/pull/984) Add option "key" to serializer associations (@Rodrigora)
|
||||||
|
- [#982](https://github.com/rails-api/active_model_serializers/pull/982) Fix typo (@bf4)
|
||||||
|
- [#981](https://github.com/rails-api/active_model_serializers/pull/981) Remove unused PORO#to_param (@bf4)
|
||||||
|
- [#978](https://github.com/rails-api/active_model_serializers/pull/978) fix generators template bug (@regonn)
|
||||||
|
- [#975](https://github.com/rails-api/active_model_serializers/pull/975) Fixes virtual value not being used (@GriffinHeart)
|
||||||
|
- [#970](https://github.com/rails-api/active_model_serializers/pull/970) Fix transient tests failures (@Rodrigora)
|
||||||
|
- [#962](https://github.com/rails-api/active_model_serializers/pull/962) Rendering objects that doesn't have serializers (@bf4, @joaomdmoura, @JustinAiken)
|
||||||
|
- [#939](https://github.com/rails-api/active_model_serializers/pull/939) Use a more precise generated cache key (@aaronlerch)
|
||||||
|
- [#971](https://github.com/rails-api/active_model_serializers/pull/971) Restore has_one to generator (@bf4)
|
||||||
|
- [#965](https://github.com/rails-api/active_model_serializers/pull/965) options fedault valueserializable_hash and as_json (@bf4)
|
||||||
|
- [#959](https://github.com/rails-api/active_model_serializers/pull/959) TYPO on README.md (@kangkyu)
|
||||||
|
|
||||||
|
### [v0.10.0.rc2 (2015-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc1...v0.10.0.rc2)
|
||||||
|
- [#958](https://github.com/rails-api/active_model_serializers/pull/958) Splitting json adapter into two (@joaomdmoura)
|
||||||
|
* adds FlattenJSON as default adapter
|
||||||
|
- [#953](https://github.com/rails-api/active_model_serializers/pull/953) use model name to determine the type (@lsylvester)
|
||||||
|
* uses model name to determine the type
|
||||||
|
- [#949](https://github.com/rails-api/active_model_serializers/pull/949) Don't pass serializer option to associated serializers (@bf4, @edwardloveall)
|
||||||
|
- [#902](https://github.com/rails-api/active_model_serializers/pull/902) Added serializer file digest to the cache_key (@cristianbica)
|
||||||
|
- [#948](https://github.com/rails-api/active_model_serializers/pull/948) AMS supports JSONAPI 1.0 instead of RC4 (@SeyZ)
|
||||||
|
- [#936](https://github.com/rails-api/active_model_serializers/pull/936) Include meta when using json adapter with custom root (@chrisbranson)
|
||||||
|
- [#942](https://github.com/rails-api/active_model_serializers/pull/942) Small code styling issue (@thiagofm)
|
||||||
|
- [#930](https://github.com/rails-api/active_model_serializers/pull/930) Reverting PR #909 (@joaomdmoura)
|
||||||
|
- [#924](https://github.com/rails-api/active_model_serializers/pull/924) Avoid unecessary calls to attribute methods when fragment caching (@navinpeiris)
|
||||||
|
- [#925](https://github.com/rails-api/active_model_serializers/pull/925) Updates JSON API Adapter to generate RC4 schema (@benedikt)
|
||||||
|
* adds JSON API support 1.0
|
||||||
|
- [#918](https://github.com/rails-api/active_model_serializers/pull/918) Adding rescue_with_handler to clear state (@ryansch)
|
||||||
|
- [#909](https://github.com/rails-api/active_model_serializers/pull/909) Defining Json-API Adapter as Default (@joaomdmoura)
|
||||||
|
* remove root key option and split JSON adapter
|
||||||
|
- [#914](https://github.com/rails-api/active_model_serializers/pull/914) Prevent possible duplicated attributes in serializer (@groyoh)
|
||||||
|
- [#880](https://github.com/rails-api/active_model_serializers/pull/880) Inabling subclasses serializers to inherit attributes (@groyoh)
|
||||||
|
- [#913](https://github.com/rails-api/active_model_serializers/pull/913) Avoiding the serializer option when instantiating a new one for ArraySerializer Fixed #911 (@groyoh)
|
||||||
|
- [#897](https://github.com/rails-api/active_model_serializers/pull/897) Allow to define custom serializer for given class (@imanel)
|
||||||
|
- [#892](https://github.com/rails-api/active_model_serializers/pull/892) Fixed a bug that appeared when json adapter serialize a nil association (@groyoh)
|
||||||
|
- [#895](https://github.com/rails-api/active_model_serializers/pull/895) Adding a test to cover 'meta' and 'meta_key' attr_readers (@adomokos)
|
||||||
|
- [#894](https://github.com/rails-api/active_model_serializers/pull/894) Fixing typos in README.md (@adomokos)
|
||||||
|
- [#888](https://github.com/rails-api/active_model_serializers/pull/888) Changed duplicated test name in action controller test (@groyoh)
|
||||||
|
- [#890](https://github.com/rails-api/active_model_serializers/pull/890) Remove unused method `def_serializer` (@JustinAiken)
|
||||||
|
- [#887](https://github.com/rails-api/active_model_serializers/pull/887) Fixing tests on JRuby (@joaomdmoura)
|
||||||
|
- [#885](https://github.com/rails-api/active_model_serializers/pull/885) Updates rails versions for test and dev (@tonyta)
|
||||||
|
|
||||||
|
### [v0.10.0.rc1 (2015-04-22)](https://github.com/rails-api/active_model_serializers/compare/86fc7d7227f3ce538fcb28c1e8c7069ce311f0e1...v0.10.0.rc1)
|
||||||
|
- [#810](https://github.com/rails-api/active_model_serializers/pull/810) Adding Fragment Cache to AMS (@joaomdmoura)
|
||||||
|
* adds fragment cache support
|
||||||
|
- [#868](https://github.com/rails-api/active_model_serializers/pull/868) Fixed a bug that appears when a nil association is included (@groyoh)
|
||||||
|
- [#861](https://github.com/rails-api/active_model_serializers/pull/861) README: Add emphasis to single-word difference (@machty)
|
||||||
|
- [#858](https://github.com/rails-api/active_model_serializers/pull/858) Included resource fixes (@mateomurphy)
|
||||||
|
- [#853](https://github.com/rails-api/active_model_serializers/pull/853) RC3 Updates for JSON API (@mateomurphy)
|
||||||
|
- [#852](https://github.com/rails-api/active_model_serializers/pull/852) Fix options merge order in `each_association` (@mateomurphy)
|
||||||
|
- [#850](https://github.com/rails-api/active_model_serializers/pull/850) Use association value for determining serializer used (@mateomurphy)
|
||||||
|
- [#843](https://github.com/rails-api/active_model_serializers/pull/843) Remove the mailing list from the README (@JoshSmith)
|
||||||
|
- [#842](https://github.com/rails-api/active_model_serializers/pull/842) Add notes on how you can help to contributing documentation (@JoshSmith)
|
||||||
|
- [#833](https://github.com/rails-api/active_model_serializers/pull/833) Cache serializers for class (@lsylvester)
|
||||||
|
- [#837](https://github.com/rails-api/active_model_serializers/pull/837) Store options in array serializers (@kurko)
|
||||||
|
- [#836](https://github.com/rails-api/active_model_serializers/pull/836) Makes passed in options accessible inside serializers (@kurko)
|
||||||
|
- [#773](https://github.com/rails-api/active_model_serializers/pull/773) Make json api adapter 'include' option accept an array (@sweatypitts)
|
||||||
|
- [#830](https://github.com/rails-api/active_model_serializers/pull/830) Add contributing readme (@JoshSmith)
|
||||||
|
- [#811](https://github.com/rails-api/active_model_serializers/pull/811) Reimplement serialization scope and scope_name (@mateomurphy)
|
||||||
|
- [#725](https://github.com/rails-api/active_model_serializers/pull/725) Support has_one to be compatible with 0.8.x (@ggordon)
|
||||||
|
* adds `has_one` attribute for backwards compatibility
|
||||||
|
- [#822](https://github.com/rails-api/active_model_serializers/pull/822) Replace has_one with attribute in template (@bf4)
|
||||||
|
- [#821](https://github.com/rails-api/active_model_serializers/pull/821) Fix explicit serializer for associations (@wjordan)
|
||||||
|
- [#798](https://github.com/rails-api/active_model_serializers/pull/798) Fix lost test `test_include_multiple_posts_and_linked` (@donbobka)
|
||||||
|
- [#807](https://github.com/rails-api/active_model_serializers/pull/807) Add Overriding attribute methods section to README. (@alexstophel)
|
||||||
|
- [#693](https://github.com/rails-api/active_model_serializers/pull/693) Cache Support at AMS 0.10.0 (@joaomdmoura)
|
||||||
|
* adds cache support to attributes and associations.
|
||||||
|
- [#792](https://github.com/rails-api/active_model_serializers/pull/792) Association overrides (@kurko)
|
||||||
|
* adds method to override association
|
||||||
|
- [#794](https://github.com/rails-api/active_model_serializers/pull/794) add to_param for correct URL generation (@carlesjove)
|
||||||
|
|
||||||
|
### v0.10.0-pre
|
||||||
|
|
||||||
|
- [Introduce Adapter](https://github.com/rails-api/active_model_serializers/commit/f00fe5595ddf741dc26127ed8fe81adad833ead5)
|
||||||
|
- Prefer `ActiveModel::Serializer` to `ActiveModelSerializers`:
|
||||||
|
- [Namespace](https://github.com/rails-api/active_model_serializers/commit/729a823868e8c7ac86c653fcc7100ee511e08cb6#diff-fe7aa2941c19a41ccea6e52940d84016).
|
||||||
|
- [README](https://github.com/rails-api/active_model_serializers/commit/4a2d9853ba7486acc1747752982aa5650e7fd6e9).
|
||||||
15
CHANGELOG-prehistory.md
Normal file
15
CHANGELOG-prehistory.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## Prehistory
|
||||||
|
|
||||||
|
- [Changing Serialization/Serializers namespace to `Serializable` (November 30, 2011)](https://github.com/rails/rails/commit/8896b4fdc8a543157cdf4dfc378607ebf6c10ab0)
|
||||||
|
- [Merge branch 'serializers'. This implements the ActiveModel::Serializer object. Includes code, tests, generators and guides. From José and Yehuda with love.](https://github.com/rails/rails/commit/fcacc6986ab60f1fb2e423a73bf47c7abd7b191d)
|
||||||
|
- But [was reverted](https://github.com/rails/rails/commit/5b2eb64ceb08cd005dc06b721935de5853971473).
|
||||||
|
'[Revert the serializers API as other alternatives are now also under discussion](https://github.com/rails/rails/commit/0a4035b12a6c59253cb60f9e3456513c6a6a9d33)'.
|
||||||
|
- [Proposed Implementation to Rails 3.2 by @wycats and @josevalim (November 25, 2011)](https://github.com/rails/rails/pull/3753)
|
||||||
|
- [Creation of `ActionController::Serialization`, initial serializer
|
||||||
|
support (September, 26 2011)](https://github.com/rails/rails/commit/8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe).
|
||||||
|
- [Docs and CHANGELOG](https://github.com/rails/rails/commit/696d01f7f4a8ed787924a41cce6df836cd73c46f)
|
||||||
|
- [Deprecation of ActiveModel::Serialization to ActiveModel::Serializable](https://github.com/rails/rails/blob/696d01f7f4a8ed787924a41cce6df836cd73c46f/activemodel/lib/active_model/serialization.rb)
|
||||||
|
- [Creation of `ActiveModel::Serialization` from `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/c6bc8e662614be711f45a8d4b231d5f993b024a7#diff-d029b9768d8df0407a35804a468e3ae5)
|
||||||
|
- [Integration of `ActiveModel::Serializer` into `ActiveRecord::Serialization`](https://github.com/rails/rails/commit/783db25e0c640c1588732967a87d65c10fddc08e)
|
||||||
|
- [Creation of `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/d2b78b3594b9cc9870e6a6ebfeb2e56d00e6ddb8#diff-80d5beeced9bdc24ca2b04a201543bdd)
|
||||||
|
- [Creation of `ActiveModel::Serializers::JSON` in Rails (2009)](https://github.com/rails/rails/commit/fbdf706fffbfb17731a1f459203d242414ef5086)
|
||||||
643
CHANGELOG.md
643
CHANGELOG.md
@ -1,6 +1,6 @@
|
|||||||
## 0.10.x
|
## Dev
|
||||||
|
|
||||||
### [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/master..dev)
|
||||||
|
|
||||||
Breaking changes:
|
Breaking changes:
|
||||||
|
|
||||||
@ -10,641 +10,10 @@ Fixes:
|
|||||||
|
|
||||||
Misc:
|
Misc:
|
||||||
|
|
||||||
### [v0.10.6 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.5...v0.10.6)
|
## [0.10.x](CHANGELOG-0-10.md)
|
||||||
|
|
||||||
Fixes:
|
## [0.09.x](CHANGELOG-0-09.md)
|
||||||
|
|
||||||
- [#1857](https://github.com/rails-api/active_model_serializers/pull/1857) JSON:API does not load belongs_to relation to get identifier id. (@bf4)
|
## [0.08.x](CHANGELOG-0-08.md)
|
||||||
- [#2119](https://github.com/rails-api/active_model_serializers/pull/2119) JSON:API returns null resource object identifier when 'id' is null. (@bf4)
|
|
||||||
- [#2093](https://github.com/rails-api/active_model_serializers/pull/2093) undef problematic Serializer methods: display, select. (@bf4)
|
|
||||||
|
|
||||||
Misc:
|
## [Prehistory](CHANGELOG-prehistory.md)
|
||||||
|
|
||||||
- [#2104](https://github.com/rails-api/active_model_serializers/pull/2104) Documentation for serializers and rendering. (@cassidycodes)
|
|
||||||
- [#2081](https://github.com/rails-api/active_model_serializers/pull/2081) Documentation for `include` option in adapters. (@charlie-wasp)
|
|
||||||
- [#2120](https://github.com/rails-api/active_model_serializers/pull/2120) Documentation for association options: foreign_key, type, class_name, namespace. (@bf4)
|
|
||||||
|
|
||||||
### [v0.10.5 (2017-03-07)](https://github.com/rails-api/active_model_serializers/compare/v0.10.4...v0.10.5)
|
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
- [#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)
|
|
||||||
- [#2057](https://github.com/rails-api/active_model_serializers/pull/2057)
|
|
||||||
Update version constraint for jsonapi-renderer to `['>= 0.1.1.beta1', '< 0.2']`
|
|
||||||
(@jaredbeck)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
|
|
||||||
- [#2055](https://github.com/rails-api/active_model_serializers/pull/2055)
|
|
||||||
Replace deprecated dependency jsonapi with jsonapi-renderer. (@jaredbeck)
|
|
||||||
- [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) Make test attributes explicit. Tests have Model#associations. (@bf4)
|
|
||||||
- [#1981](https://github.com/rails-api/active_model_serializers/pull/1981) Fix relationship link documentation. (@groyoh)
|
|
||||||
- [#2035](https://github.com/rails-api/active_model_serializers/pull/2035) Document how to disable the logger. (@MSathieu)
|
|
||||||
- [#2039](https://github.com/rails-api/active_model_serializers/pull/2039) Documentation fixes. (@biow0lf)
|
|
||||||
|
|
||||||
### [v0.10.4 (2017-01-06)](https://github.com/rails-api/active_model_serializers/compare/v0.10.3...v0.10.4)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
|
|
||||||
- [#2005](https://github.com/rails-api/active_model_serializers/pull/2005) Update jsonapi runtime dependency to 0.1.1.beta6, support Ruby 2.4. (@kofronpi)
|
|
||||||
- [#1993](https://github.com/rails-api/active_model_serializers/pull/1993) Swap out KeyTransform for CaseTransform gem for the possibility of native extension use. (@NullVoxPopuli)
|
|
||||||
|
|
||||||
### [v0.10.3 (2016-11-21)](https://github.com/rails-api/active_model_serializers/compare/v0.10.2...v0.10.3)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- [#1973](https://github.com/rails-api/active_model_serializers/pull/1973) Fix namespace lookup for collections and has_many relationships (@groyoh)
|
|
||||||
- [#1887](https://github.com/rails-api/active_model_serializers/pull/1887) Make the comment reflect what the function does (@johnnymo87)
|
|
||||||
- [#1890](https://github.com/rails-api/active_model_serializers/issues/1890) Ensure generator inherits from ApplicationSerializer when available (@richmolj)
|
|
||||||
- [#1922](https://github.com/rails-api/active_model_serializers/pull/1922) Make railtie an optional dependency in runtime (@ggpasqualino)
|
|
||||||
- [#1930](https://github.com/rails-api/active_model_serializers/pull/1930) Ensure valid jsonapi when relationship has no links or data (@richmolj)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
- [#1757](https://github.com/rails-api/active_model_serializers/pull/1757) Make serializer lookup chain configurable. (@NullVoxPopuli)
|
|
||||||
- [#1968](https://github.com/rails-api/active_model_serializers/pull/1968) (@NullVoxPopuli)
|
|
||||||
- Add controller namespace to default controller lookup
|
|
||||||
- Provide a `namespace` render option
|
|
||||||
- document how set the namespace in the controller for implicit lookup.
|
|
||||||
- [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli)
|
|
||||||
- Added `jsonapi_namespace_separator` config option.
|
|
||||||
- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee)
|
|
||||||
- [#1917](https://github.com/rails-api/active_model_serializers/pull/1917) Add `jsonapi_pagination_links_enabled` configuration option (@richmolj)
|
|
||||||
- [#1797](https://github.com/rails-api/active_model_serializers/pull/1797) Only include 'relationships' when sideloading (@richmolj)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- [#1833](https://github.com/rails-api/active_model_serializers/pull/1833) Remove relationship links if they are null (@groyoh)
|
|
||||||
- [#1881](https://github.com/rails-api/active_model_serializers/pull/1881) ActiveModelSerializers::Model correctly works with string keys (@yevhene)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [#1767](https://github.com/rails-api/active_model_serializers/pull/1767) Replace raising/rescuing `CollectionSerializer::NoSerializerError`,
|
|
||||||
throw/catch `:no_serializer`. (@bf4)
|
|
||||||
- [#1839](https://github.com/rails-api/active_model_serializers/pull/1839) `fields` tests demonstrating usage for both attributes and relationships. (@NullVoxPopuli)
|
|
||||||
- [#1812](https://github.com/rails-api/active_model_serializers/pull/1812) add a code of conduct (@corainchicago)
|
|
||||||
|
|
||||||
- [#1878](https://github.com/rails-api/active_model_serializers/pull/1878) Cache key generation for serializers now uses `ActiveSupport::Cache.expand_cache_key` instead of `Array#join` by default and is also overridable. This change should be backward-compatible. (@markiz)
|
|
||||||
|
|
||||||
- [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@cassidycodes)
|
|
||||||
- [#1909](https://github.com/rails-api/active_model_serializers/pull/1909) Add documentation for relationship links. (@vasilakisfil, @NullVoxPopuli)
|
|
||||||
- [#1959](https://github.com/rails-api/active_model_serializers/pull/1959) Add documentation for root. (@shunsuke227ono)
|
|
||||||
- [#1967](https://github.com/rails-api/active_model_serializers/pull/1967) Improve type method documentation. (@yukideluxe)
|
|
||||||
|
|
||||||
### [v0.10.2 (2016-07-05)](https://github.com/rails-api/active_model_serializers/compare/v0.10.1...v0.10.2)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- [#1814](https://github.com/rails-api/active_model_serializers/pull/1814) Ensuring read_multi works with fragment cache
|
|
||||||
- [#1848](https://github.com/rails-api/active_model_serializers/pull/1848) Redefine associations on inherited serializers. (@EhsanYousefi)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [#1808](https://github.com/rails-api/active_model_serializers/pull/1808) Adds documentation for `fields` option. (@luizkowalski)
|
|
||||||
|
|
||||||
### [v0.10.1 (2016-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0...v0.10.1)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- [#1668](https://github.com/rails-api/active_model_serializers/pull/1668) Exclude nil and empty links. (@sigmike)
|
|
||||||
- [#1426](https://github.com/rails-api/active_model_serializers/pull/1426) Add ActiveModelSerializers.config.default_includes (@empact)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- [#1754](https://github.com/rails-api/active_model_serializers/pull/1754) Fixes #1759, Grape integration, improves serialization_context
|
|
||||||
missing error message on pagination. Document overriding CollectionSerializer#paginated?. (@bf4)
|
|
||||||
Moved serialization_context creation to Grape formatter, so resource serialization works without explicit calls to the `render` helper method.
|
|
||||||
Added Grape collection tests. (@onomated)
|
|
||||||
- [#1287](https://github.com/rails-api/active_model_serializers/pull/1287) Pass `fields` options from adapter to serializer. (@vasilakisfil)
|
|
||||||
- [#1710](https://github.com/rails-api/active_model_serializers/pull/1710) Prevent association loading when `include_data` option
|
|
||||||
is set to `false`. (@groyoh)
|
|
||||||
- [#1747](https://github.com/rails-api/active_model_serializers/pull/1747) Improve jsonapi mime type registration for Rails 5 (@remear)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [#1734](https://github.com/rails-api/active_model_serializers/pull/1734) Adds documentation for conditional attribute (@lambda2)
|
|
||||||
- [#1685](https://github.com/rails-api/active_model_serializers/pull/1685) Replace `IncludeTree` with `IncludeDirective` from the jsonapi gem.
|
|
||||||
|
|
||||||
### [v0.10.0 (2016-05-17)](https://github.com/rails-api/active_model_serializers/compare/4a2d9853ba7...v0.10.0)
|
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
- [#1662](https://github.com/rails-api/active_model_serializers/pull/1662) Drop support for Rails 4.0 and Ruby 2.0.0. (@remear)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- [#1677](https://github.com/rails-api/active_model_serializers/pull/1677) Add `assert_schema`, `assert_request_schema`, `assert_request_response_schema`. (@bf4)
|
|
||||||
- [#1697](https://github.com/rails-api/active_model_serializers/pull/1697) Include actual exception message with custom exceptions;
|
|
||||||
`Test::Schema` exceptions are now `Minitest::Assertion`s. (@bf4)
|
|
||||||
- [#1699](https://github.com/rails-api/active_model_serializers/pull/1699) String/Lambda support for conditional attributes/associations (@mtsmfm)
|
|
||||||
- [#1687](https://github.com/rails-api/active_model_serializers/pull/1687) Only calculate `_cache_digest` (in `cache_key`) when `skip_digest` is false. (@bf4)
|
|
||||||
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
|
|
||||||
to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader)
|
|
||||||
- [#1726](https://github.com/rails-api/active_model_serializers/pull/1726) Adds polymorphic option to association definition which includes association type/nesting in serializer (@cgmckeever)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre)
|
|
||||||
- [#1730](https://github.com/rails-api/active_model_serializers/pull/1730) Adds documentation for overriding default serializer based on conditions (@groyoh/@cgmckeever)
|
|
||||||
|
|
||||||
### [v0.10.0.rc5 (2016-04-04)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc4...v0.10.0.rc5)
|
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
|
|
||||||
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Changed :dashed key transform to :dash. (@remear)
|
|
||||||
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear)
|
|
||||||
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
|
|
||||||
take precedence over `serialization_scope` in the controller.
|
|
||||||
Fix tests that required tearing down dynamic methods. (@bf4)
|
|
||||||
- [#1644](https://github.com/rails-api/active_model_serializers/pull/1644) Include adapter name in cache key so
|
|
||||||
that the same serializer can be cached per adapter. (@bf4 via #1346 by @kevintyll)
|
|
||||||
- [#1642](https://github.com/rails-api/active_model_serializers/pull/1642) Prefer object.cache_key over the generated
|
|
||||||
cache key. (@bf4 via #1346 by @kevintyll)
|
|
||||||
- [#1637](https://github.com/rails-api/active_model_serializers/pull/1637) Make references to 'ActionController::Base.cache_store' explicit
|
|
||||||
in order to avoid issues when application controllers inherit from 'ActionController::API'. (@ncuesta)
|
|
||||||
- [#1633](https://github.com/rails-api/active_model_serializers/pull/1633) Yield 'serializer' to serializer association blocks. (@bf4)
|
|
||||||
- [#1616](https://github.com/rails-api/active_model_serializers/pull/1616) SerializableResource handles no serializer like controller. (@bf4)
|
|
||||||
- [#1618](https://github.com/rails-api/active_model_serializers/issues/1618) Get collection root key for
|
|
||||||
empty collection from explicit serializer option, when possible. (@bf4)
|
|
||||||
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Provide key translation. (@remear)
|
|
||||||
- [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe
|
|
||||||
(using the Attributes adapter by default). (@bf4)
|
|
||||||
- [#1550](https://github.com/rails-api/active_model_serializers/pull/1550) Add
|
|
||||||
Rails url_helpers to `SerializationContext` for use in links. (@remear, @bf4)
|
|
||||||
- [#1004](https://github.com/rails-api/active_model_serializers/pull/1004) JSON API errors object implementation.
|
|
||||||
- Only implements `detail` and `source` as derived from `ActiveModel::Error`
|
|
||||||
- Provides checklist of remaining questions and remaining parts of the spec.
|
|
||||||
- [#1515](https://github.com/rails-api/active_model_serializers/pull/1515) Adds support for symbols to the
|
|
||||||
`ActiveModel::Serializer.type` method. (@groyoh)
|
|
||||||
- [#1504](https://github.com/rails-api/active_model_serializers/pull/1504) Adds the changes missing from #1454
|
|
||||||
and add more tests for resource identifier and relationship objects. Fix association block with link
|
|
||||||
returning `data: nil`.(@groyoh)
|
|
||||||
- [#1372](https://github.com/rails-api/active_model_serializers/pull/1372) Support
|
|
||||||
cache_store.read_multi. (@LcpMarvel)
|
|
||||||
- [#1018](https://github.com/rails-api/active_model_serializers/pull/1018) Add more tests and docs for top-level links. (@leandrocp)
|
|
||||||
- [#1454](https://github.com/rails-api/active_model_serializers/pull/1454) Add support for
|
|
||||||
relationship-level links and meta attributes. (@beauby)
|
|
||||||
- [#1340](https://github.com/rails-api/active_model_serializers/pull/1340) Add support for resource-level meta. (@beauby)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- [#1657](https://github.com/rails-api/active_model_serializers/pull/1657) Add missing missing require "active_support/json". (@andreaseger)
|
|
||||||
- [#1661](https://github.com/rails-api/active_model_serializers/pull/1661) Fixes `read_attribute_for_serialization` not
|
|
||||||
seeing methods defined in serialization superclass (#1653, #1658, #1660), introduced in #1650. (@bf4)
|
|
||||||
- [#1651](https://github.com/rails-api/active_model_serializers/pull/1651) Fix deserialization of nil relationships. (@NullVoxPopuli)
|
|
||||||
- [#1480](https://github.com/rails-api/active_model_serializers/pull/1480) Fix setting of cache_store from Rails configuration. (@bf4)
|
|
||||||
Fix unintentional mutating of value in memory cache store. (@groyoh)
|
|
||||||
- [#1622](https://github.com/rails-api/active_model_serializers/pull/1622) Fragment cache changed from per-record to per-serializer.
|
|
||||||
Now, two serializers that use the same model may be separately cached. (@lserman)
|
|
||||||
- [#1478](https://github.com/rails-api/active_model_serializers/pull/1478) Cache store will now be correctly set when serializers are
|
|
||||||
loaded *before* Rails initializes. (@bf4)
|
|
||||||
- [#1570](https://github.com/rails-api/active_model_serializers/pull/1570) Fixed pagination issue with last page size. (@bmorrall)
|
|
||||||
- [#1516](https://github.com/rails-api/active_model_serializers/pull/1516) No longer return a nil href when only
|
|
||||||
adding meta to a relationship link. (@groyoh)
|
|
||||||
- [#1458](https://github.com/rails-api/active_model_serializers/pull/1458) Preserve the serializer
|
|
||||||
type when fragment caching. (@bdmac)
|
|
||||||
- [#1477](https://github.com/rails-api/active_model_serializers/pull/1477) Fix `fragment_cached?`
|
|
||||||
method to check if caching. (@bdmac)
|
|
||||||
- [#1501](https://github.com/rails-api/active_model_serializers/pull/1501) Adds tests for SerializableResource::use_adapter?,doc typos (@domitian)
|
|
||||||
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [#1608](https://github.com/rails-api/active_model_serializers/pull/1608) Move SerializableResource to ActiveModelSerializers (@groyoh)
|
|
||||||
- [#1602](https://github.com/rails-api/active_model_serializers/pull/1602) Add output examples to Adapters docs (@remear)
|
|
||||||
- [#1557](https://github.com/rails-api/active_model_serializers/pull/1557) Update docs regarding overriding the root key (@Jwan622)
|
|
||||||
- [#1471](https://github.com/rails-api/active_model_serializers/pull/1471) [Cleanup] Serializer caching is its own concern. (@bf4)
|
|
||||||
- [#1482](https://github.com/rails-api/active_model_serializers/pull/1482) Document JSON API implementation defs and progress in class. (@bf4)
|
|
||||||
- [#1551](https://github.com/rails-api/active_model_serializers/pull/1551) Added codebeat badge (@korzonek)
|
|
||||||
- [#1527](https://github.com/rails-api/active_model_serializers/pull/1527) Refactor fragment cache class. (@groyoh)
|
|
||||||
- [#1560](https://github.com/rails-api/active_model_serializers/pull/1560) Update rubocop and address its warnings. (@bf4 @groyoh)
|
|
||||||
- [#1545](https://github.com/rails-api/active_model_serializers/pull/1545) Document how to pass arbitrary options to the
|
|
||||||
serializer (@CodedBeardedSignedTaylor)
|
|
||||||
- [#1496](https://github.com/rails-api/active_model_serializers/pull/1496) Run all branches against JRuby on CI (@nadavshatz)
|
|
||||||
- [#1559](https://github.com/rails-api/active_model_serializers/pull/1559) Add a deprecation DSL. (@bf4 @groyoh)
|
|
||||||
- [#1543](https://github.com/rails-api/active_model_serializers/pull/1543) Add the changes missing from #1535. (@groyoh)
|
|
||||||
- [#1535](https://github.com/rails-api/active_model_serializers/pull/1535) Move the adapter and adapter folder to
|
|
||||||
active_model_serializers folder and changes the module namespace. (@domitian @bf4)
|
|
||||||
- [#1497](https://github.com/rails-api/active_model_serializers/pull/1497) Add JRuby-9000 to appveyor.yml(@corainchicago)
|
|
||||||
- [#1420](https://github.com/rails-api/active_model_serializers/pull/1420) Adds tests and documentation for polymorphism(@marcgarreau)
|
|
||||||
|
|
||||||
|
|
||||||
### [v0.10.0.rc4 (2016-01-27)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc3...v0.10.0.rc4)
|
|
||||||
Breaking changes:
|
|
||||||
|
|
||||||
- [#1360](https://github.com/rails-api/active_model_serializers/pull/1360)
|
|
||||||
[#1369](https://github.com/rails-api/active_model_serializers/pull/1369) Drop support for Ruby 1.9.3 (@karaAJC, @maurogeorge)
|
|
||||||
- [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Remove Serializer#root_name (@beauby)
|
|
||||||
- [#1138](https://github.com/rails-api/active_model_serializers/pull/1138) Introduce Adapter::Base (@bf4)
|
|
||||||
* Adapters now inherit Adapter::Base. 'Adapter' is now a module, no longer a class.
|
|
||||||
* using a class as a namespace that you also inherit from is complicated and circular at times i.e.
|
|
||||||
buggy (see https://github.com/rails-api/active_model_serializers/pull/1177)
|
|
||||||
* The class methods on Adapter aren't necessarily related to the instance methods, they're more
|
|
||||||
Adapter functions.
|
|
||||||
* named `Base` because it's a Rails-ism.
|
|
||||||
* It helps to isolate and highlight what the Adapter interface actually is.
|
|
||||||
- [#1418](https://github.com/rails-api/active_model_serializers/pull/1418)
|
|
||||||
serialized collections now use the root option as is; now, only the
|
|
||||||
root derived from the serializer or object is always pluralized.
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
- [#1406](https://github.com/rails-api/active_model_serializers/pull/1406) Allow for custom dynamic values in JSON API links (@beauby)
|
|
||||||
- [#1270](https://github.com/rails-api/active_model_serializers/pull/1270) Adds `assert_response_schema` test helper (@maurogeorge)
|
|
||||||
- [#1099](https://github.com/rails-api/active_model_serializers/pull/1099) Adds `assert_serializer` test helper (@maurogeorge)
|
|
||||||
- [#1403](https://github.com/rails-api/active_model_serializers/pull/1403) Add support for if/unless on attributes/associations (@beauby)
|
|
||||||
- [#1248](https://github.com/rails-api/active_model_serializers/pull/1248) Experimental: Add support for JSON API deserialization (@beauby)
|
|
||||||
- [#1378](https://github.com/rails-api/active_model_serializers/pull/1378) Change association blocks
|
|
||||||
to be evaluated in *serializer* scope, rather than *association* scope. (@bf4)
|
|
||||||
* Syntax changes from e.g.
|
|
||||||
`has_many :titles do customers.pluck(:title) end` (in #1356) to
|
|
||||||
`has_many :titles do object.customers.pluck(:title) end`
|
|
||||||
- [#1356](https://github.com/rails-api/active_model_serializers/pull/1356) Add inline syntax for
|
|
||||||
attributes and associations (@bf4 @beauby @noahsilas)
|
|
||||||
* Allows defining attributes so that they don't conflict with existing methods. e.g. `attribute
|
|
||||||
:title do 'Mr. Topum Hat' end`
|
|
||||||
* Allows defining associations so that they don't conflict with existing methods. e.g. `has_many
|
|
||||||
:titles do customers.pluck(:title) end`
|
|
||||||
* Allows dynamic associations, as compared to compare to using
|
|
||||||
[`virtual_value`](https://github.com/rails-api/active_model_serializers/pull/1356#discussion_r47146466).
|
|
||||||
e.g. `has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]`
|
|
||||||
* Removes dynamically defined methods on the serializer
|
|
||||||
- [#1336](https://github.com/rails-api/active_model_serializers/pull/1336) Added support for Grape >= 0.13, < 1.0 (@johnhamelink)
|
|
||||||
- [#1322](https://github.com/rails-api/active_model_serializers/pull/1322) Instrumenting rendering of resources (@bf4, @maurogeorge)
|
|
||||||
- [#1291](https://github.com/rails-api/active_model_serializers/pull/1291) Add logging (@maurogeorge)
|
|
||||||
- [#1272](https://github.com/rails-api/active_model_serializers/pull/1272) Add PORO serializable base class: ActiveModelSerializers::Model (@bf4)
|
|
||||||
- [#1255](https://github.com/rails-api/active_model_serializers/pull/1255) Make more class attributes inheritable (@bf4)
|
|
||||||
- [#1249](https://github.com/rails-api/active_model_serializers/pull/1249) Inheritance of serializer inheriting the cache configuration(@Rodrigora)
|
|
||||||
- [#1247](https://github.com/rails-api/active_model_serializers/pull/1247) Add support for toplevel JSON API links (@beauby)
|
|
||||||
- [#1246](https://github.com/rails-api/active_model_serializers/pull/1246) Add support for resource-level JSON API links (@beauby)
|
|
||||||
- [#1225](https://github.com/rails-api/active_model_serializers/pull/1225) Better serializer lookup, use nested serializer when it exists (@beauby)
|
|
||||||
- [#1213](https://github.com/rails-api/active_model_serializers/pull/1213) `type` directive for serializer to control type field with json-api adapter (@youroff)
|
|
||||||
- [#1172](https://github.com/rails-api/active_model_serializers/pull/1172) Better serializer registration, get more than just the first module (@bf4)
|
|
||||||
- [#1158](https://github.com/rails-api/active_model_serializers/pull/1158) Add support for wildcards in `include` option (@beauby)
|
|
||||||
- [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested
|
|
||||||
associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby).
|
|
||||||
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
|
|
||||||
- [#1251](https://github.com/rails-api/active_model_serializers/pull/1251) Rename ArraySerializer to
|
|
||||||
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
|
|
||||||
- [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that,
|
|
||||||
when disabled, requires serializers to explicitly specified. (@trek)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- [#1352](https://github.com/rails-api/active_model_serializers/pull/1352) Fix generators; Isolate Rails-specifc code in Railties (@dgynn, @bf4)
|
|
||||||
- [#1384](https://github.com/rails-api/active_model_serializers/pull/1384)Fix database state leaking across tests (@bf4)
|
|
||||||
- [#1297](https://github.com/rails-api/active_model_serializers/pull/1297) Fix `fields` option to restrict relationships as well (@beauby)
|
|
||||||
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)
|
|
||||||
- [#1214](https://github.com/rails-api/active_model_serializers/pull/1214) retrieve the key from the reflection options when building associations (@NullVoxPopuli, @hut8)
|
|
||||||
- [#1358](https://github.com/rails-api/active_model_serializers/pull/1358) Handle serializer file paths with spaces (@rwstauner, @bf4)
|
|
||||||
- [#1195](https://github.com/rails-api/active_model_serializers/pull/1195) Fix id override (@beauby)
|
|
||||||
- [#1185](https://github.com/rails-api/active_model_serializers/pull/1185) Fix options passing in Json and Attributes adapters (@beauby)
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
|
|
||||||
- [#1383](https://github.com/rails-api/active_model_serializers/pull/1383) Simplify reflections handling (@beauby)
|
|
||||||
- [#1370](https://github.com/rails-api/active_model_serializers/pull/1370) Simplify attributes handling via a mixin (@beauby)
|
|
||||||
- [#1301](https://github.com/rails-api/active_model_serializers/pull/1301) Mapping JSON API spec / schema to AMS (@bf4)
|
|
||||||
- [#1271](https://github.com/rails-api/active_model_serializers/pull/1271) Handle no serializer source file to digest (@bf4)
|
|
||||||
- [#1260](https://github.com/rails-api/active_model_serializers/pull/1260) Serialization and Cache Documentation (@bf4)
|
|
||||||
- [#1259](https://github.com/rails-api/active_model_serializers/pull/1259) Add more info to CONTRIBUTING (@bf4)
|
|
||||||
- [#1233](https://github.com/rails-api/active_model_serializers/pull/1233) Top-level meta and meta_key options no longer handled at serializer level (@beauby)
|
|
||||||
- [#1232](https://github.com/rails-api/active_model_serializers/pull/1232) fields option no longer handled at serializer level (@beauby)
|
|
||||||
- [#1220](https://github.com/rails-api/active_model_serializers/pull/1220) Remove empty rubocop.rake (@maurogeorge)
|
|
||||||
- [#1178](https://github.com/rails-api/active_model_serializers/pull/1178) env CAPTURE_STDERR=false lets devs see hard failures (@bf4)
|
|
||||||
- [#1177](https://github.com/rails-api/active_model_serializers/pull/1177) Remove Adapter autoloads in favor of require (@bf4)
|
|
||||||
- [#1117](https://github.com/rails-api/active_model_serializers/pull/1117) FlattenJson adapter no longer inherits Json adapter, renamed to Attributes (@bf4)
|
|
||||||
- [#1171](https://github.com/rails-api/active_model_serializers/pull/1171) add require statements to top of file (@shicholas)
|
|
||||||
- [#1167](https://github.com/rails-api/active_model_serializers/pull/1167) Delegate Serializer.attributes to Serializer.attribute (@bf4)
|
|
||||||
- [#1174](https://github.com/rails-api/active_model_serializers/pull/1174) Consistently refer to the 'JSON API' and the 'JsonApi' adapter (@bf4)
|
|
||||||
- [#1173](https://github.com/rails-api/active_model_serializers/pull/1173) Comment private accessor warnings (@bf4)
|
|
||||||
- [#1166](https://github.com/rails-api/active_model_serializers/pull/1166) Prefer methods over instance variables (@bf4)
|
|
||||||
- [#1168](https://github.com/rails-api/active_model_serializers/pull/1168) Fix appveyor failure cache not being expired (@bf4)
|
|
||||||
- [#1161](https://github.com/rails-api/active_model_serializers/pull/1161) Remove duplicate test helper (@bf4)
|
|
||||||
- [#1360](https://github.com/rails-api/active_model_serializers/pull/1360) Update CI to test 2.2.2 -> 2.2.3 (@karaAJC)
|
|
||||||
- [#1371](https://github.com/rails-api/active_model_serializers/pull/1371) Refactor, update, create documentation (@bf4)
|
|
||||||
|
|
||||||
### [v0.10.0.rc3 (2015-09-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc2...v0.10.0.rc3)
|
|
||||||
- [#1129](https://github.com/rails-api/active_model_serializers/pull/1129) Remove SerializableResource.serialize in favor of `.new` (@bf4)
|
|
||||||
- [#1155](https://github.com/rails-api/active_model_serializers/pull/1155) Outside controller use tutorial (@CodedBeardedSignedTaylor)
|
|
||||||
- [#1154](https://github.com/rails-api/active_model_serializers/pull/1154) Rubocop fixes for issues introduced by #1089 (@NullVoxPopuli)
|
|
||||||
- [#1089](https://github.com/rails-api/active_model_serializers/pull/1089) Add ActiveModelSerializers.logger with default null device (@bf4)
|
|
||||||
- [#1109](https://github.com/rails-api/active_model_serializers/pull/1109) Make better use of Minitest's lifecycle (@bf4)
|
|
||||||
- [#1144](https://github.com/rails-api/active_model_serializers/pull/1144) Fix Markdown to adapters documentation (@bacarini)
|
|
||||||
- [#1121](https://github.com/rails-api/active_model_serializers/pull/1121) Refactor `add_links` in JSONAPI adapter. (@beauby)
|
|
||||||
- [#1150](https://github.com/rails-api/active_model_serializers/pull/1150) Remove legacy method accidentally reintroduced in #1017 (@beauby)
|
|
||||||
- [#1149](https://github.com/rails-api/active_model_serializers/pull/1149) Update README with nested included association example. (@mattmueller)
|
|
||||||
- [#1110](https://github.com/rails-api/active_model_serializers/pull/1110) Add lint tests for AR models (@beauby)
|
|
||||||
- [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Extended format for JSONAPI `include` option (@beauby)
|
|
||||||
* adds extended format for `include` option to JsonApi adapter
|
|
||||||
- [#1142](https://github.com/rails-api/active_model_serializers/pull/1142) Updating wording on cache expiry in README (@leighhalliday)
|
|
||||||
- [#1140](https://github.com/rails-api/active_model_serializers/pull/1140) Fix typo in fieldset exception (@lautis)
|
|
||||||
- [#1132](https://github.com/rails-api/active_model_serializers/pull/1132) Get rid of unnecessary instance variables, and implied dependencies. (@beauby)
|
|
||||||
- [#1139](https://github.com/rails-api/active_model_serializers/pull/1139) Documentation for serializing resources without render (@PericlesTheo)
|
|
||||||
- [#1017](https://github.com/rails-api/active_model_serializers/pull/1017) Make Adapters registerable so they are not namespace-constrained (@bf4)
|
|
||||||
- [#1120](https://github.com/rails-api/active_model_serializers/pull/1120) Add windows platform to loading sqlite3 (@Eric-Guo)
|
|
||||||
- [#1123](https://github.com/rails-api/active_model_serializers/pull/1123) Remove url options (@bacarini)
|
|
||||||
- [#1093](https://github.com/rails-api/active_model_serializers/pull/1093) Factor `with_adapter` + force cache clear before each test. (@beauby)
|
|
||||||
- [#1095](https://github.com/rails-api/active_model_serializers/pull/1095) Add documentation about configuration options. (@beauby)
|
|
||||||
- [#1069](https://github.com/rails-api/active_model_serializers/pull/1069) Add test coverage; account for no artifacts on CI (@bf4)
|
|
||||||
- [#1103](https://github.com/rails-api/active_model_serializers/pull/1103) Move `id` and `json_api_type` methods from `Serializer` to `JsonApi`. (@beauby)
|
|
||||||
- [#1106](https://github.com/rails-api/active_model_serializers/pull/1106) Add Style enforcer (via Rubocop) (@bf4)
|
|
||||||
- [#1079](https://github.com/rails-api/active_model_serializers/pull/1079) Add ArraySerializer#object like Serializer (@bf4)
|
|
||||||
- [#1096](https://github.com/rails-api/active_model_serializers/pull/1096) Fix definition of serializer attributes with multiple calls to `attri… (@beauby)
|
|
||||||
- [#1105](https://github.com/rails-api/active_model_serializers/pull/1105) Add ActiveRecord-backed fixtures. (@beauby)
|
|
||||||
- [#1108](https://github.com/rails-api/active_model_serializers/pull/1108) Better lint (@bf4)
|
|
||||||
- [#1102](https://github.com/rails-api/active_model_serializers/pull/1102) Remove remains of `embed` option. (@beauby)
|
|
||||||
- [#1090](https://github.com/rails-api/active_model_serializers/pull/1090) Clarify AMS dependencies (@bf4)
|
|
||||||
- [#1081](https://github.com/rails-api/active_model_serializers/pull/1081) Add configuration option to set resource type to singular/plural (@beauby)
|
|
||||||
- [#1067](https://github.com/rails-api/active_model_serializers/pull/1067) Fix warnings (@bf4)
|
|
||||||
- [#1066](https://github.com/rails-api/active_model_serializers/pull/1066) Adding appveyor to the project (@joaomdmoura, @Eric-Guo, @bf4)
|
|
||||||
- [#1071](https://github.com/rails-api/active_model_serializers/pull/1071) Make testing suite running and pass in Windows (@Eric-Guo, @bf4)
|
|
||||||
- [#1041](https://github.com/rails-api/active_model_serializers/pull/1041) Adding pagination links (@bacarini)
|
|
||||||
* adds support for `pagination links` at top level of JsonApi adapter
|
|
||||||
- [#1063](https://github.com/rails-api/active_model_serializers/pull/1063) Lead by example: lint PORO model (@bf4)
|
|
||||||
- [#1](https://github.com/rails-api/active_model_serializers/pull/1) Test caller line parsing and digesting (@bf4)
|
|
||||||
- [#1048](https://github.com/rails-api/active_model_serializers/pull/1048) Let FlattenJson adapter decide it doesn't include meta (@bf4)
|
|
||||||
- [#1060](https://github.com/rails-api/active_model_serializers/pull/1060) Update fragment cache to support namespaced objects (@aaronlerch)
|
|
||||||
- [#1052](https://github.com/rails-api/active_model_serializers/pull/1052) Use underscored json_root when serializing a collection (@whatthewhat)
|
|
||||||
- [#1051](https://github.com/rails-api/active_model_serializers/pull/1051) Fix some invalid JSON in docs (@tjschuck)
|
|
||||||
- [#1049](https://github.com/rails-api/active_model_serializers/pull/1049) Fix incorrect s/options = {}/options ||= {} (@bf4)
|
|
||||||
- [#1037](https://github.com/rails-api/active_model_serializers/pull/1037) allow for type attribute (@lanej)
|
|
||||||
- [#1034](https://github.com/rails-api/active_model_serializers/pull/1034) allow id attribute to be overriden (@lanej)
|
|
||||||
- [#1035](https://github.com/rails-api/active_model_serializers/pull/1035) Fixed Comments highlight (@artLopez)
|
|
||||||
- [#1031](https://github.com/rails-api/active_model_serializers/pull/1031) Disallow to define multiple associations at once (@bolshakov)
|
|
||||||
- [#1032](https://github.com/rails-api/active_model_serializers/pull/1032) Wrap railtie requirement with rescue (@elliotlarson)
|
|
||||||
- [#1026](https://github.com/rails-api/active_model_serializers/pull/1026) Bump Version Number to 0.10.0.rc2 (@jfelchner)
|
|
||||||
- [#985](https://github.com/rails-api/active_model_serializers/pull/985) Associations implementation refactoring (@bolshakov)
|
|
||||||
- [#954](https://github.com/rails-api/active_model_serializers/pull/954) Encapsulate serialization in ActiveModel::SerializableResource (@bf4)
|
|
||||||
- [#972](https://github.com/rails-api/active_model_serializers/pull/972) Capture app warnings on test run (@bf4)
|
|
||||||
- [#1019](https://github.com/rails-api/active_model_serializers/pull/1019) Improve README.md (@baojjeu)
|
|
||||||
- [#998](https://github.com/rails-api/active_model_serializers/pull/998) Changing root to model class name (@joaomdmoura)
|
|
||||||
- [#1006](https://github.com/rails-api/active_model_serializers/pull/1006) Fix adapter inflection bug for api -> API (@bf4)
|
|
||||||
- [#1016](https://github.com/rails-api/active_model_serializers/pull/1016) require rails/railtie before subclassing Rails::Railtie (@bf4)
|
|
||||||
- [#1013](https://github.com/rails-api/active_model_serializers/pull/1013) Root option with empty array support (@vyrak, @mareczek)
|
|
||||||
- [#994](https://github.com/rails-api/active_model_serializers/pull/994) Starting Docs structure (@joaomdmoura)
|
|
||||||
- [#1007](https://github.com/rails-api/active_model_serializers/pull/1007) Bug fix for ArraySerializer json_key (@jiajiawang)
|
|
||||||
- [#1003](https://github.com/rails-api/active_model_serializers/pull/1003) Fix transient test failures (@Rodrigora)
|
|
||||||
- [#996](https://github.com/rails-api/active_model_serializers/pull/996) Add linter for serializable resource (@bf4)
|
|
||||||
- [#990](https://github.com/rails-api/active_model_serializers/pull/990) Adding json-api meta test (@joaomdmoura)
|
|
||||||
- [#984](https://github.com/rails-api/active_model_serializers/pull/984) Add option "key" to serializer associations (@Rodrigora)
|
|
||||||
- [#982](https://github.com/rails-api/active_model_serializers/pull/982) Fix typo (@bf4)
|
|
||||||
- [#981](https://github.com/rails-api/active_model_serializers/pull/981) Remove unused PORO#to_param (@bf4)
|
|
||||||
- [#978](https://github.com/rails-api/active_model_serializers/pull/978) fix generators template bug (@regonn)
|
|
||||||
- [#975](https://github.com/rails-api/active_model_serializers/pull/975) Fixes virtual value not being used (@GriffinHeart)
|
|
||||||
- [#970](https://github.com/rails-api/active_model_serializers/pull/970) Fix transient tests failures (@Rodrigora)
|
|
||||||
- [#962](https://github.com/rails-api/active_model_serializers/pull/962) Rendering objects that doesn't have serializers (@bf4, @joaomdmoura, @JustinAiken)
|
|
||||||
- [#939](https://github.com/rails-api/active_model_serializers/pull/939) Use a more precise generated cache key (@aaronlerch)
|
|
||||||
- [#971](https://github.com/rails-api/active_model_serializers/pull/971) Restore has_one to generator (@bf4)
|
|
||||||
- [#965](https://github.com/rails-api/active_model_serializers/pull/965) options fedault valueserializable_hash and as_json (@bf4)
|
|
||||||
- [#959](https://github.com/rails-api/active_model_serializers/pull/959) TYPO on README.md (@kangkyu)
|
|
||||||
|
|
||||||
### [v0.10.0.rc2 (2015-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc1...v0.10.0.rc2)
|
|
||||||
- [#958](https://github.com/rails-api/active_model_serializers/pull/958) Splitting json adapter into two (@joaomdmoura)
|
|
||||||
* adds FlattenJSON as default adapter
|
|
||||||
- [#953](https://github.com/rails-api/active_model_serializers/pull/953) use model name to determine the type (@lsylvester)
|
|
||||||
* uses model name to determine the type
|
|
||||||
- [#949](https://github.com/rails-api/active_model_serializers/pull/949) Don't pass serializer option to associated serializers (@bf4, @edwardloveall)
|
|
||||||
- [#902](https://github.com/rails-api/active_model_serializers/pull/902) Added serializer file digest to the cache_key (@cristianbica)
|
|
||||||
- [#948](https://github.com/rails-api/active_model_serializers/pull/948) AMS supports JSONAPI 1.0 instead of RC4 (@SeyZ)
|
|
||||||
- [#936](https://github.com/rails-api/active_model_serializers/pull/936) Include meta when using json adapter with custom root (@chrisbranson)
|
|
||||||
- [#942](https://github.com/rails-api/active_model_serializers/pull/942) Small code styling issue (@thiagofm)
|
|
||||||
- [#930](https://github.com/rails-api/active_model_serializers/pull/930) Reverting PR #909 (@joaomdmoura)
|
|
||||||
- [#924](https://github.com/rails-api/active_model_serializers/pull/924) Avoid unecessary calls to attribute methods when fragment caching (@navinpeiris)
|
|
||||||
- [#925](https://github.com/rails-api/active_model_serializers/pull/925) Updates JSON API Adapter to generate RC4 schema (@benedikt)
|
|
||||||
* adds JSON API support 1.0
|
|
||||||
- [#918](https://github.com/rails-api/active_model_serializers/pull/918) Adding rescue_with_handler to clear state (@ryansch)
|
|
||||||
- [#909](https://github.com/rails-api/active_model_serializers/pull/909) Defining Json-API Adapter as Default (@joaomdmoura)
|
|
||||||
* remove root key option and split JSON adapter
|
|
||||||
- [#914](https://github.com/rails-api/active_model_serializers/pull/914) Prevent possible duplicated attributes in serializer (@groyoh)
|
|
||||||
- [#880](https://github.com/rails-api/active_model_serializers/pull/880) Inabling subclasses serializers to inherit attributes (@groyoh)
|
|
||||||
- [#913](https://github.com/rails-api/active_model_serializers/pull/913) Avoiding the serializer option when instantiating a new one for ArraySerializer Fixed #911 (@groyoh)
|
|
||||||
- [#897](https://github.com/rails-api/active_model_serializers/pull/897) Allow to define custom serializer for given class (@imanel)
|
|
||||||
- [#892](https://github.com/rails-api/active_model_serializers/pull/892) Fixed a bug that appeared when json adapter serialize a nil association (@groyoh)
|
|
||||||
- [#895](https://github.com/rails-api/active_model_serializers/pull/895) Adding a test to cover 'meta' and 'meta_key' attr_readers (@adomokos)
|
|
||||||
- [#894](https://github.com/rails-api/active_model_serializers/pull/894) Fixing typos in README.md (@adomokos)
|
|
||||||
- [#888](https://github.com/rails-api/active_model_serializers/pull/888) Changed duplicated test name in action controller test (@groyoh)
|
|
||||||
- [#890](https://github.com/rails-api/active_model_serializers/pull/890) Remove unused method `def_serializer` (@JustinAiken)
|
|
||||||
- [#887](https://github.com/rails-api/active_model_serializers/pull/887) Fixing tests on JRuby (@joaomdmoura)
|
|
||||||
- [#885](https://github.com/rails-api/active_model_serializers/pull/885) Updates rails versions for test and dev (@tonyta)
|
|
||||||
|
|
||||||
### [v0.10.0.rc1 (2015-04-22)](https://github.com/rails-api/active_model_serializers/compare/86fc7d7227f3ce538fcb28c1e8c7069ce311f0e1...v0.10.0.rc1)
|
|
||||||
- [#810](https://github.com/rails-api/active_model_serializers/pull/810) Adding Fragment Cache to AMS (@joaomdmoura)
|
|
||||||
* adds fragment cache support
|
|
||||||
- [#868](https://github.com/rails-api/active_model_serializers/pull/868) Fixed a bug that appears when a nil association is included (@groyoh)
|
|
||||||
- [#861](https://github.com/rails-api/active_model_serializers/pull/861) README: Add emphasis to single-word difference (@machty)
|
|
||||||
- [#858](https://github.com/rails-api/active_model_serializers/pull/858) Included resource fixes (@mateomurphy)
|
|
||||||
- [#853](https://github.com/rails-api/active_model_serializers/pull/853) RC3 Updates for JSON API (@mateomurphy)
|
|
||||||
- [#852](https://github.com/rails-api/active_model_serializers/pull/852) Fix options merge order in `each_association` (@mateomurphy)
|
|
||||||
- [#850](https://github.com/rails-api/active_model_serializers/pull/850) Use association value for determining serializer used (@mateomurphy)
|
|
||||||
- [#843](https://github.com/rails-api/active_model_serializers/pull/843) Remove the mailing list from the README (@JoshSmith)
|
|
||||||
- [#842](https://github.com/rails-api/active_model_serializers/pull/842) Add notes on how you can help to contributing documentation (@JoshSmith)
|
|
||||||
- [#833](https://github.com/rails-api/active_model_serializers/pull/833) Cache serializers for class (@lsylvester)
|
|
||||||
- [#837](https://github.com/rails-api/active_model_serializers/pull/837) Store options in array serializers (@kurko)
|
|
||||||
- [#836](https://github.com/rails-api/active_model_serializers/pull/836) Makes passed in options accessible inside serializers (@kurko)
|
|
||||||
- [#773](https://github.com/rails-api/active_model_serializers/pull/773) Make json api adapter 'include' option accept an array (@sweatypitts)
|
|
||||||
- [#830](https://github.com/rails-api/active_model_serializers/pull/830) Add contributing readme (@JoshSmith)
|
|
||||||
- [#811](https://github.com/rails-api/active_model_serializers/pull/811) Reimplement serialization scope and scope_name (@mateomurphy)
|
|
||||||
- [#725](https://github.com/rails-api/active_model_serializers/pull/725) Support has_one to be compatible with 0.8.x (@ggordon)
|
|
||||||
* adds `has_one` attribute for backwards compatibility
|
|
||||||
- [#822](https://github.com/rails-api/active_model_serializers/pull/822) Replace has_one with attribute in template (@bf4)
|
|
||||||
- [#821](https://github.com/rails-api/active_model_serializers/pull/821) Fix explicit serializer for associations (@wjordan)
|
|
||||||
- [#798](https://github.com/rails-api/active_model_serializers/pull/798) Fix lost test `test_include_multiple_posts_and_linked` (@donbobka)
|
|
||||||
- [#807](https://github.com/rails-api/active_model_serializers/pull/807) Add Overriding attribute methods section to README. (@alexstophel)
|
|
||||||
- [#693](https://github.com/rails-api/active_model_serializers/pull/693) Cache Support at AMS 0.10.0 (@joaomdmoura)
|
|
||||||
* adds cache support to attributes and associations.
|
|
||||||
- [#792](https://github.com/rails-api/active_model_serializers/pull/792) Association overrides (@kurko)
|
|
||||||
* adds method to override association
|
|
||||||
- [#794](https://github.com/rails-api/active_model_serializers/pull/794) add to_param for correct URL generation (@carlesjove)
|
|
||||||
|
|
||||||
### v0.10.0-pre
|
|
||||||
|
|
||||||
- [Introduce Adapter](https://github.com/rails-api/active_model_serializers/commit/f00fe5595ddf741dc26127ed8fe81adad833ead5)
|
|
||||||
- Prefer `ActiveModel::Serializer` to `ActiveModelSerializers`:
|
|
||||||
- [Namespace](https://github.com/rails-api/active_model_serializers/commit/729a823868e8c7ac86c653fcc7100ee511e08cb6#diff-fe7aa2941c19a41ccea6e52940d84016).
|
|
||||||
- [README](https://github.com/rails-api/active_model_serializers/commit/4a2d9853ba7486acc1747752982aa5650e7fd6e9).
|
|
||||||
|
|
||||||
## 0.09.x
|
|
||||||
|
|
||||||
### v0.9.3 (2015/01/21 20:29 +00:00)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- [#774](https://github.com/rails-api/active_model_serializers/pull/774) Fix nested include attributes (@nhocki)
|
|
||||||
- [#771](https://github.com/rails-api/active_model_serializers/pull/771) Make linked resource type names consistent with root names (@sweatypitts)
|
|
||||||
- [#696](https://github.com/rails-api/active_model_serializers/pull/696) Explicitly set serializer for associations (@ggordon)
|
|
||||||
- [#700](https://github.com/rails-api/active_model_serializers/pull/700) sparse fieldsets (@arenoir)
|
|
||||||
- [#768](https://github.com/rails-api/active_model_serializers/pull/768) Adds support for `meta` and `meta_key` attribute (@kurko)
|
|
||||||
|
|
||||||
### v0.9.1 (2014/12/04 11:54 +00:00)
|
|
||||||
- [#707](https://github.com/rails-api/active_model_serializers/pull/707) A Friendly Note on Which AMS Version to Use (@jherdman)
|
|
||||||
- [#730](https://github.com/rails-api/active_model_serializers/pull/730) Fixes nested has_many links in JSONAPI (@kurko)
|
|
||||||
- [#718](https://github.com/rails-api/active_model_serializers/pull/718) Allow overriding the adapter with render option (@ggordon)
|
|
||||||
- [#720](https://github.com/rails-api/active_model_serializers/pull/720) Rename attribute with :key (0.8.x compatibility) (@ggordon)
|
|
||||||
- [#728](https://github.com/rails-api/active_model_serializers/pull/728) Use type as key for linked resources (@kurko)
|
|
||||||
- [#729](https://github.com/rails-api/active_model_serializers/pull/729) Use the new beta build env on Travis (@joshk)
|
|
||||||
- [#703](https://github.com/rails-api/active_model_serializers/pull/703) Support serializer and each_serializer options in renderer (@ggordon, @mieko)
|
|
||||||
- [#727](https://github.com/rails-api/active_model_serializers/pull/727) Includes links inside of linked resources (@kurko)
|
|
||||||
- [#726](https://github.com/rails-api/active_model_serializers/pull/726) Bugfix: include nested has_many associations (@kurko)
|
|
||||||
- [#722](https://github.com/rails-api/active_model_serializers/pull/722) Fix infinite recursion (@ggordon)
|
|
||||||
- [#1](https://github.com/rails-api/active_model_serializers/pull/1) Allow for the implicit use of ArraySerializer when :each_serializer is specified (@mieko)
|
|
||||||
- [#692](https://github.com/rails-api/active_model_serializers/pull/692) Include 'linked' member for json-api collections (@ggordon)
|
|
||||||
- [#714](https://github.com/rails-api/active_model_serializers/pull/714) Define as_json instead of to_json (@guilleiguaran)
|
|
||||||
- [#710](https://github.com/rails-api/active_model_serializers/pull/710) JSON-API: Don't include linked section if associations are empty (@guilleiguaran)
|
|
||||||
- [#711](https://github.com/rails-api/active_model_serializers/pull/711) Fixes rbx gems bundling on TravisCI (@kurko)
|
|
||||||
- [#709](https://github.com/rails-api/active_model_serializers/pull/709) Add type key when association name is different than object type (@guilleiguaran)
|
|
||||||
- [#708](https://github.com/rails-api/active_model_serializers/pull/708) Handle correctly null associations (@guilleiguaran)
|
|
||||||
- [#691](https://github.com/rails-api/active_model_serializers/pull/691) Fix embed option for associations (@jacob-s-son)
|
|
||||||
- [#689](https://github.com/rails-api/active_model_serializers/pull/689) Fix support for custom root in JSON-API adapter (@guilleiguaran)
|
|
||||||
- [#685](https://github.com/rails-api/active_model_serializers/pull/685) Serialize ids as strings in JSON-API adapter (@guilleiguaran)
|
|
||||||
- [#684](https://github.com/rails-api/active_model_serializers/pull/684) Refactor adapters to implement support for array serialization (@guilleiguaran)
|
|
||||||
- [#682](https://github.com/rails-api/active_model_serializers/pull/682) Include root by default in JSON-API serializers (@guilleiguaran)
|
|
||||||
- [#625](https://github.com/rails-api/active_model_serializers/pull/625) Add DSL for urls (@JordanFaust)
|
|
||||||
- [#677](https://github.com/rails-api/active_model_serializers/pull/677) Add support for embed: :ids option for in associations (@guilleiguaran)
|
|
||||||
- [#681](https://github.com/rails-api/active_model_serializers/pull/681) Check superclasses for Serializers (@quainjn)
|
|
||||||
- [#680](https://github.com/rails-api/active_model_serializers/pull/680) Add support for root keys (@NullVoxPopuli)
|
|
||||||
- [#675](https://github.com/rails-api/active_model_serializers/pull/675) Support Rails 4.2.0 (@tricknotes)
|
|
||||||
- [#667](https://github.com/rails-api/active_model_serializers/pull/667) Require only activemodel instead of full rails (@guilleiguaran)
|
|
||||||
- [#653](https://github.com/rails-api/active_model_serializers/pull/653) Add "_test" suffix to JsonApi::HasManyTest filename. (@alexgenco)
|
|
||||||
- [#631](https://github.com/rails-api/active_model_serializers/pull/631) Update build badge URL (@craiglittle)
|
|
||||||
|
|
||||||
### 0.9.0.alpha1 - January 7, 2014
|
|
||||||
|
|
||||||
### 0.9.0.pre
|
|
||||||
|
|
||||||
* The following methods were removed
|
|
||||||
- Model#active\_model\_serializer
|
|
||||||
- Serializer#include!
|
|
||||||
- Serializer#include?
|
|
||||||
- Serializer#attr\_disabled=
|
|
||||||
- Serializer#cache
|
|
||||||
- Serializer#perform\_caching
|
|
||||||
- Serializer#schema (needs more discussion)
|
|
||||||
- Serializer#attribute
|
|
||||||
- Serializer#include\_#{name}? (filter method added)
|
|
||||||
- Serializer#attributes (took a hash)
|
|
||||||
|
|
||||||
* The following things were added
|
|
||||||
- Serializer#filter method
|
|
||||||
- CONFIG object
|
|
||||||
|
|
||||||
* Remove support for ruby 1.8 versions.
|
|
||||||
|
|
||||||
* Require rails >= 3.2.
|
|
||||||
|
|
||||||
* Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces.
|
|
||||||
|
|
||||||
* Added a "prefix" option in case you want to use a different version of serializer.
|
|
||||||
|
|
||||||
* Serializers default namespace can be set in `default_serializer_options` and inherited by associations.
|
|
||||||
|
|
||||||
* [Beginning of rewrite: c65d387705ec534db171712671ba7fcda4f49f68](https://github.com/rails-api/active_model_serializers/commit/c65d387705ec534db171712671ba7fcda4f49f68)
|
|
||||||
|
|
||||||
## 0.08.x
|
|
||||||
|
|
||||||
### v0.8.3 (2014/12/10 14:45 +00:00)
|
|
||||||
- [#753](https://github.com/rails-api/active_model_serializers/pull/753) Test against Ruby 2.2 on Travis CI (@tricknotes)
|
|
||||||
- [#745](https://github.com/rails-api/active_model_serializers/pull/745) Missing a word (@jockee)
|
|
||||||
|
|
||||||
### v0.8.2 (2014/09/01 21:00 +00:00)
|
|
||||||
- [#612](https://github.com/rails-api/active_model_serializers/pull/612) Feature/adapter (@bolshakov)
|
|
||||||
* adds adapters pattern
|
|
||||||
- [#615](https://github.com/rails-api/active_model_serializers/pull/615) Rails does not support const_defined? in development mode (@tpitale)
|
|
||||||
- [#613](https://github.com/rails-api/active_model_serializers/pull/613) README: typo fix on attributes (@spk)
|
|
||||||
- [#614](https://github.com/rails-api/active_model_serializers/pull/614) Fix rails 4.0.x build. (@arthurnn)
|
|
||||||
- [#610](https://github.com/rails-api/active_model_serializers/pull/610) ArraySerializer (@bolshakov)
|
|
||||||
- [#607](https://github.com/rails-api/active_model_serializers/pull/607) ruby syntax highlights (@zigomir)
|
|
||||||
- [#602](https://github.com/rails-api/active_model_serializers/pull/602) Add DSL for associations (@JordanFaust)
|
|
||||||
|
|
||||||
### 0.8.1 (May 6, 2013)
|
|
||||||
|
|
||||||
* Fix bug whereby a serializer using 'options' would blow up.
|
|
||||||
|
|
||||||
### 0.8.0 (May 5, 2013)
|
|
||||||
|
|
||||||
* Attributes can now have optional types.
|
|
||||||
|
|
||||||
* A new DefaultSerializer ensures that POROs behave the same way as ActiveModels.
|
|
||||||
|
|
||||||
* If you wish to override ActiveRecord::Base#to_Json, you can now require
|
|
||||||
'active_record/serializer_override'. We don't recommend you do this, but
|
|
||||||
many users do, so we've left it optional.
|
|
||||||
|
|
||||||
* Fixed a bug where ActionController wouldn't always have MimeResponds.
|
|
||||||
|
|
||||||
* An optinal caching feature allows you to cache JSON & hashes that AMS uses.
|
|
||||||
Adding 'cached true' to your Serializers will turn on this cache.
|
|
||||||
|
|
||||||
* URL helpers used inside of Engines now work properly.
|
|
||||||
|
|
||||||
* Serializers now can filter attributes with `only` and `except`:
|
|
||||||
|
|
||||||
```
|
|
||||||
UserSerializer.new(user, only: [:first_name, :last_name])
|
|
||||||
UserSerializer.new(user, except: :first_name)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Basic Mongoid support. We now include our mixins in the right place.
|
|
||||||
|
|
||||||
* On Ruby 1.8, we now generate an `id` method that properly serializes `id`
|
|
||||||
columns. See issue #127 for more.
|
|
||||||
|
|
||||||
* Add an alias for `scope` method to be the name of the context. By default
|
|
||||||
this is `current_user`. The name is automatically set when using
|
|
||||||
`serialization_scope` in the controller.
|
|
||||||
|
|
||||||
* Pass through serialization options (such as `:include`) when a model
|
|
||||||
has no serializer defined.
|
|
||||||
|
|
||||||
## [0.7.0 (March 6, 2013)](https://github.com/rails-api/active_model_serializers/commit/fabdc621ff97fbeca317f6301973dd4564b9e695)
|
|
||||||
|
|
||||||
* ```embed_key``` option to allow embedding by attributes other than IDs
|
|
||||||
* Fix rendering nil with custom serializer
|
|
||||||
* Fix global ```self.root = false```
|
|
||||||
* Add support for specifying the serializer for an association as a String
|
|
||||||
* Able to specify keys on the attributes method
|
|
||||||
* Serializer Reloading via ActiveSupport::DescendantsTracker
|
|
||||||
* Reduce double map to once; Fixes datamapper eager loading.
|
|
||||||
|
|
||||||
## 0.6.0 (October 22, 2012)
|
|
||||||
|
|
||||||
* Serialize sets properly
|
|
||||||
* Add root option to ArraySerializer
|
|
||||||
* Support polymorphic associations
|
|
||||||
* Support :each_serializer in ArraySerializer
|
|
||||||
* Add `scope` method to easily access the scope in the serializer
|
|
||||||
* Fix regression with Rails 3.2.6; add Rails 4 support
|
|
||||||
* Allow serialization_scope to be disabled with serialization_scope nil
|
|
||||||
* Array serializer should support pure ruby objects besides serializers
|
|
||||||
|
|
||||||
## 0.05.x
|
|
||||||
|
|
||||||
### [0.5.2 (June 5, 2012)](https://github.com/rails-api/active_model_serializers/commit/615afd125c260432d456dc8be845867cf87ea118#diff-0c5c12f311d3b54734fff06069efd2ac)
|
|
||||||
|
|
||||||
### [0.5.1 (May 23, 2012)](https://github.com/rails-api/active_model_serializers/commit/00194ec0e41831802fcbf893a34c0bb0853ebe14#diff-0c5c12f311d3b54734fff06069efd2ac)
|
|
||||||
|
|
||||||
### [0.5.0 (May 16, 2012)](https://github.com/rails-api/active_model_serializers/commit/33d4842dcd35c7167b0b33fc0abcf00fb2c92286)
|
|
||||||
|
|
||||||
* First tagged version
|
|
||||||
* Changes generators to always generate an ApplicationSerializer
|
|
||||||
|
|
||||||
## [0.1.0 (December 21, 2011)](https://github.com/rails-api/active_model_serializers/commit/1e0c9ef93b96c640381575dcd30be07ac946818b)
|
|
||||||
|
|
||||||
## First Commit as [Rails Serializers 0.0.1](https://github.com/rails-api/active_model_serializers/commit/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e)
|
|
||||||
(December 1, 2011).
|
|
||||||
|
|
||||||
## Prehistory
|
|
||||||
|
|
||||||
- [Changing Serialization/Serializers namespace to `Serializable` (November 30, 2011)](https://github.com/rails/rails/commit/8896b4fdc8a543157cdf4dfc378607ebf6c10ab0)
|
|
||||||
- [Merge branch 'serializers'. This implements the ActiveModel::Serializer object. Includes code, tests, generators and guides. From José and Yehuda with love.](https://github.com/rails/rails/commit/fcacc6986ab60f1fb2e423a73bf47c7abd7b191d)
|
|
||||||
- But [was reverted](https://github.com/rails/rails/commit/5b2eb64ceb08cd005dc06b721935de5853971473).
|
|
||||||
'[Revert the serializers API as other alternatives are now also under discussion](https://github.com/rails/rails/commit/0a4035b12a6c59253cb60f9e3456513c6a6a9d33)'.
|
|
||||||
- [Proposed Implementation to Rails 3.2 by @wycats and @josevalim (November 25, 2011)](https://github.com/rails/rails/pull/3753)
|
|
||||||
- [Creation of `ActionController::Serialization`, initial serializer
|
|
||||||
support (September, 26 2011)](https://github.com/rails/rails/commit/8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe).
|
|
||||||
- [Docs and CHANGELOG](https://github.com/rails/rails/commit/696d01f7f4a8ed787924a41cce6df836cd73c46f)
|
|
||||||
- [Deprecation of ActiveModel::Serialization to ActiveModel::Serializable](https://github.com/rails/rails/blob/696d01f7f4a8ed787924a41cce6df836cd73c46f/activemodel/lib/active_model/serialization.rb)
|
|
||||||
- [Creation of `ActiveModel::Serialization` from `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/c6bc8e662614be711f45a8d4b231d5f993b024a7#diff-d029b9768d8df0407a35804a468e3ae5)
|
|
||||||
- [Integration of `ActiveModel::Serializer` into `ActiveRecord::Serialization`](https://github.com/rails/rails/commit/783db25e0c640c1588732967a87d65c10fddc08e)
|
|
||||||
- [Creation of `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/d2b78b3594b9cc9870e6a6ebfeb2e56d00e6ddb8#diff-80d5beeced9bdc24ca2b04a201543bdd)
|
|
||||||
- [Creation of `ActiveModel::Serializers::JSON` in Rails (2009)](https://github.com/rails/rails/commit/fbdf706fffbfb17731a1f459203d242414ef5086)
|
|
||||||
|
|||||||
@ -4,13 +4,7 @@ Before opening an issue, try the following:
|
|||||||
|
|
||||||
##### Consult the documentation
|
##### Consult the documentation
|
||||||
|
|
||||||
See if your issue can be resolved by information in the documentation.
|
See if your issue can be resolved by information in the [documentation](README.md).
|
||||||
|
|
||||||
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs)
|
|
||||||
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0)
|
|
||||||
- [Guides](docs)
|
|
||||||
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
|
||||||
- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
|
|
||||||
|
|
||||||
##### Check for an existing issue
|
##### Check for an existing issue
|
||||||
|
|
||||||
@ -43,7 +37,9 @@ for discussion or add your comments to existing ones.
|
|||||||
We also gladly welcome pull requests. When preparing to work on pull request,
|
We also gladly welcome pull requests. When preparing to work on pull request,
|
||||||
please adhere to these standards:
|
please adhere to these standards:
|
||||||
|
|
||||||
- Base work on the master branch unless fixing an issue with
|
- Base work on the relevant branch:
|
||||||
|
[0.10-stable](https://github.com/rails-api/active_model_serializers/tree/0-10-stable)
|
||||||
|
or
|
||||||
[0.9-stable](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
[0.9-stable](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
||||||
or
|
or
|
||||||
[0.8-stable](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
|
[0.8-stable](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
|
||||||
@ -52,10 +48,10 @@ please adhere to these standards:
|
|||||||
- Note any specific areas that should be reviewed.
|
- Note any specific areas that should be reviewed.
|
||||||
- Include tests.
|
- Include tests.
|
||||||
- The test suite must pass on [supported Ruby versions](.travis.yml)
|
- The test suite must pass on [supported Ruby versions](.travis.yml)
|
||||||
- Include updates to the [documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs)
|
- Include updates to the [documentation](docs)
|
||||||
where applicable.
|
where applicable.
|
||||||
- Update the
|
- Update the
|
||||||
[CHANGELOG](https://github.com/rails-api/active_model_serializers/blob/master/CHANGELOG.md)
|
[CHANGELOG](CHANGELOG.md)
|
||||||
to the appropriate sections with a brief description of the changes.
|
to the appropriate sections with a brief description of the changes.
|
||||||
- Do not change the VERSION file.
|
- Do not change the VERSION file.
|
||||||
|
|
||||||
@ -102,4 +98,3 @@ fi
|
|||||||
unset RAILS_VERSION
|
unset RAILS_VERSION
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
56
Gemfile
56
Gemfile
@ -1,56 +0,0 @@
|
|||||||
source 'https://rubygems.org'
|
|
||||||
#
|
|
||||||
# Add a Gemfile.local to locally bundle gems outside of version control
|
|
||||||
local_gemfile = File.join(File.expand_path('..', __FILE__), 'Gemfile.local')
|
|
||||||
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'
|
|
||||||
|
|
||||||
if version == 'master'
|
|
||||||
gem 'rack', github: 'rack/rack'
|
|
||||||
gem 'arel', github: 'rails/arel'
|
|
||||||
git 'https://github.com/rails/rails.git' do
|
|
||||||
gem 'railties'
|
|
||||||
gem 'activesupport'
|
|
||||||
gem 'activemodel'
|
|
||||||
gem 'actionpack'
|
|
||||||
gem 'activerecord', group: :test
|
|
||||||
# Rails 5
|
|
||||||
gem 'actionview'
|
|
||||||
end
|
|
||||||
else
|
|
||||||
gem_version = "~> #{version}.0"
|
|
||||||
gem 'railties', gem_version
|
|
||||||
gem 'activesupport', gem_version
|
|
||||||
gem 'activemodel', gem_version
|
|
||||||
gem 'actionpack', gem_version
|
|
||||||
gem 'activerecord', gem_version, group: :test
|
|
||||||
end
|
|
||||||
|
|
||||||
# https://github.com/bundler/bundler/blob/89a8778c19269561926cea172acdcda241d26d23/lib/bundler/dependency.rb#L30-L54
|
|
||||||
@windows_platforms = [:mswin, :mingw, :x64_mingw]
|
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
|
||||||
gem 'tzinfo-data', platforms: (@windows_platforms + [:jruby])
|
|
||||||
|
|
||||||
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
|
|
||||||
gem 'codeclimate-test-reporter', require: false
|
|
||||||
gem 'm', '~> 1.5'
|
|
||||||
gem 'pry', '~> 0.10'
|
|
||||||
gem 'pry-byebug', '~> 3.4', platform: :ruby
|
|
||||||
end
|
|
||||||
|
|
||||||
group :development, :test do
|
|
||||||
gem 'rubocop', '~> 0.40.0', require: false
|
|
||||||
gem 'yard', require: false
|
|
||||||
end
|
|
||||||
268
README.md
268
README.md
@ -1,77 +1,11 @@
|
|||||||
# ActiveModelSerializers
|
# ActiveModelSerializers
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>Build Status</td>
|
|
||||||
<td>
|
|
||||||
<a href="https://travis-ci.org/rails-api/active_model_serializers"><img src="https://travis-ci.org/rails-api/active_model_serializers.svg?branch=master" alt="Build Status" ></a>
|
|
||||||
<a href="https://ci.appveyor.com/project/joaomdmoura/active-model-serializers/branch/master"><img src="https://ci.appveyor.com/api/projects/status/x6xdjydutm54gvyt/branch/master?svg=true" alt="Build status"></a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Code Quality</td>
|
|
||||||
<td>
|
|
||||||
<a href="https://codeclimate.com/github/rails-api/active_model_serializers"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/gpa.svg" alt="Code Quality"></a>
|
|
||||||
<a href="https://codebeat.co/projects/github-com-rails-api-active_model_serializers"><img src="https://codebeat.co/badges/a9ab35fa-8b5a-4680-9d4e-a81f9a55ebcd" alt="codebeat" ></a>
|
|
||||||
<a href="https://codeclimate.com/github/rails-api/active_model_serializers/coverage"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/coverage.svg" alt="Test Coverage"></a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Issue Stats</td>
|
|
||||||
<td>
|
|
||||||
<a href="https://github.com/rails-api/active_model_serializers/pulse/monthly">Pulse</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
ActiveModelSerializers brings convention over configuration to your JSON generation.
|
|
||||||
|
|
||||||
ActiveModelSerializers works through two components: **serializers** and **adapters**.
|
|
||||||
|
|
||||||
Serializers describe _which_ attributes and relationships should be serialized.
|
|
||||||
|
|
||||||
Adapters describe _how_ attributes and relationships should be serialized.
|
|
||||||
|
|
||||||
SerializableResource co-ordinates the resource, Adapter and Serializer to produce the
|
|
||||||
resource serialization. The serialization has the `#as_json`, `#to_json` and `#serializable_hash`
|
|
||||||
methods used by the Rails JSON Renderer. (SerializableResource actually delegates
|
|
||||||
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).
|
|
||||||
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`.
|
|
||||||
|
|
||||||
`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
|
## Installation
|
||||||
|
|
||||||
Add this line to your application's Gemfile:
|
|
||||||
|
|
||||||
```
|
|
||||||
gem 'active_model_serializers', '~> 0.10.0'
|
|
||||||
```
|
|
||||||
|
|
||||||
And then execute:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ bundle
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
See [Getting Started](docs/general/getting_started.md) for the nuts and bolts.
|
|
||||||
|
|
||||||
More information is available in the [Guides](docs) and
|
|
||||||
[High-level behavior](README.md#high-level-behavior).
|
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new)
|
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new)
|
||||||
@ -86,10 +20,11 @@ Thanks!
|
|||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
If you're reading this at https://github.com/rails-api/active_model_serializers you are
|
If you're reading this at https://github.com/rails-api/active_model_serializers you are
|
||||||
reading documentation for our `master`, which may include features that have not
|
reading documentation for our `master`, which is not yet released.
|
||||||
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)
|
Please see below for the documentation relevant to you.
|
||||||
|
|
||||||
|
- [0.10 (0-10-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-10-stable)
|
||||||
- [0.10.6 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.6)
|
- [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)
|
- [](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
|
||||||
- [Guides](docs)
|
- [Guides](docs)
|
||||||
@ -101,203 +36,8 @@ been released yet. Please see below for the documentation relevant to you.
|
|||||||
|
|
||||||
## High-level behavior
|
## High-level behavior
|
||||||
|
|
||||||
Choose an adapter from [adapters](lib/active_model_serializers/adapter):
|
|
||||||
|
|
||||||
``` ruby
|
|
||||||
ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
|
|
||||||
```
|
|
||||||
|
|
||||||
Given a [serializable model](lib/active_model/serializer/lint.rb):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# either
|
|
||||||
class SomeResource < ActiveRecord::Base
|
|
||||||
# columns: title, body
|
|
||||||
end
|
|
||||||
# or
|
|
||||||
class SomeResource < ActiveModelSerializers::Model
|
|
||||||
attributes :title, :body
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
And initialized as:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration')
|
|
||||||
```
|
|
||||||
|
|
||||||
Given a serializer for the serializable model:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class SomeSerializer < ActiveModel::Serializer
|
|
||||||
attribute :title, key: :name
|
|
||||||
attributes :body
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The model can be serialized as:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
options = {}
|
|
||||||
serialization = ActiveModelSerializers::SerializableResource.new(resource, options)
|
|
||||||
serialization.to_json
|
|
||||||
serialization.as_json
|
|
||||||
```
|
|
||||||
|
|
||||||
SerializableResource delegates to the adapter, which it builds as:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
adapter_options = {}
|
|
||||||
adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options)
|
|
||||||
adapter.to_json
|
|
||||||
adapter.as_json
|
|
||||||
adapter.serializable_hash
|
|
||||||
```
|
|
||||||
|
|
||||||
The adapter formats the serializer's attributes and associations (a.k.a. includes):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
serializer_options = {}
|
|
||||||
serializer = SomeSerializer.new(resource, serializer_options)
|
|
||||||
serializer.attributes
|
|
||||||
serializer.associations
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions,
|
|
||||||
please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or
|
|
||||||
[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md).
|
|
||||||
|
|
||||||
The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
|
|
||||||
|
|
||||||
### ActiveModel::Serializer
|
|
||||||
|
|
||||||
An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/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).
|
|
||||||
|
|
||||||
#### ActiveModel::CollectionSerializer
|
|
||||||
|
|
||||||
The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
|
|
||||||
and, if there is no serializer, primitives.
|
|
||||||
|
|
||||||
### ActiveModelSerializers::Adapter::Base
|
|
||||||
|
|
||||||
The **`ActiveModelSerializeres::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.
|
|
||||||
|
|
||||||
### ActiveModelSerializers::SerializableResource
|
|
||||||
|
|
||||||
The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter
|
|
||||||
to an object that responds to `to_json`, and `as_json`. It is used in the controller to
|
|
||||||
encapsulate the serialization resource when rendered. However, it can also be used on its own
|
|
||||||
to serialize a resource outside of a controller, as well.
|
|
||||||
|
|
||||||
### Primitive handling
|
|
||||||
|
|
||||||
Definitions: A primitive is usually a String or Array. There is no serializer
|
|
||||||
defined for them; they will be serialized when the resource is converted to JSON (`as_json` or
|
|
||||||
`to_json`). (The below also applies for any object with no serializer.)
|
|
||||||
|
|
||||||
- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
|
|
||||||
|
|
||||||
Internally, if no serializer can be found in the controller, the resource is not decorated by
|
|
||||||
ActiveModelSerializers.
|
|
||||||
|
|
||||||
- However, when a primitive value is an attribute or in a collection, it is not modified.
|
|
||||||
|
|
||||||
When serializing a collection and the collection serializer (CollectionSerializer) cannot
|
|
||||||
identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128).
|
|
||||||
For example, when caught by `Reflection#build_association`, and the association value is set directly:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
|
|
||||||
```
|
|
||||||
|
|
||||||
(which is called by the adapter as `serializer.associations(*)`.)
|
|
||||||
|
|
||||||
### How options are parsed
|
|
||||||
|
|
||||||
High-level overview:
|
|
||||||
|
|
||||||
- For a **collection**
|
|
||||||
- `:serializer` specifies the collection serializer and
|
|
||||||
- `: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).
|
|
||||||
The remaining options are serializer options.
|
|
||||||
|
|
||||||
Details:
|
|
||||||
|
|
||||||
1. **ActionController::Serialization**
|
|
||||||
1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)`
|
|
||||||
1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
|
|
||||||
The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
|
|
||||||
1. **ActiveModelSerializers::SerializableResource**
|
|
||||||
1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
|
|
||||||
- Where `serializer?` is `use_adapter? && !!(serializer)`
|
|
||||||
- Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil);
|
|
||||||
False when explicit adapter is falsy (nil or false)'
|
|
||||||
- Where `serializer`:
|
|
||||||
1. from explicit `:serializer` option, else
|
|
||||||
2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)`
|
|
||||||
1. A side-effect of checking `serializer` is:
|
|
||||||
- The `:serializer` option is removed from the serializer_opts hash
|
|
||||||
- If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option
|
|
||||||
1. The serializer and adapter are created as
|
|
||||||
1. `serializer_instance = serializer.new(resource, serializer_opts)`
|
|
||||||
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).
|
|
||||||
1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
|
|
||||||
resource as defined by the serializer.
|
|
||||||
|
|
||||||
(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)`
|
|
||||||
methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
|
|
||||||
to know about, but not part of ActiveModelSerializers.)
|
|
||||||
|
|
||||||
### What does a 'serializable resource' look like?
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
|
|
||||||
ActiveModelSerializers provides a
|
|
||||||
[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/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.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class MyModel < ActiveModelSerializers::Model
|
|
||||||
attributes :id, :name, :level
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
|
|
||||||
ActiveRecord::Base object or not.
|
|
||||||
|
|
||||||
Outside of the controller the rules are **exactly** the same as for records. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: MyModel.new(level: 'awesome'), adapter: :json
|
|
||||||
```
|
|
||||||
|
|
||||||
would be serialized the same as
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Semantic Versioning
|
## Semantic Versioning
|
||||||
|
|
||||||
This project adheres to [semver](http://semver.org/)
|
This project adheres to [semver](http://semver.org/)
|
||||||
|
|||||||
69
Rakefile
69
Rakefile
@ -3,72 +3,3 @@ begin
|
|||||||
rescue LoadError
|
rescue LoadError
|
||||||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||||
end
|
end
|
||||||
begin
|
|
||||||
require 'simplecov'
|
|
||||||
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
|
||||||
end
|
|
||||||
import('lib/tasks/rubocop.rake')
|
|
||||||
|
|
||||||
Bundler::GemHelper.install_tasks
|
|
||||||
|
|
||||||
require 'yard'
|
|
||||||
|
|
||||||
namespace :yard do
|
|
||||||
YARD::Rake::YardocTask.new(:doc) do |t|
|
|
||||||
t.stats_options = ['--list-undoc']
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'start a gem server'
|
|
||||||
task :server do
|
|
||||||
sh 'bundle exec yard server --gems'
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'use Graphviz to generate dot graph'
|
|
||||||
task :graph do
|
|
||||||
output_file = 'doc/erd.dot'
|
|
||||||
sh "bundle exec yard graph --protected --full --dependencies > #{output_file}"
|
|
||||||
puts 'open doc/erd.dot if you have graphviz installed'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rake/testtask'
|
|
||||||
|
|
||||||
Rake::TestTask.new(:test) do |t|
|
|
||||||
t.libs << 'lib'
|
|
||||||
t.libs << 'test'
|
|
||||||
t.pattern = 'test/**/*_test.rb'
|
|
||||||
t.ruby_opts = ['-r./test/test_helper.rb']
|
|
||||||
t.ruby_opts << ' -w' unless ENV['NO_WARN'] == 'true'
|
|
||||||
t.verbose = true
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Run isolated tests'
|
|
||||||
task isolated: ['test:isolated']
|
|
||||||
namespace :test do
|
|
||||||
task :isolated do
|
|
||||||
desc 'Run isolated tests for Railtie'
|
|
||||||
require 'shellwords'
|
|
||||||
dir = File.dirname(__FILE__)
|
|
||||||
dir = Shellwords.shellescape(dir)
|
|
||||||
isolated_test_files = FileList['test/**/*_test_isolated.rb']
|
|
||||||
# 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
|
|
||||||
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if ENV['RAILS_VERSION'].to_s > '4.0' && RUBY_ENGINE == 'ruby'
|
|
||||||
task default: [:isolated, :test, :rubocop]
|
|
||||||
else
|
|
||||||
task default: [:test, :rubocop]
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'CI test task'
|
|
||||||
task ci: [:default]
|
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
lib = File.expand_path('../lib', __FILE__)
|
|
||||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
||||||
require 'active_model/serializer/version'
|
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = 'active_model_serializers'
|
spec.name = 'active_model_serializers'
|
||||||
spec.version = ActiveModel::Serializer::VERSION
|
spec.version = "1.0.0-dev"
|
||||||
spec.platform = Gem::Platform::RUBY
|
spec.platform = Gem::Platform::RUBY
|
||||||
spec.authors = ['Steve Klabnik']
|
spec.authors = ['Steve Klabnik']
|
||||||
spec.email = ['steve@steveklabnik.com']
|
spec.email = ['steve@steveklabnik.com']
|
||||||
@ -20,44 +17,4 @@ Gem::Specification.new do |spec|
|
|||||||
spec.executables = []
|
spec.executables = []
|
||||||
|
|
||||||
spec.required_ruby_version = '>= 2.1'
|
spec.required_ruby_version = '>= 2.1'
|
||||||
|
|
||||||
rails_versions = ['>= 4.1', '< 6']
|
|
||||||
spec.add_runtime_dependency 'activemodel', rails_versions
|
|
||||||
# 'activesupport', rails_versions
|
|
||||||
# 'builder'
|
|
||||||
|
|
||||||
spec.add_runtime_dependency 'actionpack', rails_versions
|
|
||||||
# 'activesupport', rails_versions
|
|
||||||
# 'rack'
|
|
||||||
# 'rack-test', '~> 0.6.2'
|
|
||||||
|
|
||||||
spec.add_development_dependency 'railties', rails_versions
|
|
||||||
# 'activesupport', rails_versions
|
|
||||||
# 'actionpack', rails_versions
|
|
||||||
# 'rake', '>= 0.8.7'
|
|
||||||
|
|
||||||
# 'activesupport', rails_versions
|
|
||||||
# 'i18n,
|
|
||||||
# 'tzinfo'
|
|
||||||
# 'minitest'
|
|
||||||
# 'thread_safe'
|
|
||||||
|
|
||||||
spec.add_runtime_dependency 'jsonapi-renderer', ['>= 0.1.1.beta1', '< 0.2']
|
|
||||||
spec.add_runtime_dependency 'case_transform', '>= 0.2'
|
|
||||||
|
|
||||||
spec.add_development_dependency 'activerecord', rails_versions
|
|
||||||
# arel
|
|
||||||
# activesupport
|
|
||||||
# activemodel
|
|
||||||
|
|
||||||
# Soft dependency for pagination
|
|
||||||
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 'timecop', '~> 0.7'
|
|
||||||
spec.add_development_dependency 'grape', ['>= 0.13', '< 0.19.1']
|
|
||||||
spec.add_development_dependency 'json_schema'
|
|
||||||
spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0']
|
|
||||||
end
|
end
|
||||||
|
|||||||
21
appveyor.yml
21
appveyor.yml
@ -2,29 +2,10 @@ version: 1.0.{build}-{branch}
|
|||||||
|
|
||||||
skip_tags: true
|
skip_tags: true
|
||||||
|
|
||||||
environment:
|
|
||||||
JRUBY_OPTS: "--dev -J-Xmx1024M --debug"
|
|
||||||
matrix:
|
|
||||||
- ruby_version: "Ruby21"
|
|
||||||
- ruby_version: "Ruby21-x64"
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
- vendor/bundle
|
- 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
|
|
||||||
- bundle env
|
|
||||||
- bundle install --path=vendor/bundle --retry=3 --jobs=3
|
|
||||||
|
|
||||||
before_test:
|
|
||||||
- ruby -v
|
|
||||||
- gem -v
|
|
||||||
- bundle -v
|
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- bundle exec rake ci
|
- true
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
|||||||
171
bin/bench
171
bin/bench
@ -1,171 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# ActiveModelSerializers Benchmark driver
|
|
||||||
# Adapted from
|
|
||||||
# https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/driver.rb
|
|
||||||
require 'bundler'
|
|
||||||
Bundler.setup
|
|
||||||
require 'json'
|
|
||||||
require 'pathname'
|
|
||||||
require 'optparse'
|
|
||||||
require 'digest'
|
|
||||||
require 'pathname'
|
|
||||||
require 'shellwords'
|
|
||||||
require 'logger'
|
|
||||||
require 'English'
|
|
||||||
|
|
||||||
class BenchmarkDriver
|
|
||||||
ROOT = Pathname File.expand_path(File.join('..', '..'), __FILE__)
|
|
||||||
BASE = ENV.fetch('BASE') { ROOT.join('test', 'benchmark') }
|
|
||||||
ESCAPED_BASE = Shellwords.shellescape(BASE)
|
|
||||||
|
|
||||||
def self.benchmark(options)
|
|
||||||
new(options).run
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.parse_argv_and_run(argv = ARGV, options = {})
|
|
||||||
options = {
|
|
||||||
repeat_count: 1,
|
|
||||||
pattern: [],
|
|
||||||
env: 'CACHE_ON=on'
|
|
||||||
}.merge!(options)
|
|
||||||
|
|
||||||
OptionParser.new do |opts|
|
|
||||||
opts.banner = 'Usage: bin/bench [options]'
|
|
||||||
|
|
||||||
opts.on('-r', '--repeat-count [NUM]', 'Run benchmarks [NUM] times taking the best result') do |value|
|
|
||||||
options[:repeat_count] = value.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on('-p', '--pattern <PATTERN1,PATTERN2,PATTERN3>', 'Benchmark name pattern') do |value|
|
|
||||||
options[:pattern] = value.split(',')
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on('-e', '--env <var1=val1,var2=val2,var3=vale>', 'ENV variables to pass in') do |value|
|
|
||||||
options[:env] = value.split(',')
|
|
||||||
end
|
|
||||||
end.parse!(argv)
|
|
||||||
|
|
||||||
benchmark(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :commit_hash, :base
|
|
||||||
|
|
||||||
# Based on logfmt:
|
|
||||||
# https://www.brandur.org/logfmt
|
|
||||||
# For more complete implementation see:
|
|
||||||
# see https://github.com/arachnid-cb/logfmtr/blob/master/lib/logfmtr/base.rb
|
|
||||||
# For usage see:
|
|
||||||
# https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write/
|
|
||||||
# https://engineering.heroku.com/blogs/2014-09-05-hutils-explore-your-structured-data-logs/
|
|
||||||
# For Ruby parser see:
|
|
||||||
# https://github.com/cyberdelia/logfmt-ruby
|
|
||||||
def self.summary_logger(device = 'output.txt')
|
|
||||||
require 'time'
|
|
||||||
logger = Logger.new(device)
|
|
||||||
logger.level = Logger::INFO
|
|
||||||
logger.formatter = proc { |severity, datetime, progname, msg|
|
|
||||||
msg = "'#{msg}'"
|
|
||||||
"level=#{severity} time=#{datetime.utc.iso8601(6)} pid=#{Process.pid} progname=#{progname} msg=#{msg}#{$INPUT_RECORD_SEPARATOR}"
|
|
||||||
}
|
|
||||||
logger
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.stdout_logger
|
|
||||||
logger = Logger.new(STDOUT)
|
|
||||||
logger.level = Logger::INFO
|
|
||||||
logger.formatter = proc { |_, _, _, msg| "#{msg}#{$INPUT_RECORD_SEPARATOR}" }
|
|
||||||
logger
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(options)
|
|
||||||
@writer = ENV['SUMMARIZE'] ? self.class.summary_logger : self.class.stdout_logger
|
|
||||||
@repeat_count = options[:repeat_count]
|
|
||||||
@pattern = options[:pattern]
|
|
||||||
@commit_hash = options.fetch(:commit_hash) { `git rev-parse --short HEAD`.chomp }
|
|
||||||
@base = options.fetch(:base) { ESCAPED_BASE }
|
|
||||||
@env = Array(options[:env]).join(' ')
|
|
||||||
@rubyopt = options[:rubyopt] # TODO: rename
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
files.each do |path|
|
|
||||||
next if !@pattern.empty? && /#{@pattern.join('|')}/ !~ File.basename(path)
|
|
||||||
run_single(Shellwords.shellescape(path))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def files
|
|
||||||
Dir[File.join(base, 'bm_*')]
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_single(path)
|
|
||||||
script = "RAILS_ENV=production #{@env} ruby #{@rubyopt} #{path}"
|
|
||||||
environment = `ruby -v`.chomp.strip[/\d+\.\d+\.\d+\w+/]
|
|
||||||
|
|
||||||
runs_output = measure(script)
|
|
||||||
if runs_output.empty?
|
|
||||||
results = { error: :no_results }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
results['commit_hash'] = commit_hash
|
|
||||||
results['version'] = runs_output.first['version']
|
|
||||||
results['rails_version'] = runs_output.first['rails_version']
|
|
||||||
results['benchmark_run[environment]'] = environment
|
|
||||||
results['runs'] = []
|
|
||||||
|
|
||||||
runs_output.each do |output|
|
|
||||||
results['runs'] << {
|
|
||||||
'benchmark_type[category]' => output['label'],
|
|
||||||
'benchmark_run[result][iterations_per_second]' => output['iterations_per_second'].round(3),
|
|
||||||
'benchmark_run[result][total_allocated_objects_per_iteration]' => output['total_allocated_objects_per_iteration']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
results && report(results)
|
|
||||||
end
|
|
||||||
|
|
||||||
def report(results)
|
|
||||||
@writer.info { 'Benchmark results:' }
|
|
||||||
@writer.info { JSON.pretty_generate(results) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def summarize(result)
|
|
||||||
puts "#{result['label']} #{result['iterations_per_second']}/ips; #{result['total_allocated_objects_per_iteration']} objects"
|
|
||||||
end
|
|
||||||
|
|
||||||
# FIXME: ` provides the full output but it'll return failed output as well.
|
|
||||||
def measure(script)
|
|
||||||
results = Hash.new { |h, k| h[k] = [] }
|
|
||||||
|
|
||||||
@repeat_count.times do
|
|
||||||
output = sh(script)
|
|
||||||
output.each_line do |line|
|
|
||||||
next if line.nil?
|
|
||||||
begin
|
|
||||||
result = JSON.parse(line)
|
|
||||||
rescue JSON::ParserError
|
|
||||||
result = { error: line } # rubocop:disable Lint/UselessAssignment
|
|
||||||
else
|
|
||||||
summarize(result)
|
|
||||||
results[result['label']] << result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
results.map do |_, bm_runs|
|
|
||||||
bm_runs.sort_by do |run|
|
|
||||||
run['iterations_per_second']
|
|
||||||
end.last
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sh(cmd)
|
|
||||||
`#{cmd}`
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
BenchmarkDriver.parse_argv_and_run if $PROGRAM_NAME == __FILE__
|
|
||||||
@ -1,316 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
require 'fileutils'
|
|
||||||
require 'pathname'
|
|
||||||
require 'shellwords'
|
|
||||||
require 'English'
|
|
||||||
|
|
||||||
############################
|
|
||||||
# USAGE
|
|
||||||
#
|
|
||||||
# bundle exec bin/bench_regression <ref1> <ref2>
|
|
||||||
# <ref1> defaults to the current branch
|
|
||||||
# <ref2> defaults to the master branch
|
|
||||||
# bundle exec bin/bench_regression current # will run on the current branch
|
|
||||||
# bundle exec bin/bench_regression revisions 792fb8a90 master # every revision inclusive
|
|
||||||
# bundle exec bin/bench_regression 792fb8a90 master --repeat-count 2 --env CACHE_ON=off
|
|
||||||
# bundle exec bin/bench_regression vendor
|
|
||||||
###########################
|
|
||||||
|
|
||||||
class BenchRegression
|
|
||||||
ROOT = Pathname File.expand_path(File.join(*['..', '..']), __FILE__)
|
|
||||||
TMP_DIR_NAME = File.join('tmp', 'bench')
|
|
||||||
TMP_DIR = File.join(ROOT, TMP_DIR_NAME)
|
|
||||||
E_TMP_DIR = Shellwords.shellescape(TMP_DIR)
|
|
||||||
load ROOT.join('bin', 'bench')
|
|
||||||
|
|
||||||
attr_reader :source_stasher
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@source_stasher = SourceStasher.new
|
|
||||||
end
|
|
||||||
|
|
||||||
class SourceStasher
|
|
||||||
attr_reader :gem_require_paths, :gem_paths
|
|
||||||
attr_writer :vendor
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@gem_require_paths = []
|
|
||||||
@gem_paths = []
|
|
||||||
refresh_temp_dir
|
|
||||||
@vendor = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def temp_dir_empty?
|
|
||||||
File.directory?(TMP_DIR) &&
|
|
||||||
Dir[File.join(TMP_DIR, '*')].none?
|
|
||||||
end
|
|
||||||
|
|
||||||
def empty_temp_dir
|
|
||||||
return if @vendor
|
|
||||||
return if temp_dir_empty?
|
|
||||||
FileUtils.mkdir_p(TMP_DIR)
|
|
||||||
Dir[File.join(TMP_DIR, '*')].each do |file|
|
|
||||||
if File.directory?(file)
|
|
||||||
FileUtils.rm_rf(file)
|
|
||||||
else
|
|
||||||
FileUtils.rm(file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fill_temp_dir
|
|
||||||
vendor_files(Dir[File.join(ROOT, 'test', 'benchmark', '*.{rb,ru}')])
|
|
||||||
# vendor_file(File.join('bin', 'bench'))
|
|
||||||
housekeeping { empty_temp_dir }
|
|
||||||
vendor_gem('benchmark-ips')
|
|
||||||
end
|
|
||||||
|
|
||||||
def vendor_files(files)
|
|
||||||
files.each do |file|
|
|
||||||
vendor_file(file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def vendor_file(file)
|
|
||||||
FileUtils.cp(file, File.join(TMP_DIR, File.basename(file)))
|
|
||||||
end
|
|
||||||
|
|
||||||
def vendor_gem(gem_name)
|
|
||||||
directory_name = `bundle exec gem unpack benchmark-ips --target=#{E_TMP_DIR}`[/benchmark-ips.+\d/]
|
|
||||||
gem_paths << File.join(TMP_DIR, directory_name)
|
|
||||||
gem_require_paths << File.join(TMP_DIR_NAME, directory_name, 'lib')
|
|
||||||
housekeeping { remove_vendored_gems }
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_vendored_gems
|
|
||||||
return if @vendor
|
|
||||||
FileUtils.rm_rf(*gem_paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh_temp_dir
|
|
||||||
empty_temp_dir
|
|
||||||
fill_temp_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
def housekeeping
|
|
||||||
at_exit { yield }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module RevisionMethods
|
|
||||||
module_function
|
|
||||||
def current_branch
|
|
||||||
@current_branch ||= `cat .git/HEAD | cut -d/ -f3,4,5`.chomp
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_revision
|
|
||||||
`git rev-parse --short HEAD`.chomp
|
|
||||||
end
|
|
||||||
|
|
||||||
def revision_description(rev)
|
|
||||||
`git log --oneline -1 #{rev}`.chomp
|
|
||||||
end
|
|
||||||
|
|
||||||
def revisions(start_ref, end_ref)
|
|
||||||
cmd = "git rev-list --reverse #{start_ref}..#{end_ref}"
|
|
||||||
`#{cmd}`.chomp.split("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
def checkout_ref(ref)
|
|
||||||
`git checkout #{ref}`.chomp
|
|
||||||
if $CHILD_STATUS
|
|
||||||
STDERR.puts "Checkout failed: #{ref}, #{$CHILD_STATUS.exitstatus}" unless $CHILD_STATUS.success?
|
|
||||||
$CHILD_STATUS.success?
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def clean_head
|
|
||||||
system('git reset --hard --quiet')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
module ShellMethods
|
|
||||||
|
|
||||||
def sh(cmd)
|
|
||||||
puts cmd
|
|
||||||
# system(cmd)
|
|
||||||
run(cmd)
|
|
||||||
# env = {}
|
|
||||||
# # out = STDOUT
|
|
||||||
# pid = spawn(env, cmd)
|
|
||||||
# Process.wait(pid)
|
|
||||||
# pid = fork do
|
|
||||||
# exec cmd
|
|
||||||
# end
|
|
||||||
# Process.waitpid2(pid)
|
|
||||||
# puts $CHILD_STATUS.exitstatus
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'pty'
|
|
||||||
# should consider trapping SIGINT in here
|
|
||||||
def run(cmd)
|
|
||||||
puts cmd
|
|
||||||
child_process = ''
|
|
||||||
result = ''
|
|
||||||
# http://stackoverflow.com/a/1162850
|
|
||||||
# stream output of subprocess
|
|
||||||
begin
|
|
||||||
PTY.spawn(cmd) do |stdin, _stdout, pid|
|
|
||||||
begin
|
|
||||||
# Do stuff with the output here. Just printing to show it works
|
|
||||||
stdin.each do |line|
|
|
||||||
print line
|
|
||||||
result << line
|
|
||||||
end
|
|
||||||
child_process = PTY.check(pid)
|
|
||||||
rescue Errno::EIO
|
|
||||||
puts 'Errno:EIO error, but this probably just means ' \
|
|
||||||
'that the process has finished giving output'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue PTY::ChildExited
|
|
||||||
puts 'The child process exited!'
|
|
||||||
end
|
|
||||||
unless (child_process && child_process.success?)
|
|
||||||
exitstatus = child_process.exitstatus
|
|
||||||
puts "FAILED: #{child_process.pid} exited with status #{exitstatus.inspect} due to failed command #{cmd}"
|
|
||||||
exit exitstatus || 1
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def bundle(ref)
|
|
||||||
system("rm -f Gemfile.lock")
|
|
||||||
# This is absolutely critical for bundling to work
|
|
||||||
Bundler.with_clean_env do
|
|
||||||
system("bundle check ||
|
|
||||||
bundle install --local ||
|
|
||||||
bundle install ||
|
|
||||||
bundle update")
|
|
||||||
end
|
|
||||||
|
|
||||||
# if $CHILD_STATUS
|
|
||||||
# STDERR.puts "Bundle failed at: #{ref}, #{$CHILD_STATUS.exitstatus}" unless $CHILD_STATUS.success?
|
|
||||||
# $CHILD_STATUS.success?
|
|
||||||
# else
|
|
||||||
# false
|
|
||||||
# end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
include ShellMethods
|
|
||||||
include RevisionMethods
|
|
||||||
|
|
||||||
def benchmark_refs(ref1: nil, ref2: nil, cmd:)
|
|
||||||
checking_out = false
|
|
||||||
ref0 = current_branch
|
|
||||||
ref1 ||= current_branch
|
|
||||||
ref2 ||= 'master'
|
|
||||||
p [ref0, ref1, ref2, current_revision]
|
|
||||||
|
|
||||||
run_benchmark_at_ref(cmd, ref1)
|
|
||||||
p [ref0, ref1, ref2, current_revision]
|
|
||||||
run_benchmark_at_ref(cmd, ref2)
|
|
||||||
p [ref0, ref1, ref2, current_revision]
|
|
||||||
|
|
||||||
checking_out = true
|
|
||||||
checkout_ref(ref0)
|
|
||||||
rescue Exception # rubocop:disable Lint/RescueException
|
|
||||||
STDERR.puts "[ERROR] #{$!.message}"
|
|
||||||
checkout_ref(ref0) unless checking_out
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
def benchmark_revisions(ref1: nil, ref2: nil, cmd:)
|
|
||||||
checking_out = false
|
|
||||||
ref0 = current_branch
|
|
||||||
ref1 ||= current_branch
|
|
||||||
ref2 ||= 'master'
|
|
||||||
|
|
||||||
revisions(ref1, ref2).each do |rev|
|
|
||||||
STDERR.puts "Checking out: #{revision_description(rev)}"
|
|
||||||
|
|
||||||
run_benchmark_at_ref(cmd, rev)
|
|
||||||
clean_head
|
|
||||||
end
|
|
||||||
checking_out = true
|
|
||||||
checkout_ref(ref0)
|
|
||||||
rescue Exception # rubocop:disable Lint/RescueException
|
|
||||||
STDERR.puts "[ERROR]: #{$!.message}"
|
|
||||||
checkout_ref(ref0) unless checking_out
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_benchmark_at_ref(cmd, ref)
|
|
||||||
checkout_ref(ref)
|
|
||||||
run_benchmark(cmd, ref)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_benchmark(cmd, ref = nil)
|
|
||||||
ref ||= current_revision
|
|
||||||
bundle(ref) &&
|
|
||||||
benchmark_tests(cmd, ref)
|
|
||||||
end
|
|
||||||
|
|
||||||
def benchmark_tests(cmd, ref)
|
|
||||||
base = E_TMP_DIR
|
|
||||||
# cmd.sub('bin/bench', 'tmp/revision_runner/bench')
|
|
||||||
# bundle = Gem.bin('bunle'
|
|
||||||
# Bundler.with_clean_env(&block)
|
|
||||||
|
|
||||||
# cmd = Shellwords.shelljoin(cmd)
|
|
||||||
# cmd = "COMMIT_HASH=#{ref} BASE=#{base} bundle exec ruby -rbenchmark/ips #{cmd}"
|
|
||||||
# Add vendoring benchmark/ips to load path
|
|
||||||
|
|
||||||
# CURRENT THINKING: IMPORTANT
|
|
||||||
# Pass into require statement as RUBYOPTS i.e. via env rather than command line argument
|
|
||||||
# otherwise, have a 'fast ams benchmarking' module that extends benchmarkings to add the 'ams'
|
|
||||||
# method but doesn't depend on benchmark-ips
|
|
||||||
options = {
|
|
||||||
commit_hash: ref,
|
|
||||||
base: base,
|
|
||||||
rubyopt: Shellwords.shellescape("-Ilib:#{source_stasher.gem_require_paths.join(':')}")
|
|
||||||
}
|
|
||||||
BenchmarkDriver.parse_argv_and_run(ARGV.dup, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if $PROGRAM_NAME == __FILE__
|
|
||||||
benchmarking = BenchRegression.new
|
|
||||||
|
|
||||||
case ARGV[0]
|
|
||||||
when 'current'
|
|
||||||
# Run current branch only
|
|
||||||
|
|
||||||
# super simple command line parsing
|
|
||||||
args = ARGV.dup
|
|
||||||
_ = args.shift # remove 'current' from args
|
|
||||||
cmd = args
|
|
||||||
benchmarking.run_benchmark(cmd)
|
|
||||||
when 'revisions'
|
|
||||||
# Runs on every revision
|
|
||||||
|
|
||||||
# super simple command line parsing
|
|
||||||
args = ARGV.dup
|
|
||||||
_ = args.shift
|
|
||||||
ref1 = args.shift # remove 'revisions' from args
|
|
||||||
ref2 = args.shift
|
|
||||||
cmd = args
|
|
||||||
benchmarking.benchmark_revisions(ref1: ref1, ref2: ref2, cmd: cmd)
|
|
||||||
when 'vendor'
|
|
||||||
# Just prevents vendored files from being cleaned up
|
|
||||||
# at exit. (They are vendored at initialize.)
|
|
||||||
benchmarking.source_stasher.vendor = true
|
|
||||||
else
|
|
||||||
# Default: Compare current_branch to master
|
|
||||||
# Optionally: pass in two refs as args to `bin/bench_regression`
|
|
||||||
# TODO: Consider checking across more revisions, to automatically find problems.
|
|
||||||
|
|
||||||
# super simple command line parsing
|
|
||||||
args = ARGV.dup
|
|
||||||
ref1 = args.shift
|
|
||||||
ref2 = args.shift
|
|
||||||
cmd = args
|
|
||||||
benchmarking.benchmark_refs(ref1: ref1, ref2: ref2, cmd: cmd)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
38
bin/rubocop
38
bin/rubocop
@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# bin/rubocop [-A|-t|-h]
|
|
||||||
# bin/rubocop [file or path] [cli options]
|
|
||||||
#
|
|
||||||
# Options:
|
|
||||||
# Autocorrect -A
|
|
||||||
# AutoGenConfig -t
|
|
||||||
# Usage -h,--help,help
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
case $1 in
|
|
||||||
-A)
|
|
||||||
echo "Rubocop autocorrect is ON" >&2
|
|
||||||
bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_correct
|
|
||||||
;;
|
|
||||||
|
|
||||||
-t)
|
|
||||||
echo "Rubocop is generating a new TODO" >&2
|
|
||||||
bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_gen_config
|
|
||||||
;;
|
|
||||||
|
|
||||||
-h|--help|help)
|
|
||||||
sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
# with no args, run vanilla rubocop
|
|
||||||
# else assume we're passing in arbitrary arguments
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
bundle exec rake -f lib/tasks/rubocop.rake rubocop
|
|
||||||
else
|
|
||||||
bundle exec rubocop "$@"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
|
|
||||||
start)
|
|
||||||
config="${CONFIG_RU:-test/benchmark/config.ru}"
|
|
||||||
bundle exec ruby -Ilib -S rackup "$config" --daemonize --pid tmp/benchmark_app.pid --warn --server webrick
|
|
||||||
until [ -f 'tmp/benchmark_app.pid' ]; do
|
|
||||||
sleep 0.1 # give it time to start.. I don't know a better way
|
|
||||||
done
|
|
||||||
cat tmp/benchmark_app.pid
|
|
||||||
true
|
|
||||||
;;
|
|
||||||
|
|
||||||
stop)
|
|
||||||
if [ -f 'tmp/benchmark_app.pid' ]; then
|
|
||||||
kill -TERM $(cat tmp/benchmark_app.pid)
|
|
||||||
else
|
|
||||||
echo 'No pidfile'
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
status)
|
|
||||||
if [ -f 'tmp/benchmark_app.pid' ]; then
|
|
||||||
kill -0 $(cat tmp/benchmark_app.pid)
|
|
||||||
[ "$?" -eq 0 ]
|
|
||||||
else
|
|
||||||
echo 'No pidfile'
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 [start|stop|status]"
|
|
||||||
;;
|
|
||||||
|
|
||||||
esac
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
# Docs - ActiveModel::Serializer 0.10.x
|
|
||||||
|
|
||||||
This is the documentation of ActiveModelSerializers, it's focused on the **0.10.x version.**
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
## General
|
|
||||||
|
|
||||||
- [Getting Started](general/getting_started.md)
|
|
||||||
- [Configuration Options](general/configuration_options.md)
|
|
||||||
- [Serializers](general/serializers.md)
|
|
||||||
- [Adapters](general/adapters.md)
|
|
||||||
- [Rendering](general/rendering.md)
|
|
||||||
- [Caching](general/caching.md)
|
|
||||||
- [Logging](general/logging.md)
|
|
||||||
- [Deserialization](general/deserialization.md)
|
|
||||||
- [Instrumentation](general/instrumentation.md)
|
|
||||||
- JSON API
|
|
||||||
- [Schema](jsonapi/schema.md)
|
|
||||||
- [Errors](jsonapi/errors.md)
|
|
||||||
|
|
||||||
## How to
|
|
||||||
|
|
||||||
- [How to add root key](howto/add_root_key.md)
|
|
||||||
- [How to add pagination links](howto/add_pagination_links.md)
|
|
||||||
- [How to add relationship links](howto/add_relationship_links.md)
|
|
||||||
- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md)
|
|
||||||
- [Testing ActiveModelSerializers](howto/test.md)
|
|
||||||
- [Passing Arbitrary Options](howto/passing_arbitrary_options.md)
|
|
||||||
- [How to serialize a Plain-Old Ruby Object (PORO)](howto/serialize_poro.md)
|
|
||||||
- [How to upgrade from `0.8` to `0.10` safely](howto/upgrade_from_0_8_to_0_10.md)
|
|
||||||
|
|
||||||
## Integrations
|
|
||||||
|
|
||||||
| Integration | Supported ActiveModelSerializers versions | Gem name and/or link
|
|
||||||
|----|-----|----
|
|
||||||
| Ember.js | 0.9.x | [active-model-adapter](https://github.com/ember-data/active-model-adapter)
|
|
||||||
| Ember.js | 0.10.x + | [docs/integrations/ember-and-json-api.md](integrations/ember-and-json-api.md)
|
|
||||||
| Grape | 0.10.x + | [docs/integrations/grape.md](integrations/grape.md) |
|
|
||||||
| Grape | 0.9.x | https://github.com/jrhe/grape-active_model_serializers/ |
|
|
||||||
| Sinatra | 0.9.x | https://github.com/SauloSilva/sinatra-active-model-serializers/
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
# STYLE
|
|
||||||
|
|
||||||
## Code and comments
|
|
||||||
|
|
||||||
- We are actively working to identify tasks under the label [**Good for New
|
|
||||||
Contributors**](https://github.com/rails-api/active_model_serializers/labels/Good%20for%20New%20Contributors).
|
|
||||||
- [Changelog
|
|
||||||
Missing](https://github.com/rails-api/active_model_serializers/issues?q=label%3A%22Changelog+Missing%22+is%3Aclosed) is
|
|
||||||
an easy way to help out.
|
|
||||||
|
|
||||||
- [Fix a bug](https://github.com/rails-api/active_model_serializers/labels/Ready%20for%20PR).
|
|
||||||
- Ready for PR - A well defined bug, needs someone to PR a fix.
|
|
||||||
- Bug - Anything that is broken.
|
|
||||||
- Regression - A bug that did not exist in previous versions and isn't a new feature (applied in tandem with Bug).
|
|
||||||
- Performance - A performance related issue. We could track this as a bug, but usually these would have slightly lower priority than standard bugs.
|
|
||||||
|
|
||||||
- [Develop new features](https://github.com/rails-api/active_model_serializers/labels/Feature).
|
|
||||||
|
|
||||||
- [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).
|
|
||||||
|
|
||||||
- We are also encouraging comments to substantial changes (larger than bugfixes and simple features) under an
|
|
||||||
"RFC" (Request for Comments) process before we start active development.
|
|
||||||
Look for the [**RFC**](https://github.com/rails-api/active_model_serializers/labels/RFC) label.
|
|
||||||
|
|
||||||
|
|
||||||
## Pull requests
|
|
||||||
|
|
||||||
- If the tests pass and the pull request looks good, a maintainer will merge it.
|
|
||||||
- If the pull request needs to be changed,
|
|
||||||
- you can change it by updating the branch you generated the pull request from
|
|
||||||
- either by adding more commits, or
|
|
||||||
- by force pushing to it
|
|
||||||
- A maintainer can make any changes themselves and manually merge the code in.
|
|
||||||
|
|
||||||
## Commit messages
|
|
||||||
|
|
||||||
- [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
|
||||||
- [http://stopwritingramblingcommitmessages.com/](http://stopwritingramblingcommitmessages.com/)
|
|
||||||
- [ThoughtBot style guide](https://github.com/thoughtbot/guides/tree/master/style#git)
|
|
||||||
|
|
||||||
#### About Pull Requests (PR's)
|
|
||||||
|
|
||||||
- [Using Pull Requests](https://help.github.com/articles/using-pull-requests)
|
|
||||||
- [Github pull requests made easy](http://www.element84.com/github-pull-requests-made-easy.html)
|
|
||||||
- [Exercism Git Workflow](http://help.exercism.io/git-workflow.html).
|
|
||||||
- [Level up your Git](http://rakeroutes.com/blog/deliberate-git/)
|
|
||||||
- [All Your Open Source Code Are Belong To Us](http://www.benjaminfleischer.com/2013/07/30/all-your-open-source-code-are-belong-to-us/)
|
|
||||||
|
|
||||||
## Issue Labeling
|
|
||||||
|
|
||||||
ActiveModelSerializers uses a subset of [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels) for Github Issues. You can [see our labels here](https://github.com/rails-api/active_model_serializers/labels).
|
|
||||||
|
|
||||||
@ -1,263 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Adapters
|
|
||||||
|
|
||||||
ActiveModelSerializers offers the ability to configure which adapter
|
|
||||||
to use both globally and/or when serializing (usually when rendering).
|
|
||||||
|
|
||||||
The global adapter configuration is set on [`ActiveModelSerializers.config`](configuration_options.md).
|
|
||||||
It should be set only once, preferably at initialization.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter = :json_api
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter = :json
|
|
||||||
```
|
|
||||||
|
|
||||||
The local adapter option is in the format `adapter: adapter`, where `adapter` is
|
|
||||||
any of the same values as set globally.
|
|
||||||
|
|
||||||
The configured adapter can be set as a symbol, class, or class name, as described in
|
|
||||||
[Advanced adapter configuration](adapters.md#advanced-adapter-configuration).
|
|
||||||
|
|
||||||
The `Attributes` adapter does not include a root key. It is just the serialized attributes.
|
|
||||||
|
|
||||||
Use either the `JSON` or `JSON API` adapters if you want the response document to have a root key.
|
|
||||||
|
|
||||||
## Built in Adapters
|
|
||||||
|
|
||||||
### Attributes - Default
|
|
||||||
|
|
||||||
It's the default adapter, it generates a json response without a root key.
|
|
||||||
Doesn't follow any specific convention.
|
|
||||||
|
|
||||||
##### Example output
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Title 1",
|
|
||||||
"body": "Body 1",
|
|
||||||
"publish_at": "2020-03-16T03:55:25.291Z",
|
|
||||||
"author": {
|
|
||||||
"first_name": "Bob",
|
|
||||||
"last_name": "Jones"
|
|
||||||
},
|
|
||||||
"comments": [
|
|
||||||
{
|
|
||||||
"body": "cool"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body": "awesome"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### JSON
|
|
||||||
|
|
||||||
The json response is always rendered with a root key.
|
|
||||||
|
|
||||||
The root key can be overridden by:
|
|
||||||
* passing the `root` option in the render call. See details in the [Rendering Guides](rendering.md#overriding-the-root-key).
|
|
||||||
* setting the `type` of the serializer. See details in the [Serializers Guide](serializers.md#type).
|
|
||||||
|
|
||||||
Doesn't follow any specific convention.
|
|
||||||
|
|
||||||
##### Example output
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"post": {
|
|
||||||
"title": "Title 1",
|
|
||||||
"body": "Body 1",
|
|
||||||
"publish_at": "2020-03-16T03:55:25.291Z",
|
|
||||||
"author": {
|
|
||||||
"first_name": "Bob",
|
|
||||||
"last_name": "Jones"
|
|
||||||
},
|
|
||||||
"comments": [{
|
|
||||||
"body": "cool"
|
|
||||||
}, {
|
|
||||||
"body": "awesome"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### JSON API
|
|
||||||
|
|
||||||
This adapter follows **version 1.0** of the [format specified](../jsonapi/schema.md) in
|
|
||||||
[jsonapi.org/format](http://jsonapi.org/format).
|
|
||||||
|
|
||||||
##### Example output
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"id": "1337",
|
|
||||||
"type": "posts",
|
|
||||||
"attributes": {
|
|
||||||
"title": "Title 1",
|
|
||||||
"body": "Body 1",
|
|
||||||
"publish-at": "2020-03-16T03:55:25.291Z"
|
|
||||||
},
|
|
||||||
"relationships": {
|
|
||||||
"author": {
|
|
||||||
"data": {
|
|
||||||
"id": "1",
|
|
||||||
"type": "authors"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"comments": {
|
|
||||||
"data": [{
|
|
||||||
"id": "7",
|
|
||||||
"type": "comments"
|
|
||||||
}, {
|
|
||||||
"id": "12",
|
|
||||||
"type": "comments"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"post-authors": "https://example.com/post_authors"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"rating": 5,
|
|
||||||
"favorite-count": 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Include option
|
|
||||||
|
|
||||||
Which [serializer associations](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#associations) are rendered can be specified using the `include` option. The option usage is consistent with [the include option in the JSON API spec](http://jsonapi.org/format/#fetching-includes), and is available in all adapters.
|
|
||||||
|
|
||||||
Example of the usage:
|
|
||||||
```ruby
|
|
||||||
render json: @posts, include: ['author', 'comments', 'comments.author']
|
|
||||||
# or
|
|
||||||
render json: @posts, include: 'author,comments,comments.author'
|
|
||||||
```
|
|
||||||
|
|
||||||
The format of the `include` option can be either:
|
|
||||||
|
|
||||||
- a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes).
|
|
||||||
- an Array of Symbols and Hashes.
|
|
||||||
- a mix of both.
|
|
||||||
|
|
||||||
An empty string or an empty array will prevent rendering of any associations.
|
|
||||||
|
|
||||||
In addition, two types of wildcards may be used:
|
|
||||||
|
|
||||||
- `*` includes one level of associations.
|
|
||||||
- `**` includes all recursively.
|
|
||||||
|
|
||||||
These can be combined with other paths.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, include: '**' # or '*' for a single layer
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
The following would render posts and include:
|
|
||||||
|
|
||||||
- the author
|
|
||||||
- the author's comments, and
|
|
||||||
- every resource referenced by the author's comments (recursively).
|
|
||||||
|
|
||||||
It could be combined, like above, with other paths in any combination desired.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, include: 'author.comments.**'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Wildcards are ActiveModelSerializers-specific, they are not part of the JSON API spec.
|
|
||||||
|
|
||||||
The default include for the JSON API adapter is no associations. The default for the JSON and Attributes adapters is all associations.
|
|
||||||
|
|
||||||
For the JSON API adapter associated resources will be gathered in the `"included"` member. For the JSON and Attributes
|
|
||||||
adapters associated resources will be rendered among the other attributes.
|
|
||||||
|
|
||||||
Only for the JSON API adapter you can specify, which attributes of associated resources will be rendered. This feature
|
|
||||||
is called [sparse fieldset](http://jsonapi.org/format/#fetching-sparse-fieldsets):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, include: 'comments', fields: { comments: ['content', 'created_at'] }
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Security Considerations
|
|
||||||
|
|
||||||
Since the included options may come from the query params (i.e. user-controller):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, include: params[:include]
|
|
||||||
```
|
|
||||||
|
|
||||||
The user could pass in `include=**`.
|
|
||||||
|
|
||||||
We recommend filtering any user-supplied includes appropriately.
|
|
||||||
|
|
||||||
## Advanced adapter configuration
|
|
||||||
|
|
||||||
### Registering an adapter
|
|
||||||
|
|
||||||
The default adapter can be configured, as above, to use any class given to it.
|
|
||||||
|
|
||||||
An adapter may also be specified, e.g. when rendering, as a class or as a symbol.
|
|
||||||
If a symbol, then the adapter must be, e.g. `:great_example`,
|
|
||||||
`ActiveModelSerializers::Adapter::GreatExample`, or registered.
|
|
||||||
|
|
||||||
There are two ways to register an adapter:
|
|
||||||
|
|
||||||
1) The simplest, is to subclass `ActiveModelSerializers::Adapter::Base`, e.g. the below will
|
|
||||||
register the `Example::UsefulAdapter` as `"example/useful_adapter"`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
module Example
|
|
||||||
class UsefulAdapter < ActiveModelSerializers::Adapter::Base
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll notice that the name it registers is the underscored namespace and class.
|
|
||||||
|
|
||||||
Under the covers, when the `ActiveModelSerializers::Adapter::Base` is subclassed, it registers
|
|
||||||
the subclass as `register("example/useful_adapter", Example::UsefulAdapter)`
|
|
||||||
|
|
||||||
2) Any class can be registered as an adapter by calling `register` directly on the
|
|
||||||
`ActiveModelSerializers::Adapter` class. e.g., the below registers `MyAdapter` as
|
|
||||||
`:special_adapter`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class MyAdapter; end
|
|
||||||
ActiveModelSerializers::Adapter.register(:special_adapter, MyAdapter)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Looking up an adapter
|
|
||||||
|
|
||||||
| Method | Return value |
|
|
||||||
| :------------ |:---------------|
|
|
||||||
| `ActiveModelSerializers::Adapter.adapter_map` | A Hash of all known adapters `{ adapter_name => adapter_class }` |
|
|
||||||
| `ActiveModelSerializers::Adapter.adapters` | A (sorted) Array of all known `adapter_names` |
|
|
||||||
| `ActiveModelSerializers::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModelSerializers::Adapter::UnknownAdapter` error |
|
|
||||||
| `ActiveModelSerializers::Adapter.adapter_class(adapter)` | Delegates to `ActiveModelSerializers::Adapter.lookup(adapter)` |
|
|
||||||
| `ActiveModelSerializers::Adapter.configured_adapter` | A convenience method for `ActiveModelSerializers::Adapter.lookup(config.adapter)` |
|
|
||||||
|
|
||||||
The registered adapter name is always a String, but may be looked up as a Symbol or String.
|
|
||||||
Helpfully, the Symbol or String is underscored, so that `get(:my_adapter)` and `get("MyAdapter")`
|
|
||||||
may both be used.
|
|
||||||
|
|
||||||
For more information, see [the Adapter class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/adapter.rb)
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Caching
|
|
||||||
|
|
||||||
## Warning
|
|
||||||
|
|
||||||
There is currently a problem with caching in AMS [Caching doesn't improve performance](https://github.com/rails-api/active_model_serializers/issues/1586). Adding caching _may_ slow down your application, rather than speeding it up. We suggest you benchmark any caching you implement before using in a production enviroment
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
To cache a serializer, call ```cache``` and pass its options.
|
|
||||||
The options are the same options of ```ActiveSupport::Cache::Store```, plus
|
|
||||||
a ```key``` option that will be the prefix of the object cache
|
|
||||||
on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```.
|
|
||||||
|
|
||||||
The cache support is optimized to use the cached object in multiple request. An object cached on a ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically.
|
|
||||||
|
|
||||||
**[NOTE] Every object is individually cached.**
|
|
||||||
|
|
||||||
**[NOTE] The cache is automatically expired after an object is updated, but it's not deleted.**
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}```
|
|
||||||
```
|
|
||||||
|
|
||||||
Take the example below:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
cache key: 'post', expires_in: 3.hours
|
|
||||||
attributes :title, :body
|
|
||||||
|
|
||||||
has_many :comments
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
On this example every ```Post``` object will be cached with
|
|
||||||
the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want,
|
|
||||||
but in this case it will be automatically expired after 3 hours.
|
|
||||||
|
|
||||||
## Fragment Caching
|
|
||||||
|
|
||||||
If there is some API endpoint that shouldn't be fully cached, you can still optimise it, using Fragment Cache on the attributes and relationships that you want to cache.
|
|
||||||
|
|
||||||
You can define the attribute by using ```only``` or ```except``` option on cache method.
|
|
||||||
|
|
||||||
**[NOTE] Cache serializers will be used at their relationships**
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
cache key: 'post', expires_in: 3.hours, only: [:title]
|
|
||||||
attributes :title, :body
|
|
||||||
|
|
||||||
has_many :comments
|
|
||||||
end
|
|
||||||
```
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Configuration Options
|
|
||||||
|
|
||||||
The following configuration options can be set on
|
|
||||||
`ActiveModelSerializers.config`, preferably inside an initializer.
|
|
||||||
|
|
||||||
## General
|
|
||||||
|
|
||||||
##### adapter
|
|
||||||
|
|
||||||
The [adapter](adapters.md) to use.
|
|
||||||
|
|
||||||
Possible values:
|
|
||||||
|
|
||||||
- `:attributes` (default)
|
|
||||||
- `:json`
|
|
||||||
- `:json_api`
|
|
||||||
|
|
||||||
##### serializer_lookup_enabled
|
|
||||||
|
|
||||||
Enable automatic serializer lookup.
|
|
||||||
|
|
||||||
Possible values:
|
|
||||||
|
|
||||||
- `true` (default)
|
|
||||||
- `false`
|
|
||||||
|
|
||||||
When `false`, serializers must be explicitly specified.
|
|
||||||
|
|
||||||
##### key_transform
|
|
||||||
|
|
||||||
The [key transform](key_transforms.md) to use.
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Result |
|
|
||||||
|----|----|
|
|
||||||
| `:camel` | ExampleKey |
|
|
||||||
| `:camel_lower` | exampleKey |
|
|
||||||
| `:dash` | example-key |
|
|
||||||
| `:unaltered` | the original, unaltered key |
|
|
||||||
| `:underscore` | example_key |
|
|
||||||
| `nil` | use the adapter default |
|
|
||||||
|
|
||||||
Each adapter has a default key transform configured:
|
|
||||||
|
|
||||||
| Adapter | Default Key Transform |
|
|
||||||
|----|----|
|
|
||||||
| `Attributes` | `:unaltered` |
|
|
||||||
| `Json` | `:unaltered` |
|
|
||||||
| `JsonApi` | `:dash` |
|
|
||||||
|
|
||||||
`config.key_transform` is a global override of the adapter default. Adapters
|
|
||||||
still prefer the render option `:key_transform` over this setting.
|
|
||||||
|
|
||||||
*NOTE: Key transforms can be expensive operations. If key transforms are unnecessary for the
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
##### serializer_lookup_chain
|
|
||||||
|
|
||||||
Configures how serializers are searched for. By default, the lookup chain is
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::LookupChain::DEFAULT
|
|
||||||
```
|
|
||||||
|
|
||||||
which is shorthand for
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
[
|
|
||||||
ActiveModelSerializers::LookupChain::BY_PARENT_SERIALIZER,
|
|
||||||
ActiveModelSerializers::LookupChain::BY_NAMESPACE,
|
|
||||||
ActiveModelSerializers::LookupChain::BY_RESOURCE_NAMESPACE,
|
|
||||||
ActiveModelSerializers::LookupChain::BY_RESOURCE
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Each of the array entries represent a proc. A serializer lookup proc will be yielded 3 arguments. `resource_class`, `serializer_class`, and `namespace`.
|
|
||||||
|
|
||||||
Note that:
|
|
||||||
- `resource_class` is the class of the resource being rendered
|
|
||||||
- by default `serializer_class` is `ActiveModel::Serializer`
|
|
||||||
- for association lookup it's the "parent" serializer
|
|
||||||
- `namespace` correspond to either the controller namespace or the [optionally] specified [namespace render option](./rendering.md#namespace)
|
|
||||||
|
|
||||||
An example config could be:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.serializer_lookup_chain = [
|
|
||||||
lambda do |resource_class, serializer_class, namespace|
|
|
||||||
"API::#{namespace}::#{resource_class}"
|
|
||||||
end
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
If you simply want to add to the existing lookup_chain. Use `unshift`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.serializer_lookup_chain.unshift(
|
|
||||||
lambda do |resource_class, serializer_class, namespace|
|
|
||||||
# ...
|
|
||||||
end
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## JSON API
|
|
||||||
|
|
||||||
##### jsonapi_resource_type
|
|
||||||
|
|
||||||
Sets whether the [type](http://jsonapi.org/format/#document-resource-identifier-objects)
|
|
||||||
of the resource should be `singularized` or `pluralized` when it is not
|
|
||||||
[explicitly specified by the serializer](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#type)
|
|
||||||
|
|
||||||
Possible values:
|
|
||||||
|
|
||||||
- `:singular`
|
|
||||||
- `:plural` (default)
|
|
||||||
|
|
||||||
##### jsonapi_namespace_separator
|
|
||||||
|
|
||||||
Sets separator string for namespaced models to render `type` attribute.
|
|
||||||
|
|
||||||
|
|
||||||
| Separator | Example: Admin::User |
|
|
||||||
|----|----|
|
|
||||||
| `'-'` (default) | 'admin-users'
|
|
||||||
| `'--'` (recommended) | 'admin--users'
|
|
||||||
|
|
||||||
See [Recommendation for dasherizing (kebab-case-ing) namespaced object, such as `Admin::User`](https://github.com/json-api/json-api/issues/850)
|
|
||||||
for more discussion.
|
|
||||||
|
|
||||||
##### jsonapi_include_toplevel_object
|
|
||||||
|
|
||||||
Include a [top level jsonapi member](http://jsonapi.org/format/#document-jsonapi-object)
|
|
||||||
in the response document.
|
|
||||||
|
|
||||||
Possible values:
|
|
||||||
|
|
||||||
- `true`
|
|
||||||
- `false` (default)
|
|
||||||
|
|
||||||
##### jsonapi_version
|
|
||||||
|
|
||||||
The latest version of the spec to which the API conforms.
|
|
||||||
|
|
||||||
Default: `'1.0'`.
|
|
||||||
|
|
||||||
*Used when `jsonapi_include_toplevel_object` is `true`*
|
|
||||||
|
|
||||||
##### jsonapi_toplevel_meta
|
|
||||||
|
|
||||||
Optional top-level metadata. Not included if empty.
|
|
||||||
|
|
||||||
Default: `{}`.
|
|
||||||
|
|
||||||
*Used when `jsonapi_include_toplevel_object` is `true`*
|
|
||||||
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
To run a hook when ActiveModelSerializers is loaded, use
|
|
||||||
`ActiveSupport.on_load(:action_controller) do end`
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Deserialization
|
|
||||||
|
|
||||||
This is currently an *experimental* feature. The interface may change.
|
|
||||||
|
|
||||||
## JSON API
|
|
||||||
|
|
||||||
The `ActiveModelSerializers::Deserialization` defines two methods (namely `jsonapi_parse` and `jsonapi_parse!`), which take a `Hash` or an instance of `ActionController::Parameters` representing a JSON API payload, and return a hash that can directly be used to create/update models. The bang version throws an `InvalidDocument` exception when parsing fails, whereas the "safe" version simply returns an empty hash.
|
|
||||||
|
|
||||||
- Parameters
|
|
||||||
- document: `Hash` or `ActionController::Parameters` instance
|
|
||||||
- options:
|
|
||||||
- only: `Array` of whitelisted fields
|
|
||||||
- except: `Array` of blacklisted fields
|
|
||||||
- keys: `Hash` of fields the name of which needs to be modified (e.g. `{ :author => :user, :date => :created_at }`)
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsController < ActionController::Base
|
|
||||||
def create
|
|
||||||
Post.create(create_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_params
|
|
||||||
ActiveModelSerializers::Deserialization.jsonapi_parse(params, only: [:title, :content, :author])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Given a JSON API document,
|
|
||||||
|
|
||||||
```
|
|
||||||
document = {
|
|
||||||
'data' => {
|
|
||||||
'id' => 1,
|
|
||||||
'type' => 'post',
|
|
||||||
'attributes' => {
|
|
||||||
'title' => 'Title 1',
|
|
||||||
'date' => '2015-12-20'
|
|
||||||
},
|
|
||||||
'relationships' => {
|
|
||||||
'author' => {
|
|
||||||
'data' => {
|
|
||||||
'type' => 'user',
|
|
||||||
'id' => '2'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'second_author' => {
|
|
||||||
'data' => nil
|
|
||||||
},
|
|
||||||
'comments' => {
|
|
||||||
'data' => [{
|
|
||||||
'type' => 'comment',
|
|
||||||
'id' => '3'
|
|
||||||
},{
|
|
||||||
'type' => 'comment',
|
|
||||||
'id' => '4'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The entire document can be parsed without specifying any options:
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::Deserialization.jsonapi_parse(document)
|
|
||||||
#=>
|
|
||||||
# {
|
|
||||||
# title: 'Title 1',
|
|
||||||
# date: '2015-12-20',
|
|
||||||
# author_id: 2,
|
|
||||||
# second_author_id: nil
|
|
||||||
# comment_ids: [3, 4]
|
|
||||||
# }
|
|
||||||
```
|
|
||||||
|
|
||||||
and fields, relationships, and polymorphic relationships can be specified via the options:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::Deserialization
|
|
||||||
.jsonapi_parse(document, only: [:title, :date, :author],
|
|
||||||
keys: { date: :published_at },
|
|
||||||
polymorphic: [:author])
|
|
||||||
#=>
|
|
||||||
# {
|
|
||||||
# title: 'Title 1',
|
|
||||||
# published_at: '2015-12-20',
|
|
||||||
# author_id: '2',
|
|
||||||
# author_type: 'user'
|
|
||||||
# }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Attributes/Json
|
|
||||||
|
|
||||||
There is currently no deserialization for those adapters.
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Fields
|
|
||||||
|
|
||||||
If for any reason, you need to restrict the fields returned, you should use `fields` option.
|
|
||||||
|
|
||||||
For example, if you have a serializer like this
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class UserSerializer < ActiveModel::Serializer
|
|
||||||
attributes :access_token, :first_name, :last_name
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
and in a specific controller, you want to return `access_token` only, `fields` will help you:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class AnonymousController < ApplicationController
|
|
||||||
def create
|
|
||||||
render json: User.create(activation_state: 'anonymous'), fields: [:access_token], status: 201
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that this is only valid for the `json` and `attributes` adapter. For the `json_api` adapter, you would use
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @user, fields: { users: [:access_token] }
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `users` is the JSONAPI type.
|
|
||||||
@ -1,133 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
## Creating a Serializer
|
|
||||||
|
|
||||||
The easiest way to create a new serializer is to generate a new resource, which
|
|
||||||
will generate a serializer at the same time:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rails g resource post title:string body:string
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
|
||||||
your new model. You can also generate a serializer for an existing model with
|
|
||||||
the serializer generator:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rails g serializer post
|
|
||||||
```
|
|
||||||
|
|
||||||
The generated serializer will contain basic `attributes` and
|
|
||||||
`has_many`/`has_one`/`belongs_to` declarations, based on the model. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
attributes :title, :body
|
|
||||||
|
|
||||||
has_many :comments
|
|
||||||
has_one :author
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class CommentSerializer < ActiveModel::Serializer
|
|
||||||
attributes :name, :body
|
|
||||||
|
|
||||||
belongs_to :post
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The attribute names are a **whitelist** of attributes to be serialized.
|
|
||||||
|
|
||||||
The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between
|
|
||||||
resources. By default, when you serialize a `Post`, you will get its `Comments`
|
|
||||||
as well.
|
|
||||||
|
|
||||||
For more information, see [Serializers](/docs/general/serializers.md).
|
|
||||||
|
|
||||||
### Namespaced Models
|
|
||||||
|
|
||||||
When serializing a model inside a namespace, such as `Api::V1::Post`, ActiveModelSerializers will expect the corresponding serializer to be inside the same namespace (namely `Api::V1::PostSerializer`).
|
|
||||||
|
|
||||||
### Model Associations and Nested Serializers
|
|
||||||
|
|
||||||
When declaring a serializer for a model with associations, such as:
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
has_many :comments
|
|
||||||
end
|
|
||||||
```
|
|
||||||
ActiveModelSerializers will look for `PostSerializer::CommentSerializer` in priority, and fall back to `::CommentSerializer` in case the former does not exist. This allows for more control over the way a model gets serialized as an association of an other model.
|
|
||||||
|
|
||||||
For example, in the following situation:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class CommentSerializer < ActiveModel::Serializer
|
|
||||||
attributes :body, :date, :nb_likes
|
|
||||||
end
|
|
||||||
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
has_many :comments
|
|
||||||
class CommentSerializer < ActiveModel::Serializer
|
|
||||||
attributes :body_short
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
ActiveModelSerializers will use `PostSerializer::CommentSerializer` (thus including only the `:body_short` attribute) when serializing a `Comment` as part of a `Post`, but use `::CommentSerializer` when serializing a `Comment` directly (thus including `:body, :date, :nb_likes`).
|
|
||||||
|
|
||||||
### Extending a Base `ApplicationSerializer`
|
|
||||||
|
|
||||||
By default, new serializers descend from `ActiveModel::Serializer`. However, if
|
|
||||||
you wish to share behavior across your serializers, you can create an
|
|
||||||
`ApplicationSerializer` at `app/serializers/application_serializer.rb`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class ApplicationSerializer < ActiveModel::Serializer
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Then any newly-generated serializers will automatically descend from
|
|
||||||
`ApplicationSerializer`.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rails g serializer post
|
|
||||||
```
|
|
||||||
|
|
||||||
Now generates:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ApplicationSerializer
|
|
||||||
attributes :id
|
|
||||||
end
|
|
||||||
````
|
|
||||||
|
|
||||||
## Rails Integration
|
|
||||||
|
|
||||||
ActiveModelSerializers will automatically integrate with your Rails app,
|
|
||||||
so you won't need to update your controller.
|
|
||||||
This is a example of how the controller will look:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsController < ApplicationController
|
|
||||||
|
|
||||||
def show
|
|
||||||
@post = Post.find(params[:id])
|
|
||||||
render json: @post
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to use Rails url helpers for link generation, e.g., `link(:resources) { resources_url }`, ensure your application sets
|
|
||||||
`Rails.application.routes.default_url_options`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Rails.application.routes.default_url_options = {
|
|
||||||
host: 'example.com'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Instrumentation
|
|
||||||
|
|
||||||
ActiveModelSerializers uses the
|
|
||||||
[ActiveSupport::Notification API](http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event),
|
|
||||||
which allows for subscribing to events, such as for logging.
|
|
||||||
|
|
||||||
## Events
|
|
||||||
|
|
||||||
Name:
|
|
||||||
|
|
||||||
`render.active_model_serializers`
|
|
||||||
|
|
||||||
Payload (example):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
{
|
|
||||||
serializer: PostSerializer,
|
|
||||||
adapter: ActiveModelSerializers::Adapter::Attributes
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Subscribing:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |name, started, finished, unique_id, data|
|
|
||||||
# whatever
|
|
||||||
end
|
|
||||||
ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |*args|
|
|
||||||
event = ActiveSupport::Notifications::Event.new(*args)
|
|
||||||
# event.payload
|
|
||||||
# whatever
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## [LogSubscriber](http://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html)
|
|
||||||
|
|
||||||
ActiveModelSerializers includes an `ActiveModelSerializers::LogSubscriber` that attaches to
|
|
||||||
`render.active_model_serializers`.
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Key Transforms
|
|
||||||
|
|
||||||
Key Transforms modify the casing of keys and keys referenced in values in
|
|
||||||
serialized responses.
|
|
||||||
|
|
||||||
Provided key transforms:
|
|
||||||
|
|
||||||
| Option | Result |
|
|
||||||
|----|----|
|
|
||||||
| `:camel` | ExampleKey |
|
|
||||||
| `:camel_lower` | exampleKey |
|
|
||||||
| `:dash` | example-key |
|
|
||||||
| `:unaltered` | the original, unaltered key |
|
|
||||||
| `:underscore` | example_key |
|
|
||||||
| `nil` | use the adapter default |
|
|
||||||
|
|
||||||
Key translation precedence is as follows:
|
|
||||||
|
|
||||||
##### Adapter option
|
|
||||||
|
|
||||||
`key_transform` is provided as an option via render.
|
|
||||||
|
|
||||||
```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower```
|
|
||||||
|
|
||||||
##### Configuration option
|
|
||||||
|
|
||||||
`key_transform` is set in `ActiveModelSerializers.config.key_transform`.
|
|
||||||
|
|
||||||
```ActiveModelSerializers.config.key_transform = :camel_lower```
|
|
||||||
|
|
||||||
##### Adapter default
|
|
||||||
|
|
||||||
Each adapter has a default transform configured:
|
|
||||||
|
|
||||||
| Adapter | Default Key Transform |
|
|
||||||
|----|----|
|
|
||||||
| `Json` | `:unaltered` |
|
|
||||||
| `JsonApi` | `:dash` |
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
|
|
||||||
The default logger in a Rails application will be `Rails.logger`.
|
|
||||||
|
|
||||||
When there is no `Rails.logger`, the default logger is an instance of
|
|
||||||
`ActiveSupport::TaggedLogging` logging to STDOUT.
|
|
||||||
|
|
||||||
You may customize the logger in an initializer, for example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
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)
|
|
||||||
```
|
|
||||||
@ -1,293 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Rendering
|
|
||||||
|
|
||||||
### Implicit Serializer
|
|
||||||
|
|
||||||
In your controllers, when you use `render :json`, Rails will now first search
|
|
||||||
for a serializer for the object and use it if available.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsController < ApplicationController
|
|
||||||
def show
|
|
||||||
@post = Post.find(params[:id])
|
|
||||||
|
|
||||||
render json: @post
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
|
||||||
it exists, use it to serialize the `Post`.
|
|
||||||
|
|
||||||
### Explicit Serializer
|
|
||||||
|
|
||||||
If you wish to use a serializer other than the default, you can explicitly pass it to the renderer.
|
|
||||||
|
|
||||||
#### 1. For a resource:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @post, serializer: PostPreviewSerializer
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. For a resource collection:
|
|
||||||
|
|
||||||
Specify the serializer for each resource with `each_serializer`
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, each_serializer: PostPreviewSerializer
|
|
||||||
```
|
|
||||||
|
|
||||||
The default serializer for collections is `CollectionSerializer`.
|
|
||||||
|
|
||||||
Specify the collection serializer with the `serializer` option.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, serializer: CollectionSerializer, each_serializer: PostPreviewSerializer
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serializing non-ActiveRecord objects
|
|
||||||
|
|
||||||
See [README](../../README.md#what-does-a-serializable-resource-look-like)
|
|
||||||
|
|
||||||
## SerializableResource options
|
|
||||||
|
|
||||||
See [README](../../README.md#activemodelserializersserializableresource)
|
|
||||||
|
|
||||||
### adapter_opts
|
|
||||||
|
|
||||||
#### fields
|
|
||||||
|
|
||||||
If you are using `json` or `attributes` adapter
|
|
||||||
```ruby
|
|
||||||
render json: @user, fields: [:access_token]
|
|
||||||
```
|
|
||||||
|
|
||||||
See [Fields](fields.md) for more information.
|
|
||||||
|
|
||||||
#### adapter
|
|
||||||
|
|
||||||
This option lets you explicitly set the adapter to be used by passing a registered adapter. Your options are `:attributes`, `:json`, and `:json_api`.
|
|
||||||
|
|
||||||
```
|
|
||||||
ActiveModel::Serializer.config.adapter = :json_api
|
|
||||||
```
|
|
||||||
|
|
||||||
#### key_transform
|
|
||||||
|
|
||||||
```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower```
|
|
||||||
|
|
||||||
See [Key Transforms](key_transforms.md) for more information.
|
|
||||||
|
|
||||||
#### meta
|
|
||||||
|
|
||||||
A `meta` member can be used to include non-standard meta-information. `meta` can
|
|
||||||
be utilized in several levels in a response.
|
|
||||||
|
|
||||||
##### Top-level
|
|
||||||
|
|
||||||
To set top-level `meta` in a response, specify it in the `render` call.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @post, meta: { total: 10 }
|
|
||||||
```
|
|
||||||
|
|
||||||
The key can be customized using `meta_key` option.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @post, meta: { total: 10 }, meta_key: "custom_meta"
|
|
||||||
```
|
|
||||||
|
|
||||||
`meta` will only be included in your response if you are using an Adapter that
|
|
||||||
supports `root`, e.g., `JsonApi` and `Json` adapters. The default adapter,
|
|
||||||
`Attributes` does not have `root`.
|
|
||||||
|
|
||||||
|
|
||||||
##### Resource-level
|
|
||||||
|
|
||||||
To set resource-level `meta` in a response, define meta in a serializer with one
|
|
||||||
of the following methods:
|
|
||||||
|
|
||||||
As a single, static string.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
meta stuff: 'value'
|
|
||||||
```
|
|
||||||
|
|
||||||
As a block containing a Hash.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
meta do
|
|
||||||
{
|
|
||||||
rating: 4,
|
|
||||||
comments_count: object.comments.count
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### links
|
|
||||||
|
|
||||||
If you wish to use Rails url helpers for link generation, e.g., `link(:resources) { resources_url }`, ensure your application sets
|
|
||||||
`Rails.application.routes.default_url_options`.
|
|
||||||
|
|
||||||
##### Top-level
|
|
||||||
|
|
||||||
JsonApi supports a [links object](http://jsonapi.org/format/#document-links) to be specified at top-level, that you can specify in the `render`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
links_object = {
|
|
||||||
href: "http://example.com/api/posts",
|
|
||||||
meta: {
|
|
||||||
count: 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
render json: @posts, links: links_object
|
|
||||||
```
|
|
||||||
|
|
||||||
That's the result:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"type": "posts",
|
|
||||||
"id": "1",
|
|
||||||
"attributes": {
|
|
||||||
"title": "JSON API is awesome!",
|
|
||||||
"body": "You should be using JSON API",
|
|
||||||
"created": "2015-05-22T14:56:29.000Z",
|
|
||||||
"updated": "2015-05-22T14:56:28.000Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"links": {
|
|
||||||
"href": "http://example.com/api/posts",
|
|
||||||
"meta": {
|
|
||||||
"count": 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This feature is specific to JsonApi, so you have to use the use the [JsonApi Adapter](adapters.md#jsonapi)
|
|
||||||
|
|
||||||
|
|
||||||
##### Resource-level
|
|
||||||
|
|
||||||
In your serializer, define each link in one of the following methods:
|
|
||||||
|
|
||||||
As a static string
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
link :link_name, 'https://example.com/resource'
|
|
||||||
```
|
|
||||||
|
|
||||||
As a block to be evaluated. When using Rails, URL helpers are available.
|
|
||||||
Ensure your application sets `Rails.application.routes.default_url_options`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
link :link_name_ do
|
|
||||||
"https://example.com/resource/#{object.id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
link(:link_name) { "https://example.com/resource/#{object.id}" }
|
|
||||||
|
|
||||||
link(:link_name) { resource_url(object) }
|
|
||||||
|
|
||||||
link(:link_name) { url_for(controller: 'controller_name', action: 'index', only_path: false) }
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### serializer_opts
|
|
||||||
|
|
||||||
#### include
|
|
||||||
|
|
||||||
See [Adapters: Include Option](/docs/general/adapters.md#include-option).
|
|
||||||
|
|
||||||
#### Overriding the root key
|
|
||||||
|
|
||||||
Overriding the resource root only applies when using the JSON adapter.
|
|
||||||
|
|
||||||
Normally, the resource root is derived from the class name of the resource being serialized.
|
|
||||||
e.g. `UserPostSerializer.new(UserPost.new)` will be serialized with the root `user_post` or `user_posts` according the adapter collection pluralization rules.
|
|
||||||
|
|
||||||
When using the JSON adapter in your initializer (ActiveModelSerializers.config.adapter = :json), or passing in the adapter in your render call, you can specify the root by passing it as an argument to `render`. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @user_post, root: "admin_post", adapter: :json
|
|
||||||
```
|
|
||||||
|
|
||||||
This will be rendered as:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"admin_post": {
|
|
||||||
"title": "how to do open source"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Note: the `Attributes` adapter (default) does not include a resource root. You also will not be able to create a single top-level root if you are using the :json_api adapter.
|
|
||||||
|
|
||||||
#### namespace
|
|
||||||
|
|
||||||
The namespace for serializer lookup is based on the controller.
|
|
||||||
|
|
||||||
To configure the implicit namespace, in your controller, create a before filter
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
before_action do
|
|
||||||
self.namespace_for_serializer = Api::V2
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
`namespace` can also be passed in as a render option:
|
|
||||||
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
@post = Post.first
|
|
||||||
render json: @post, namespace: Api::V2
|
|
||||||
```
|
|
||||||
|
|
||||||
This tells the serializer lookup to check for the existence of `Api::V2::PostSerializer`, and if any relations are rendered with `@post`, they will also utilize the `Api::V2` namespace.
|
|
||||||
|
|
||||||
The `namespace` can be any object whose namespace can be represented by string interpolation (i.e. by calling to_s)
|
|
||||||
- Module `Api::V2`
|
|
||||||
- String `'Api::V2'`
|
|
||||||
- Symbol `:'Api::V2'`
|
|
||||||
|
|
||||||
Note that by using a string and symbol, Ruby will assume the namespace is defined at the top level.
|
|
||||||
|
|
||||||
|
|
||||||
#### serializer
|
|
||||||
|
|
||||||
Specify which serializer to use if you want to use a serializer other than the default.
|
|
||||||
|
|
||||||
For a single resource:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
@post = Post.first
|
|
||||||
render json: @post, serializer: SpecialPostSerializer
|
|
||||||
```
|
|
||||||
|
|
||||||
To specify which serializer to use on individual items in a collection (i.e., an `index` action), use `each_serializer`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
@posts = Post.all
|
|
||||||
render json: @posts, each_serializer: SpecialPostSerializer
|
|
||||||
```
|
|
||||||
|
|
||||||
#### scope
|
|
||||||
|
|
||||||
See [Serializers: Scope](/docs/general/serializers.md#scope).
|
|
||||||
|
|
||||||
#### scope_name
|
|
||||||
|
|
||||||
See [Serializers: Scope](/docs/general/serializers.md#scope).
|
|
||||||
|
|
||||||
## Using a serializer without `render`
|
|
||||||
|
|
||||||
See [Usage outside of a controller](../howto/outside_controller_use.md#serializing-before-controller-render).
|
|
||||||
|
|
||||||
## Pagination
|
|
||||||
|
|
||||||
See [How to add pagination links](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md).
|
|
||||||
@ -1,480 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Serializers
|
|
||||||
|
|
||||||
Given a serializer class:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class SomeSerializer < ActiveModel::Serializer
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The following methods may be defined in it:
|
|
||||||
|
|
||||||
### Attributes
|
|
||||||
|
|
||||||
#### ::attributes
|
|
||||||
|
|
||||||
Serialization of the resource `title` and `body`
|
|
||||||
|
|
||||||
| In Serializer | #attributes |
|
|
||||||
|---------------------------- |-------------|
|
|
||||||
| `attributes :title, :body` | `{ title: 'Some Title', body: 'Some Body' }`
|
|
||||||
| `attributes :title, :body`<br>`def body "Special #{object.body}" end` | `{ title: 'Some Title', body: 'Special Some Body' }`
|
|
||||||
|
|
||||||
|
|
||||||
#### ::attribute
|
|
||||||
|
|
||||||
Serialization of the resource `title`
|
|
||||||
|
|
||||||
| In Serializer | #attributes |
|
|
||||||
|---------------------------- |-------------|
|
|
||||||
| `attribute :title` | `{ title: 'Some Title' } `
|
|
||||||
| `attribute :title, key: :name` | `{ name: 'Some Title' } `
|
|
||||||
| `attribute(:title) { 'A Different Title'}` | `{ title: 'A Different Title' } `
|
|
||||||
| `attribute :title`<br>`def title 'A Different Title' end` | `{ title: 'A Different Title' }`
|
|
||||||
|
|
||||||
An `if` or `unless` option can make an attribute conditional. It takes a symbol of a method name on the serializer, or a lambda literal.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
attribute :private_data, if: :is_current_user?
|
|
||||||
attribute :another_private_data, if: -> { scope.admin? }
|
|
||||||
|
|
||||||
def is_current_user?
|
|
||||||
object.id == current_user.id
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Associations
|
|
||||||
|
|
||||||
The interface for associations is, generically:
|
|
||||||
|
|
||||||
> `association_type(association_name, options, &block)`
|
|
||||||
|
|
||||||
Where:
|
|
||||||
|
|
||||||
- `association_type` may be `has_one`, `has_many`, `belongs_to`.
|
|
||||||
- `association_name` is a method name the serializer calls.
|
|
||||||
- optional: `options` may be:
|
|
||||||
- `key:` The name used for the serialized association.
|
|
||||||
- `serializer:`
|
|
||||||
- `if:`
|
|
||||||
- `unless:`
|
|
||||||
- `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
|
|
||||||
- `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.
|
|
||||||
- prevents `association_name` method from being called.
|
|
||||||
- return value of block is used as the association value.
|
|
||||||
- yields the `serializer` to the block.
|
|
||||||
- `include_data false` prevents the `data` key from being rendered in the JSON API relationship.
|
|
||||||
|
|
||||||
#### ::has_one
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
has_one :bio
|
|
||||||
has_one :blog, key: :site
|
|
||||||
has_one :maker, virtual_value: { id: 1 }
|
|
||||||
|
|
||||||
has_one :blog do |serializer|
|
|
||||||
serializer.cached_blog
|
|
||||||
end
|
|
||||||
|
|
||||||
def cached_blog
|
|
||||||
cache_store.fetch("cached_blog:#{object.updated_at}") do
|
|
||||||
Blog.find(object.blog_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
has_one :blog, if: :show_blog?
|
|
||||||
# you can also use a string or lambda
|
|
||||||
# has_one :blog, if: 'scope.admin?'
|
|
||||||
# has_one :blog, if: -> (serializer) { serializer.scope.admin? }
|
|
||||||
# has_one :blog, if: -> { scope.admin? }
|
|
||||||
|
|
||||||
def show_blog?
|
|
||||||
scope.admin?
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ::has_many
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
has_many :comments
|
|
||||||
has_many :comments, key: :reviews
|
|
||||||
has_many :comments, serializer: CommentPreviewSerializer
|
|
||||||
has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]
|
|
||||||
has_many :comments, key: :last_comments do
|
|
||||||
last(1)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ::belongs_to
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
belongs_to :author, serializer: AuthorPreviewSerializer
|
|
||||||
belongs_to :author, key: :writer
|
|
||||||
belongs_to :post
|
|
||||||
belongs_to :blog
|
|
||||||
def blog
|
|
||||||
Blog.new(id: 999, name: 'Custom blog')
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Polymorphic Relationships
|
|
||||||
|
|
||||||
Polymorphic relationships are serialized by specifying the relationship, like any other association. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PictureSerializer < ActiveModel::Serializer
|
|
||||||
has_one :imageable
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
You can specify the serializers by [overriding serializer_for](serializers.md#overriding-association-serializer-lookup). For more context about polymorphic relationships, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.
|
|
||||||
|
|
||||||
### Caching
|
|
||||||
|
|
||||||
#### ::cache
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
cache key: 'post', expires_in: 0.1, skip_digest: true
|
|
||||||
cache expires_in: 1.day, skip_digest: true
|
|
||||||
cache key: 'writer', skip_digest: true
|
|
||||||
cache only: [:name], skip_digest: true
|
|
||||||
cache except: [:content], skip_digest: true
|
|
||||||
cache key: 'blog'
|
|
||||||
cache only: [:id]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### #cache_key
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Uses a custom non-time-based cache key
|
|
||||||
def cache_key
|
|
||||||
"#{self.class.name.downcase}/#{self.id}"
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
#### ::type
|
|
||||||
|
|
||||||
When using the `:json_api` adapter, the `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer.
|
|
||||||
|
|
||||||
When using the `:json` adapter, the `::type` method defines the name of the root element.
|
|
||||||
|
|
||||||
It either takes a `String` or `Symbol` as parameter.
|
|
||||||
|
|
||||||
Note: This method is useful only when using the `:json_api` or `:json` adapter.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
```ruby
|
|
||||||
class UserProfileSerializer < ActiveModel::Serializer
|
|
||||||
type 'profile'
|
|
||||||
|
|
||||||
attribute :name
|
|
||||||
end
|
|
||||||
class AuthorProfileSerializer < ActiveModel::Serializer
|
|
||||||
type :profile
|
|
||||||
|
|
||||||
attribute :name
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
With the `:json_api` adapter, the previous serializers would be rendered as:
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"id": "1",
|
|
||||||
"type": "profile",
|
|
||||||
"attributes": {
|
|
||||||
"name": "Julia"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the `:json` adapter, the previous serializer would be rendered as:
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"profile": {
|
|
||||||
"name": "Julia"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ::link
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
link :self do
|
|
||||||
href "https://example.com/link_author/#{object.id}"
|
|
||||||
end
|
|
||||||
link(:author) { link_author_url(object) }
|
|
||||||
link(:link_authors) { link_authors_url }
|
|
||||||
link :other, 'https://example.com/resource'
|
|
||||||
link(:posts) { link_author_posts_url(object) }
|
|
||||||
```
|
|
||||||
|
|
||||||
#### #object
|
|
||||||
|
|
||||||
The object being serialized.
|
|
||||||
|
|
||||||
#### #root
|
|
||||||
|
|
||||||
Resource root which is included in `JSON` adapter. As you can see at [Adapters Document](adapters.md), `Attribute` adapter (default) and `JSON API` adapter does not include root at top level.
|
|
||||||
By default, the resource root comes from the `model_name` of the serialized object's class.
|
|
||||||
|
|
||||||
There are several ways to specify root:
|
|
||||||
* [Overriding the root key](rendering.md#overriding-the-root-key)
|
|
||||||
* [Setting `type`](serializers.md#type)
|
|
||||||
* Specifying the `root` option, e.g. `root: 'specific_name'`, during the serializer's initialization:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::SerializableResource.new(foo, root: 'bar')
|
|
||||||
```
|
|
||||||
|
|
||||||
#### #scope
|
|
||||||
|
|
||||||
Allows you to include in the serializer access to an external method.
|
|
||||||
|
|
||||||
It's intended to provide an authorization context to the serializer, so that
|
|
||||||
you may e.g. show an admin all comments on a post, else only published comments.
|
|
||||||
|
|
||||||
- `scope` is a method on the serializer instance that comes from `options[:scope]`. It may be nil.
|
|
||||||
- `scope_name` is an option passed to the new serializer (`options[:scope_name]`). The serializer
|
|
||||||
defines a method with that name that calls the `scope`, e.g. `def current_user; scope; end`.
|
|
||||||
Note: it does not define the method if the serializer instance responds to it.
|
|
||||||
|
|
||||||
That's a lot of words, so here's some examples:
|
|
||||||
|
|
||||||
First, let's assume the serializer is instantiated in the controller, since that's the usual scenario.
|
|
||||||
We'll refer to the serialization context as `controller`.
|
|
||||||
|
|
||||||
| options | `Serializer#scope` | method definition |
|
|
||||||
|-------- | ------------------|--------------------|
|
|
||||||
| `scope: current_user, scope_name: :current_user` | `current_user` | `Serializer#current_user` calls `controller.current_user`
|
|
||||||
| `scope: view_context, scope_name: :view_context` | `view_context` | `Serializer#view_context` calls `controller.view_context`
|
|
||||||
|
|
||||||
We can take advantage of the scope to customize the objects returned based
|
|
||||||
on the current user (scope).
|
|
||||||
|
|
||||||
For example, we can limit the posts the current user sees to those they created:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :title, :body
|
|
||||||
|
|
||||||
# scope comments to those created_by the current user
|
|
||||||
has_many :comments do
|
|
||||||
object.comments.where(created_by: current_user)
|
|
||||||
end
|
|
||||||
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).
|
|
||||||
|
|
||||||
##### Controller Authorization Context
|
|
||||||
|
|
||||||
In the controller, the scope/scope_name options are equal to
|
|
||||||
the [`serialization_scope`method](https://github.com/rails-api/active_model_serializers/blob/d02cd30fe55a3ea85e1d351b6e039620903c1871/lib/action_controller/serialization.rb#L13-L20),
|
|
||||||
which is `:current_user`, by default.
|
|
||||||
|
|
||||||
Specifically, the `scope_name` is defaulted to `:current_user`, and may be set as
|
|
||||||
`serialization_scope :view_context`. The `scope` is set to `send(scope_name)` when `scope_name` is
|
|
||||||
present and the controller responds to `scope_name`.
|
|
||||||
|
|
||||||
Thus, in a serializer, the controller provides `current_user` as the
|
|
||||||
current authorization scope when you call `render :json`.
|
|
||||||
|
|
||||||
**IMPORTANT**: Since the scope is set at render, you may want to customize it so that `current_user` isn't
|
|
||||||
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`.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
class SomeController < ActionController::Base
|
|
||||||
+ serialization_scope :view_context
|
|
||||||
|
|
||||||
def current_user
|
|
||||||
User.new(id: 2, name: 'Bob', admin: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
user = User.new(id: 1, name: 'Pete')
|
|
||||||
render json: user, serializer: AdminUserSerializer, adapter: :json_api
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
We could then use the controller method `view_context` in our serializer, like so:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
class AdminUserSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :name, :can_edit
|
|
||||||
|
|
||||||
def can_edit?
|
|
||||||
+ view_context.current_user.admin?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
So that when we render the `#edit` action, we'll get
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"data":{"id":"1","type":"users","attributes":{"name":"Pete","can_edit":true}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `can_edit` is `view_context.current_user.admin?` (true).
|
|
||||||
|
|
||||||
You can also tell what to set as `serialization_scope` for specific actions.
|
|
||||||
|
|
||||||
For example, use `admin_user` only for `Admin::PostSerializer` and `current_user` for rest.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsController < ActionController::Base
|
|
||||||
|
|
||||||
before_action only: :edit do
|
|
||||||
self.class.serialization_scope :admin_user
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: @post, serializer: PostSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@post.save
|
|
||||||
render json: @post, serializer: Admin::PostSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def admin_user
|
|
||||||
User.new(id: 2, name: 'Bob', admin: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_user
|
|
||||||
User.new(id: 2, name: 'Bob', admin: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### #read_attribute_for_serialization(key)
|
|
||||||
|
|
||||||
The serialized value for a given key. e.g. `read_attribute_for_serialization(:title) #=> 'Hello World'`
|
|
||||||
|
|
||||||
#### #links
|
|
||||||
|
|
||||||
Allows you to modify the `links` node. By default, this node will be populated with the attributes set using the [::link](#link) method. Using `links: nil` will remove the `links` node.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@post,
|
|
||||||
adapter: :json_api,
|
|
||||||
links: {
|
|
||||||
self: {
|
|
||||||
href: 'http://example.com/posts',
|
|
||||||
meta: {
|
|
||||||
stuff: 'value'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### #json_key
|
|
||||||
|
|
||||||
Returns the key used by the adapter as the resource root. See [root](#root) for more information.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Given two models, a `Post(title: string, body: text)` and a
|
|
||||||
`Comment(name: string, body: text, post_id: integer)`, you will have two
|
|
||||||
serializers:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
cache key: 'posts', expires_in: 3.hours
|
|
||||||
attributes :title, :body
|
|
||||||
|
|
||||||
has_many :comments
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class CommentSerializer < ActiveModel::Serializer
|
|
||||||
attributes :name, :body
|
|
||||||
|
|
||||||
belongs_to :post
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Generally speaking, you, as a user of ActiveModelSerializers, will write (or generate) these
|
|
||||||
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)
|
|
||||||
|
|
||||||
## Overriding association methods
|
|
||||||
|
|
||||||
To override an association, call `has_many`, `has_one` or `belongs_to` with a block:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
has_many :comments do
|
|
||||||
object.comments.active
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Overriding attribute methods
|
|
||||||
|
|
||||||
To override an attribute, call `attribute` with a block:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
attribute :body do
|
|
||||||
object.body.downcase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Overriding association serializer lookup
|
|
||||||
|
|
||||||
If you want to define a specific serializer lookup for your associations, you can override
|
|
||||||
the `ActiveModel::Serializer.serializer_for` method to return a serializer class based on defined conditions.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class MySerializer < ActiveModel::Serializer
|
|
||||||
def self.serializer_for(model, options)
|
|
||||||
return SparseAdminSerializer if model.class == 'Admin'
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# the rest of the serializer
|
|
||||||
end
|
|
||||||
```
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 276 KiB |
@ -1,138 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to add pagination links
|
|
||||||
|
|
||||||
### JSON API adapter
|
|
||||||
|
|
||||||
Pagination links will be included in your response automatically as long as
|
|
||||||
the resource is paginated and if you are using the ```JsonApi``` adapter.
|
|
||||||
|
|
||||||
If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari)
|
|
||||||
or [WillPaginate](https://github.com/mislav/will_paginate).
|
|
||||||
|
|
||||||
Although the other adapters do not have this feature, it is possible to
|
|
||||||
implement pagination links to `JSON` adapter. For more information about it,
|
|
||||||
please check our docs.
|
|
||||||
|
|
||||||
###### Kaminari examples
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
#array
|
|
||||||
@posts = Kaminari.paginate_array([1, 2, 3]).page(3).per(1)
|
|
||||||
render json: @posts
|
|
||||||
|
|
||||||
#active_record
|
|
||||||
@posts = Post.page(3).per(1)
|
|
||||||
render json: @posts
|
|
||||||
```
|
|
||||||
|
|
||||||
###### WillPaginate examples
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
#array
|
|
||||||
@posts = [1,2,3].paginate(page: 3, per_page: 1)
|
|
||||||
render json: @posts
|
|
||||||
|
|
||||||
#active_record
|
|
||||||
@posts = Post.page(3).per_page(1)
|
|
||||||
render json: @posts
|
|
||||||
```
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter = :json_api
|
|
||||||
```
|
|
||||||
|
|
||||||
ex:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"type": "articles",
|
|
||||||
"id": "3",
|
|
||||||
"attributes": {
|
|
||||||
"title": "JSON API paints my bikeshed!",
|
|
||||||
"body": "The shortest article. Ever.",
|
|
||||||
"created": "2015-05-22T14:56:29.000Z",
|
|
||||||
"updated": "2015-05-22T14:56:28.000Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"links": {
|
|
||||||
"self": "http://example.com/articles?page[number]=3&page[size]=1",
|
|
||||||
"first": "http://example.com/articles?page[number]=1&page[size]=1",
|
|
||||||
"prev": "http://example.com/articles?page[number]=2&page[size]=1",
|
|
||||||
"next": "http://example.com/articles?page[number]=4&page[size]=1",
|
|
||||||
"last": "http://example.com/articles?page[number]=13&page[size]=1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
ActiveModelSerializers pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate).
|
|
||||||
|
|
||||||
|
|
||||||
### JSON adapter
|
|
||||||
|
|
||||||
If you are not using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
|
|
||||||
|
|
||||||
Add this method to your base API controller.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def pagination_dict(collection)
|
|
||||||
{
|
|
||||||
current_page: collection.current_page,
|
|
||||||
next_page: collection.next_page,
|
|
||||||
prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
|
|
||||||
total_pages: collection.total_pages,
|
|
||||||
total_count: collection.total_count
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, use it on your render method.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: posts, meta: pagination_dict(posts)
|
|
||||||
```
|
|
||||||
|
|
||||||
ex.
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"posts": [
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"title": "JSON API paints my bikeshed!",
|
|
||||||
"body": "The shortest article. Ever."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"current_page": 3,
|
|
||||||
"next_page": 4,
|
|
||||||
"prev_page": 2,
|
|
||||||
"total_pages": 10,
|
|
||||||
"total_count": 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also achieve the same result if you have a helper method that adds the pagination info in the meta tag. For instance, in your action specify a custom serializer.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@posts)
|
|
||||||
```
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
#expects pagination!
|
|
||||||
def meta_attributes(collection, extra_meta = {})
|
|
||||||
{
|
|
||||||
current_page: collection.current_page,
|
|
||||||
next_page: collection.next_page,
|
|
||||||
prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
|
|
||||||
total_pages: collection.total_pages,
|
|
||||||
total_count: collection.total_count
|
|
||||||
}.merge(extra_meta)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Attributes adapter
|
|
||||||
|
|
||||||
This adapter does not allow us to use `meta` key, due to that it is not possible to add pagination links.
|
|
||||||
@ -1,140 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to add relationship links
|
|
||||||
|
|
||||||
ActiveModelSerializers offers you many ways to add links in your JSON, depending on your needs.
|
|
||||||
The most common use case for links is supporting nested resources.
|
|
||||||
|
|
||||||
The following examples are without included relationship data (`include` param is empty),
|
|
||||||
specifically the following Rails controller was used for these examples:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class Api::V1::UsersController < ApplicationController
|
|
||||||
def show
|
|
||||||
render jsonapi: User.find(params[:id]),
|
|
||||||
serializer: Api::V1::UserSerializer,
|
|
||||||
include: []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Bear in mind though that ActiveModelSerializers are [framework-agnostic](outside_controller_use.md), Rails is just a common example here.
|
|
||||||
|
|
||||||
### Links as an attribute of a resource
|
|
||||||
**This is applicable to JSON and Attributes adapters**
|
|
||||||
|
|
||||||
You can define an attribute in the resource, named `links`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
||||||
include Rails.application.routes.url_helpers
|
|
||||||
|
|
||||||
attributes :id, :name
|
|
||||||
|
|
||||||
attribute :links do
|
|
||||||
id = object.id
|
|
||||||
{
|
|
||||||
self: api_v1_user_path(id),
|
|
||||||
microposts: api_v1_microposts_path(user_id: id)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Using the `JSON` adapter, this will result in:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user": {
|
|
||||||
"id": "1",
|
|
||||||
"name": "John",
|
|
||||||
"links": {
|
|
||||||
"self": "/api/v1/users/1",
|
|
||||||
"microposts": "/api/v1/microposts?user_id=1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Links as a property of the resource definiton
|
|
||||||
**This is only applicable to JSONAPI adapter**
|
|
||||||
|
|
||||||
You can use the `link` class method to define the links you need in the resource's primary data.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :name
|
|
||||||
|
|
||||||
link(:self) { api_v1_user_path(object.id) }
|
|
||||||
link(:microposts) { api_v1_microposts_path(user_id: object.id) }
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Using the `JSONAPI` adapter, this will result in:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"id": "1",
|
|
||||||
"type": "users",
|
|
||||||
"attributes": {
|
|
||||||
"name": "Example User"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"self": "/api/v1/users/1",
|
|
||||||
"microposts": "/api/v1/microposts?user_id=1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Links that follow the JSONAPI spec
|
|
||||||
**This is only applicable to JSONAPI adapter**
|
|
||||||
|
|
||||||
If you have a JSONAPI-strict client that you are working with (like `ember-data`)
|
|
||||||
you need to construct the links inside the relationships. Also the link to fetch the
|
|
||||||
relationship data must be under the `related` attribute, whereas to manipulate the
|
|
||||||
relationship (in case of many-to-many relationship) must be under the `self` attribute.
|
|
||||||
|
|
||||||
You can find more info in the [spec](http://jsonapi.org/format/#document-resource-object-relationships).
|
|
||||||
|
|
||||||
Here is how you can do this:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :name
|
|
||||||
|
|
||||||
has_many :microposts, serializer: Api::V1::MicropostSerializer do
|
|
||||||
link(:related) { api_v1_microposts_path(user_id: object.id) }
|
|
||||||
|
|
||||||
microposts = object.microposts
|
|
||||||
# The following code is needed to avoid n+1 queries.
|
|
||||||
# Core devs are working to remove this necessity.
|
|
||||||
# See: https://github.com/rails-api/active_model_serializers/issues/1325
|
|
||||||
microposts.loaded? ? microposts : microposts.none
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
This will result in:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"id": "1",
|
|
||||||
"type": "users",
|
|
||||||
"attributes": {
|
|
||||||
"name": "Example User"
|
|
||||||
},
|
|
||||||
"relationships": {
|
|
||||||
"microposts": {
|
|
||||||
"data": [],
|
|
||||||
"links": {
|
|
||||||
"related": "/api/v1/microposts?user_id=1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to add root key
|
|
||||||
|
|
||||||
Add the root key to your API is quite simple with ActiveModelSerializers. The **Adapter** is what determines the format of your JSON response. The default adapter is the ```Attributes``` which doesn't have the root key, so your response is something similar to:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"title": "Awesome Post Tile",
|
|
||||||
"content": "Post content"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In order to add the root key you need to use the ```JSON``` Adapter, you can change this in an initializer:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter = :json
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
ex:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"post": {
|
|
||||||
"id": 1,
|
|
||||||
"title": "Awesome Post Tile",
|
|
||||||
"content": "Post content"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
or if it returns a collection:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"posts": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"title": "Awesome Post Tile",
|
|
||||||
"content": "Post content"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"title": "Another Post Tile",
|
|
||||||
"content": "Another post content"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[There are several ways to specify root](../general/serializers.md#root) when using the JSON adapter.
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
The ActiveModelSerializers grape formatter relies on the existence of `env['grape.request']` which is implemeted by `Grape::Middleware::Globals`. You can meet his dependency by calling it before mounting the endpoints.
|
|
||||||
|
|
||||||
In the simpliest way:
|
|
||||||
|
|
||||||
```
|
|
||||||
class API < Grape::API
|
|
||||||
# @note Make sure this is above you're first +mount+
|
|
||||||
use Grape::Middleware::Globals
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
or more like what is shown in current Grape tutorials:
|
|
||||||
|
|
||||||
```
|
|
||||||
module MyApi
|
|
||||||
class ApiBase < Grape::API
|
|
||||||
use Grape::Middleware::Globals
|
|
||||||
|
|
||||||
require 'grape/active_model_serializers'
|
|
||||||
include Grape::ActiveModelSerializers
|
|
||||||
|
|
||||||
mount MyApi::V1::ApiBase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
You could meet this dependency with your own middleware. The invocation might look like:
|
|
||||||
|
|
||||||
```
|
|
||||||
module MyApi
|
|
||||||
class ApiBase < Grape::API
|
|
||||||
use My::Middleware::Thingamabob
|
|
||||||
|
|
||||||
require 'grape/active_model_serializers'
|
|
||||||
include Grape::ActiveModelSerializers
|
|
||||||
|
|
||||||
mount MyApi::V1::ApiBase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
## Using ActiveModelSerializers Outside Of A Controller
|
|
||||||
|
|
||||||
### Serializing a resource
|
|
||||||
|
|
||||||
In ActiveModelSerializers versions 0.10 or later, serializing resources outside of the controller context is fairly simple:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Create our resource
|
|
||||||
post = Post.create(title: "Sample post", body: "I love Active Model Serializers!")
|
|
||||||
|
|
||||||
# Optional options parameters for both the serializer and instance
|
|
||||||
options = {serializer: PostDetailedSerializer, username: 'sample user'}
|
|
||||||
|
|
||||||
# Create a serializable resource instance
|
|
||||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(post, options)
|
|
||||||
|
|
||||||
# Convert your resource into json
|
|
||||||
model_json = serializable_resource.as_json
|
|
||||||
```
|
|
||||||
The object that is passed to `ActiveModelSerializers::SerializableResource.new` can be a single resource or a collection.
|
|
||||||
The additional options are the same options that are passed [through controllers](../general/rendering.md#explicit-serializer).
|
|
||||||
|
|
||||||
### Looking up the Serializer for a Resource
|
|
||||||
|
|
||||||
If you want to retrieve the serializer class for a specific resource, you can do the following:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Create our resource
|
|
||||||
post = Post.create(title: "Another Example", body: "So much fun.")
|
|
||||||
|
|
||||||
# Optional options parameters
|
|
||||||
options = {}
|
|
||||||
|
|
||||||
# Retrieve the default serializer for posts
|
|
||||||
serializer = ActiveModel::Serializer.serializer_for(post, options)
|
|
||||||
```
|
|
||||||
|
|
||||||
You could also retrieve the serializer via:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::SerializableResource.new(post, options).serializer
|
|
||||||
```
|
|
||||||
|
|
||||||
Both approaches will return the serializer class that will be used for the resource.
|
|
||||||
|
|
||||||
Additionally, you could retrieve the serializer instance for the resource via:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::SerializableResource.new(post, options).serializer_instance
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serializing before controller render
|
|
||||||
|
|
||||||
At times, you might want to use a serializer without rendering it to the view. For those cases, you can create an instance of `ActiveModelSerializers::SerializableResource` with
|
|
||||||
the resource you want to be serialized and call `.as_json`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def create
|
|
||||||
message = current_user.messages.create!(message_params)
|
|
||||||
message_json = ActiveModelSerializers::SerializableResource.new(message).as_json
|
|
||||||
MessageCreationWorker.perform(message_json)
|
|
||||||
head 204
|
|
||||||
end
|
|
||||||
```
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Passing Arbitrary Options To A Serializer
|
|
||||||
|
|
||||||
In addition to the [`serialization_scope`](../general/serializers.md#scope), any options passed to `render`
|
|
||||||
that are not reserved for the [adapter](../general/rendering.md#adapter_opts)
|
|
||||||
are available in the serializer as [instance_options](../general/serializers.md#instance_options).
|
|
||||||
|
|
||||||
For example, we could pass in a field, such as `user_id` into our serializer.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# posts_controller.rb
|
|
||||||
class PostsController < ApplicationController
|
|
||||||
def dashboard
|
|
||||||
render json: @post, user_id: 12
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# post_serializer.rb
|
|
||||||
class PostSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :title, :body
|
|
||||||
|
|
||||||
def comments_by_me
|
|
||||||
Comments.where(user_id: instance_options[:user_id], post_id: object.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to serialize a Plain-Old Ruby Object (PORO)
|
|
||||||
|
|
||||||
When you are first getting started with ActiveModelSerializers, it may seem only `ActiveRecord::Base` objects can be serializable,
|
|
||||||
but pretty much any object can be serializable with ActiveModelSerializers.
|
|
||||||
Here is an example of a PORO that is serializable in most situations:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# my_model.rb
|
|
||||||
class MyModel
|
|
||||||
alias :read_attribute_for_serialization :send
|
|
||||||
attr_accessor :id, :name, :level
|
|
||||||
|
|
||||||
def initialize(attributes)
|
|
||||||
@id = attributes[:id]
|
|
||||||
@name = attributes[:name]
|
|
||||||
@level = attributes[:level]
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.model_name
|
|
||||||
@_model_name ||= ActiveModel::Name.new(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The [ActiveModel::Serializer::Lint::Tests](../../lib/active_model/serializer/lint.rb)
|
|
||||||
define and validate which methods ActiveModelSerializers expects to be implemented.
|
|
||||||
|
|
||||||
An implementation of the complete spec is included either for use or as reference:
|
|
||||||
[`ActiveModelSerializers::Model`](../../lib/active_model_serializers/model.rb).
|
|
||||||
You can use in production code that will make your PORO a lot cleaner.
|
|
||||||
|
|
||||||
The above code now becomes:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# my_model.rb
|
|
||||||
class MyModel < ActiveModelSerializers::Model
|
|
||||||
attributes :id, :name, :level
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The default serializer would be `MyModelSerializer`.
|
|
||||||
|
|
||||||
*IMPORTANT*: There is a surprising behavior (bug) in the current implementation of ActiveModelSerializers::Model that
|
|
||||||
prevents an accessor from modifying attributes on the instance. The fix for this bug
|
|
||||||
is a breaking change, so we have made an opt-in configuration.
|
|
||||||
|
|
||||||
New applications should set:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers::Model.derive_attributes_from_names_and_fix_accessors
|
|
||||||
```
|
|
||||||
|
|
||||||
Existing applications can use the fix *and* avoid breaking changes
|
|
||||||
by making a superclass for new models. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class SerializablePoro < ActiveModelSerializers::Model
|
|
||||||
derive_attributes_from_names_and_fix_accessors
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
So that `MyModel` above would inherit from `SerializablePoro`.
|
|
||||||
|
|
||||||
`derive_attributes_from_names_and_fix_accessors` prepends the `DeriveAttributesFromNamesAndFixAccessors`
|
|
||||||
module and does the following:
|
|
||||||
|
|
||||||
- `id` will *always* be in the attributes. (This is until we separate out the caching requirement for POROs.)
|
|
||||||
- Overwrites the `attributes` method to that it only returns declared attributes.
|
|
||||||
`attributes` will now be a frozen hash with indifferent access.
|
|
||||||
|
|
||||||
For more information, see [README: What does a 'serializable resource' look like?](../../README.md#what-does-a-serializable-resource-look-like).
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to test
|
|
||||||
|
|
||||||
## Controller Serializer Usage
|
|
||||||
|
|
||||||
ActiveModelSerializers provides a `assert_serializer` method to be used on your controller tests to
|
|
||||||
assert that a specific serializer was used.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsControllerTest < ActionController::TestCase
|
|
||||||
test "should render post serializer" do
|
|
||||||
get :index
|
|
||||||
assert_serializer "PostSerializer"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
See [ActiveModelSerializers::Test::Serializer](../../lib/active_model_serializers/test/serializer.rb)
|
|
||||||
for more examples and documentation.
|
|
||||||
|
|
||||||
## Serialization against a schema
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
To use the `assert_response_schema` you need to have the
|
|
||||||
[`json_schema`](https://github.com/brandur/json_schema) on your Gemfile. Please
|
|
||||||
add it to your Gemfile and run `$ bundle install`.
|
|
||||||
|
|
||||||
### Minitest test helpers
|
|
||||||
|
|
||||||
ActiveModelSerializers provides a `assert_response_schema` method to be used on your controller tests to
|
|
||||||
assert the response against a [JSON Schema](http://json-schema.org/). Let's take
|
|
||||||
a look in an example.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsController < ApplicationController
|
|
||||||
def show
|
|
||||||
@post = Post.find(params[:id])
|
|
||||||
|
|
||||||
render json: @post
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
To test the `posts#show` response of this controller we need to create a file
|
|
||||||
named `test/support/schemas/posts/show.json`. The helper uses a naming convention
|
|
||||||
to locate the file.
|
|
||||||
|
|
||||||
This file is a JSON Schema representation of our response.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"properties": {
|
|
||||||
"title" : { "type" : "string" },
|
|
||||||
"content" : { "type" : "string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With all in place we can go to our test and use the helper.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class PostsControllerTest < ActionController::TestCase
|
|
||||||
test "should render right response" do
|
|
||||||
get :index
|
|
||||||
assert_response_schema
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Load a custom schema
|
|
||||||
|
|
||||||
If we need to use another schema, for example when we have a namespaced API that
|
|
||||||
shows the same response, we can pass the path of the schema.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
module V1
|
|
||||||
class PostsController < ApplicationController
|
|
||||||
def show
|
|
||||||
@post = Post.find(params[:id])
|
|
||||||
|
|
||||||
render json: @post
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class V1::PostsControllerTest < ActionController::TestCase
|
|
||||||
test "should render right response" do
|
|
||||||
get :index
|
|
||||||
assert_response_schema('posts/show.json')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Change the schema path
|
|
||||||
|
|
||||||
By default all schemas are created at `test/support/schemas`. If we are using
|
|
||||||
RSpec for example we can change this to `spec/support/schemas` defining the
|
|
||||||
default schema path in an initializer.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.schema_path = 'spec/support/schemas'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using with the Heroku’s JSON Schema-based tools
|
|
||||||
|
|
||||||
To use the test helper with the [prmd](https://github.com/interagent/prmd) and
|
|
||||||
[committee](https://github.com/interagent/committee).
|
|
||||||
|
|
||||||
We need to change the schema path to the recommended by prmd:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.schema_path = 'docs/schema/schemata'
|
|
||||||
```
|
|
||||||
|
|
||||||
We also need to structure our schemata according to Heroku's conventions
|
|
||||||
(e.g. including
|
|
||||||
[required metadata](https://github.com/interagent/prmd/blob/master/docs/schemata.md#meta-data)
|
|
||||||
and [links](https://github.com/interagent/prmd/blob/master/docs/schemata.md#links).
|
|
||||||
|
|
||||||
#### JSON Pointers
|
|
||||||
|
|
||||||
If we plan to use [JSON
|
|
||||||
Pointers](http://spacetelescope.github.io/understanding-json-schema/UnderstandingJSONSchema.pdf) we need to define the `id` attribute on the schema. Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// attributes.json
|
|
||||||
|
|
||||||
{
|
|
||||||
"id": "file://attributes.json#",
|
|
||||||
"properties": {
|
|
||||||
"name" : { "type" : "string" },
|
|
||||||
"description" : { "type" : "string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
// show.json
|
|
||||||
|
|
||||||
{
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"$ref": "file://attributes.json#/properties/name"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"$ref": "file://attributes.json#/properties/description"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@ -1,265 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# How to migrate from `0.8` to `0.10` safely
|
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
### Proceed at your own risk
|
|
||||||
This document attempts to outline steps to upgrade your app based on the collective experience of
|
|
||||||
developers who have done this already. It may not cover all edge cases and situations that may cause issues,
|
|
||||||
so please proceed with a certain level of caution.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
This document outlines the steps needed to migrate from `0.8` to `0.10`. The method described
|
|
||||||
below has been created via the collective knowledge of contributions of those who have done
|
|
||||||
the migration successfully. The method has been tested specifically for migrating from `0.8.3`
|
|
||||||
to `0.10.2`.
|
|
||||||
|
|
||||||
The high level approach is to upgrade to `0.10` and change all serializers to use
|
|
||||||
a backwards-compatible `ActiveModel::V08::Serializer`or `ActiveModel::V08::CollectionSerializer`
|
|
||||||
and a `ActiveModelSerializers::Adapter::V08Adapter`. After a few more manual changes, you should have the same
|
|
||||||
functionality as you had with `AMS 0.8`. Then, you can continue to develop in your app by creating
|
|
||||||
new serializers that don't use these backwards compatible versions and slowly migrate
|
|
||||||
existing serializers to the `0.10` versions as needed.
|
|
||||||
|
|
||||||
### `0.10` breaking changes
|
|
||||||
- Passing a serializer to `render json:` is no longer supported
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
render json: CustomerSerializer.new(customer) # rendered in 0.8, errors in 0.10
|
|
||||||
```
|
|
||||||
|
|
||||||
- Passing a nil resource to serializer now fails
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
CustomerSerializer.new(nil) # returned nil in 0.8, throws error in 0.10
|
|
||||||
```
|
|
||||||
|
|
||||||
- Attribute methods are no longer defined on the serializer, and must be explicitly
|
|
||||||
accessed through `object`
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class MySerializer
|
|
||||||
attributes :foo, :bar
|
|
||||||
|
|
||||||
def foo
|
|
||||||
bar + 1 # bar does not work, needs to be object.bar in 0.10
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
- `root` option to collection serializer behaves differently
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# in 0.8
|
|
||||||
ActiveModel::ArraySerializer.new(resources, root: "resources")
|
|
||||||
# resulted in { "resources": <serialized_resources> }, does not work in 0.10
|
|
||||||
```
|
|
||||||
|
|
||||||
- No default serializer when serializer doesn't exist
|
|
||||||
- `@options` changed to `instance_options`
|
|
||||||
- Nested relationships are no longer walked by default. Use the `:include` option at **controller `render`** level to specify what relationships to walk. E.g. `render json: @post, include: {comments: :author}` if you want the `author` relationship walked, otherwise the json would only include the post with comments. See: https://github.com/rails-api/active_model_serializers/pull/1127
|
|
||||||
- To emulate `0.8`'s walking of arbitrarily deep relationships use: `include: '**'`. E.g. `render json: @post, include: '**'`
|
|
||||||
|
|
||||||
## Steps to migrate
|
|
||||||
|
|
||||||
### 1. Upgrade the `active_model_serializer` gem in you `Gemfile`
|
|
||||||
Change to `gem 'active_model_serializers', '~> 0.10'` and run `bundle install`
|
|
||||||
|
|
||||||
### 2. Add `ActiveModel::V08::Serializer`
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
module ActiveModel
|
|
||||||
module V08
|
|
||||||
class Serializer < ActiveModel::Serializer
|
|
||||||
include Rails.application.routes.url_helpers
|
|
||||||
|
|
||||||
# AMS 0.8 would delegate method calls from within the serializer to the
|
|
||||||
# object.
|
|
||||||
def method_missing(*args)
|
|
||||||
method = args.first
|
|
||||||
read_attribute_for_serialization(method)
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :options, :instance_options
|
|
||||||
|
|
||||||
# Since attributes could be read from the `object` via `method_missing`,
|
|
||||||
# the `try` method did not behave as before. This patches `try` with the
|
|
||||||
# original implementation plus the addition of
|
|
||||||
# ` || object.respond_to?(a.first, true)` to check if the object responded to
|
|
||||||
# the given method.
|
|
||||||
def try(*a, &b)
|
|
||||||
if a.empty? || respond_to?(a.first, true) || object.respond_to?(a.first, true)
|
|
||||||
try!(*a, &b)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# AMS 0.8 would return nil if the serializer was initialized with a nil
|
|
||||||
# resource.
|
|
||||||
def serializable_hash(adapter_options = nil,
|
|
||||||
options = {},
|
|
||||||
adapter_instance =
|
|
||||||
self.class.serialization_adapter_instance)
|
|
||||||
object.nil? ? nil : super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
```
|
|
||||||
Add this class to your app however you see fit. This is the class that your existing serializers
|
|
||||||
that inherit from `ActiveModel::Serializer` should inherit from.
|
|
||||||
|
|
||||||
### 3. Add `ActiveModel::V08::CollectionSerializer`
|
|
||||||
```ruby
|
|
||||||
module ActiveModel
|
|
||||||
module V08
|
|
||||||
class CollectionSerializer < ActiveModel::Serializer::CollectionSerializer
|
|
||||||
# In AMS 0.8, passing an ArraySerializer instance with a `root` option
|
|
||||||
# properly nested the serialized resources within the given root.
|
|
||||||
# Ex.
|
|
||||||
#
|
|
||||||
# class MyController < ActionController::Base
|
|
||||||
# def index
|
|
||||||
# render json: ActiveModel::Serializer::ArraySerializer
|
|
||||||
# .new(resources, root: "resources")
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Produced
|
|
||||||
#
|
|
||||||
# {
|
|
||||||
# "resources": [
|
|
||||||
# <serialized_resource>,
|
|
||||||
# ...
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
def as_json(options = {})
|
|
||||||
if root
|
|
||||||
{
|
|
||||||
root => super
|
|
||||||
}
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# AMS 0.8 used `DefaultSerializer` if it couldn't find a serializer for
|
|
||||||
# the given resource. When not using an adapter, this is not true in
|
|
||||||
# `0.10`
|
|
||||||
def serializer_from_resource(resource, serializer_context_class, options)
|
|
||||||
serializer_class =
|
|
||||||
options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
|
|
||||||
|
|
||||||
if serializer_class.nil? # rubocop:disable Style/GuardClause
|
|
||||||
DefaultSerializer.new(resource, options)
|
|
||||||
else
|
|
||||||
serializer_class.new(resource, options.except(:serializer))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DefaultSerializer
|
|
||||||
attr_reader :object, :options
|
|
||||||
|
|
||||||
def initialize(object, options={})
|
|
||||||
@object, @options = object, options
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializable_hash
|
|
||||||
@object.as_json(@options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
Add this class to your app however you see fit. This is the class that existing uses of
|
|
||||||
`ActiveModel::ArraySerializer` should be changed to use.
|
|
||||||
|
|
||||||
### 4. Add `ActiveModelSerializers::Adapter::V08Adapter`
|
|
||||||
```ruby
|
|
||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class V08Adapter < ActiveModelSerializers::Adapter::Base
|
|
||||||
def serializable_hash(options = nil)
|
|
||||||
options ||= {}
|
|
||||||
|
|
||||||
if serializer.respond_to?(:each)
|
|
||||||
if serializer.root
|
|
||||||
delegate_to_json_adapter(options)
|
|
||||||
else
|
|
||||||
serializable_hash_for_collection(options)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
serializable_hash_for_single_resource(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializable_hash_for_collection(options)
|
|
||||||
serializer.map do |s|
|
|
||||||
V08Adapter.new(s, instance_options)
|
|
||||||
.serializable_hash(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializable_hash_for_single_resource(options)
|
|
||||||
if serializer.object.is_a?(ActiveModel::Serializer)
|
|
||||||
# It is recommended that you add some logging here to indicate
|
|
||||||
# places that should get converted to eventually allow for this
|
|
||||||
# adapter to get removed.
|
|
||||||
@serializer = serializer.object
|
|
||||||
end
|
|
||||||
|
|
||||||
if serializer.root
|
|
||||||
delegate_to_json_adapter(options)
|
|
||||||
else
|
|
||||||
options = serialization_options(options)
|
|
||||||
serializer.serializable_hash(instance_options, options, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def delegate_to_json_adapter(options)
|
|
||||||
ActiveModelSerializers::Adapter::Json
|
|
||||||
.new(serializer, instance_options)
|
|
||||||
.serializable_hash(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
Add this class to your app however you see fit.
|
|
||||||
|
|
||||||
Add
|
|
||||||
```ruby
|
|
||||||
ActiveModelSerializers.config.adapter =
|
|
||||||
ActiveModelSerializers::Adapter::V08Adapter
|
|
||||||
```
|
|
||||||
to `config/active_model_serializer.rb` to configure AMS to use this
|
|
||||||
class as the default adapter.
|
|
||||||
|
|
||||||
### 5. Change inheritors of `ActiveModel::Serializer` to inherit from `ActiveModel::V08::Serializer`
|
|
||||||
Simple find/replace
|
|
||||||
|
|
||||||
### 6. Remove `private` keyword from serializers
|
|
||||||
Simple find/replace. This is required to allow the `ActiveModel::V08::Serializer`
|
|
||||||
to have proper access to the methods defined in the serializer.
|
|
||||||
|
|
||||||
You may be able to change the `private` to `protected`, but this is hasn't been tested yet.
|
|
||||||
|
|
||||||
### 7. Remove references to `ActiveRecord::Base#active_model_serializer`
|
|
||||||
This method is no longer supported in `0.10`.
|
|
||||||
|
|
||||||
`0.10` does a good job of discovering serializers for `ActiveRecord` objects.
|
|
||||||
|
|
||||||
### 8. Rename `ActiveModel::ArraySerializer` to `ActiveModel::V08::CollectionSerializer`
|
|
||||||
Find/replace uses of `ActiveModel::ArraySerializer` with `ActiveModel::V08::CollectionSerializer`.
|
|
||||||
|
|
||||||
Also, be sure to change the `each_serializer` keyword to `serializer` when calling making the replacement.
|
|
||||||
|
|
||||||
### 9. Replace uses of `@options` to `instance_options` in serializers
|
|
||||||
Simple find/replace
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
After you've done the steps above, you should test your app to ensure that everything is still working properly.
|
|
||||||
|
|
||||||
If you run into issues, please contribute back to this document so others can benefit from your knowledge.
|
|
||||||
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# Integrating with Ember and JSON API
|
|
||||||
|
|
||||||
- [Preparation](./ember-and-json-api.md#preparation)
|
|
||||||
- [Server-Side Changes](./ember-and-json-api.md#server-side-changes)
|
|
||||||
- [Adapter Changes](./ember-and-json-api.md#adapter-changes)
|
|
||||||
- [Serializer Changes](./ember-and-json-api.md#serializer-changes)
|
|
||||||
- [Including Nested Resources](./ember-and-json-api.md#including-nested-resources)
|
|
||||||
|
|
||||||
## Preparation
|
|
||||||
|
|
||||||
Note: This guide assumes that `ember-cli` is used for your ember app.
|
|
||||||
|
|
||||||
The JSON API specification calls for hyphens for multi-word separators. ActiveModelSerializers uses underscores.
|
|
||||||
To solve this, in Ember, both the adapter and the serializer will need some modifications:
|
|
||||||
|
|
||||||
### Server-Side Changes
|
|
||||||
|
|
||||||
First, set the adapter type in an initializer file:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# config/initializers/active_model_serializers.rb
|
|
||||||
ActiveModelSerializers.config.adapter = :json_api
|
|
||||||
```
|
|
||||||
|
|
||||||
or:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# config/initializers/active_model_serializers.rb
|
|
||||||
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
|
|
||||||
```
|
|
||||||
|
|
||||||
You will also want to set the `key_transform` to `:unaltered` since you will adjust the attributes in your Ember serializer to use underscores instead of dashes later. You could also use `:underscore`, but `:unaltered` is better for performance.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# config/initializers/active_model_serializers.rb
|
|
||||||
ActiveModelSerializers.config.key_transform = :unaltered
|
|
||||||
```
|
|
||||||
|
|
||||||
In order to properly handle JSON API responses, we need to register a JSON API renderer, like so:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# config/initializers/active_model_serializers.rb
|
|
||||||
ActiveSupport.on_load(:action_controller) do
|
|
||||||
require 'active_model_serializers/register_jsonapi_renderer'
|
|
||||||
end
|
|
||||||
```
|
|
||||||
Rails also requires your controller to tell it that you accept and generate JSONAPI data. To do that, you use `respond_to` in your controller handlers to tell rails you are consuming and returning jsonapi format data. Without this, Rails will refuse to parse the request body into params. You can add `ActionController::MimeResponds` to your application controller to enable this:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class ApplicationController < ActionController::API
|
|
||||||
include ActionController::MimeResponds
|
|
||||||
end
|
|
||||||
```
|
|
||||||
Then, in your controller you can tell rails you're accepting and rendering the jsonapi format:
|
|
||||||
```ruby
|
|
||||||
# POST /post
|
|
||||||
def create
|
|
||||||
@post = Post.new(post_params)
|
|
||||||
respond_to do |format|
|
|
||||||
if @post.save
|
|
||||||
format.jsonapi { render jsonapi: @post, status: :created, location: @post }
|
|
||||||
else
|
|
||||||
format.jsonapi { render jsonapi: @post.errors, status: :unprocessable_entity }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Only allow a trusted parameter "white list" through.
|
|
||||||
def post_params
|
|
||||||
ActiveModelSerializers::Deserialization.jsonapi_parse!(params, only: [:title, :body] )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Note:
|
|
||||||
In Rails 5, the "unsafe" method ( `jsonapi_parse!` vs the safe `jsonapi_parse`) throws an `InvalidDocument` exception when the payload does not meet basic criteria for JSON API deserialization.
|
|
||||||
|
|
||||||
|
|
||||||
### Adapter Changes
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// app/adapters/application.js
|
|
||||||
import Ember from 'ember';
|
|
||||||
import DS from 'ember-data';
|
|
||||||
import ENV from "../config/environment";
|
|
||||||
const { underscore, pluralize } = Ember.String;
|
|
||||||
|
|
||||||
export default DS.JSONAPIAdapter.extend({
|
|
||||||
namespace: 'api',
|
|
||||||
// if your rails app is on a different port from your ember app
|
|
||||||
// this can be helpful for development.
|
|
||||||
// in production, the host for both rails and ember should be the same.
|
|
||||||
host: ENV.host,
|
|
||||||
|
|
||||||
// allows the multiword paths in urls to be underscored
|
|
||||||
pathForType: function(type) {
|
|
||||||
let underscored = underscore(type);
|
|
||||||
return pluralize(underscored);
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Serializer Changes
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// app/serializers/application.js
|
|
||||||
import Ember from 'ember';
|
|
||||||
import DS from 'ember-data';
|
|
||||||
var underscore = Ember.String.underscore;
|
|
||||||
|
|
||||||
export default DS.JSONAPISerializer.extend({
|
|
||||||
keyForAttribute: function(attr) {
|
|
||||||
return underscore(attr);
|
|
||||||
},
|
|
||||||
|
|
||||||
keyForRelationship: function(rawKey) {
|
|
||||||
return underscore(rawKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Including Nested Resources
|
|
||||||
|
|
||||||
Ember Data can request related records by using `include`. Below are some examples of how to make Ember Data request the inclusion of related records. For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
store.findRecord('post', postId, { include: 'comments' } );
|
|
||||||
```
|
|
||||||
which will generate the path /posts/{postId}?include='comments'
|
|
||||||
|
|
||||||
So then in your controller, you'll want to be sure to have something like:
|
|
||||||
```ruby
|
|
||||||
render jsonapi: @post, include: params[:include]
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to use `include` on a collection, you'd write something like this:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
store.query('post', { include: 'comments' });
|
|
||||||
```
|
|
||||||
|
|
||||||
which will generate the path `/posts?include='comments'`
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Integration with Grape
|
|
||||||
|
|
||||||
[Grape](https://github.com/ruby-grape/grape) is an opinionated micro-framework for creating REST-like APIs in ruby.
|
|
||||||
|
|
||||||
ActiveModelSerializers currently supports Grape >= 0.13, < 1.0
|
|
||||||
|
|
||||||
To add [Grape](https://github.com/ruby-grape/grape) support, enable the formatter and helper functions by including `Grape::ActiveModelSerializers` in your base endpoint. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
module Example
|
|
||||||
class Dummy < Grape::API
|
|
||||||
require 'grape/active_model_serializers'
|
|
||||||
include Grape::ActiveModelSerializers
|
|
||||||
mount Example::V1::Base
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Aside from this, [configuration](../general/configuration_options.md) of ActiveModelSerializers is exactly the same.
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
# [JSON API Errors](http://jsonapi.org/format/#errors)
|
|
||||||
|
|
||||||
Rendering error documents requires specifying the error serializer(s):
|
|
||||||
|
|
||||||
- Serializer:
|
|
||||||
- For a single resource: `serializer: ActiveModel::Serializer::ErrorSerializer`.
|
|
||||||
- For a collection: `serializer: ActiveModel::Serializer::ErrorsSerializer`, `each_serializer: ActiveModel::Serializer::ErrorSerializer`.
|
|
||||||
|
|
||||||
The resource **MUST** have a non-empty associated `#errors` object.
|
|
||||||
The `errors` object must have a `#messages` method that returns a hash of error name to array of
|
|
||||||
descriptions.
|
|
||||||
|
|
||||||
## Use in controllers
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
resource = Profile.new(name: 'Name 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
comments: 'Comments 1')
|
|
||||||
resource.errors.add(:name, 'cannot be nil')
|
|
||||||
resource.errors.add(:name, 'must be longer')
|
|
||||||
resource.errors.add(:id, 'must be a uuid')
|
|
||||||
|
|
||||||
render json: resource, status: 422, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer
|
|
||||||
# #=>
|
|
||||||
# { :errors =>
|
|
||||||
# [
|
|
||||||
# { :source => { :pointer => '/data/attributes/name' }, :detail => 'cannot be nil' },
|
|
||||||
# { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be longer' },
|
|
||||||
# { :source => { :pointer => '/data/attributes/id' }, :detail => 'must be a uuid' }
|
|
||||||
# ]
|
|
||||||
# }.to_json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Direct error document generation
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
options = nil
|
|
||||||
resource = ModelWithErrors.new
|
|
||||||
resource.errors.add(:name, 'must be awesome')
|
|
||||||
|
|
||||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(
|
|
||||||
resource, {
|
|
||||||
serializer: ActiveModel::Serializer::ErrorSerializer,
|
|
||||||
adapter: :json_api
|
|
||||||
})
|
|
||||||
serializable_resource.as_json(options)
|
|
||||||
# #=>
|
|
||||||
# {
|
|
||||||
# :errors =>
|
|
||||||
# [
|
|
||||||
# { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be awesome' }
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
```
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
[Back to Guides](../README.md)
|
|
||||||
|
|
||||||
[](http://jsonapi.org/)
|
|
||||||
|
|
||||||
## JSON API Requests
|
|
||||||
|
|
||||||
- [Query Parameters Spec](http://jsonapi.org/format/#query-parameters)
|
|
||||||
|
|
||||||
Headers:
|
|
||||||
|
|
||||||
- Request: `Accept: application/vnd.api+json`
|
|
||||||
- Response: `Content-Type: application/vnd.api+json`
|
|
||||||
|
|
||||||
### [Fetching Data](http://jsonapi.org/format/#fetching)
|
|
||||||
|
|
||||||
A server MUST support fetching resource data for every URL provided as:
|
|
||||||
|
|
||||||
- a `self` link as part of the top-level links object
|
|
||||||
- a `self` link as part of a resource-level links object
|
|
||||||
- a `related` link as part of a relationship-level links object
|
|
||||||
|
|
||||||
Example supported requests
|
|
||||||
|
|
||||||
- Individual resource or collection
|
|
||||||
- GET /articles
|
|
||||||
- GET /articles/1
|
|
||||||
- GET /articles/1/author
|
|
||||||
- Relationships
|
|
||||||
- GET /articles/1/relationships/comments
|
|
||||||
- GET /articles/1/relationships/author
|
|
||||||
- Optional: [Inclusion of related resources](http://jsonapi.org/format/#fetching-includes) `JSONAPI::IncludeDirective`
|
|
||||||
- GET /articles/1?`include`=comments
|
|
||||||
- GET /articles/1?`include`=comments.author
|
|
||||||
- GET /articles/1?`include`=author,comments.author
|
|
||||||
- GET /articles/1/relationships/comments?`include`=comments.author
|
|
||||||
- Optional: [Sparse Fieldsets](http://jsonapi.org/format/#fetching-sparse-fieldsets) `ActiveModel::Serializer::Fieldset`
|
|
||||||
- GET /articles?`include`=author&`fields`[articles]=title,body&`fields`[people]=name
|
|
||||||
- Optional: [Sorting](http://jsonapi.org/format/#fetching-sorting)
|
|
||||||
- GET /people?`sort`=age
|
|
||||||
- GET /people?`sort`=age,author.name
|
|
||||||
- GET /articles?`sort`=-created,title
|
|
||||||
- Optional: [Pagination](http://jsonapi.org/format/#fetching-pagination)
|
|
||||||
- GET /articles?`page`[number]=3&`page`[size]=1
|
|
||||||
- Optional: [Filtering](http://jsonapi.org/format/#fetching-filtering)
|
|
||||||
- GET /comments?`filter`[post]=1
|
|
||||||
- GET /comments?`filter`[post]=1,2
|
|
||||||
- GET /comments?`filter`[post]=1,2
|
|
||||||
|
|
||||||
### [CRUD Actions](http://jsonapi.org/format/#crud)
|
|
||||||
|
|
||||||
### [Asynchronous Processing](http://jsonapi.org/recommendations/#asynchronous-processing)
|
|
||||||
|
|
||||||
### [Bulk Operations Extension](http://jsonapi.org/extensions/bulk/)
|
|
||||||
|
|
||||||
## JSON API Document Schema
|
|
||||||
|
|
||||||
| JSON API object | JSON API properties | Required | ActiveModelSerializers representation |
|
|
||||||
|-----------------------|----------------------------------------------------------------------------------------------------|----------|---------------------------------------|
|
|
||||||
| schema | oneOf (success, failure, info) | |
|
|
||||||
| success | data, included, meta, links, jsonapi | | AM::SerializableResource
|
|
||||||
| success.meta | meta | | AMS::Adapter::Base#meta
|
|
||||||
| success.included | UniqueArray(resource) | | AMS::Adapter::JsonApi#serializable_hash_for_collection
|
|
||||||
| success.data | data | |
|
|
||||||
| success.links | allOf (links, pagination) | | AMS::Adapter::JsonApi#links_for
|
|
||||||
| success.jsonapi | jsonapi | |
|
|
||||||
| failure | errors, meta, jsonapi | errors | AMS::Adapter::JsonApi#failure_document, #1004
|
|
||||||
| failure.errors | UniqueArray(error) | | AM::S::ErrorSerializer, #1004
|
|
||||||
| meta | Object | |
|
|
||||||
| data | oneOf (resource, UniqueArray(resource)) | | AMS::Adapter::JsonApi#serializable_hash_for_collection,#serializable_hash_for_single_resource
|
|
||||||
| resource | String(type), String(id),<br>attributes, relationships,<br>links, meta | type, id | AM::S::Adapter::JsonApi#primary_data_for
|
|
||||||
| links | Uri(self), Link(related) | | #1028, #1246, #1282
|
|
||||||
| link | oneOf (linkString, linkObject) | |
|
|
||||||
| link.linkString | Uri | |
|
|
||||||
| link.linkObject | Uri(href), meta | href |
|
|
||||||
| attributes | patternProperties(<br>`"^(?!relationships$|links$)\\w[-\\w_]*$"`),<br>any valid JSON | | AM::Serializer#attributes, AMS::Adapter::JsonApi#resource_object_for
|
|
||||||
| relationships | patternProperties(<br>`"^\\w[-\\w_]*$"`);<br>links, relationships.data, meta | | AMS::Adapter::JsonApi#relationships_for
|
|
||||||
| relationships.data | oneOf (relationshipToOne, relationshipToMany) | | AMS::Adapter::JsonApi#resource_identifier_for
|
|
||||||
| relationshipToOne | anyOf(empty, linkage) | |
|
|
||||||
| relationshipToMany | UniqueArray(linkage) | |
|
|
||||||
| empty | null | |
|
|
||||||
| linkage | String(type), String(id), meta | type, id | AMS::Adapter::JsonApi#primary_data_for
|
|
||||||
| pagination | pageObject(first), pageObject(last),<br>pageObject(prev), pageObject(next) | | AMS::Adapter::JsonApi::PaginationLinks#serializable_hash
|
|
||||||
| pagination.pageObject | oneOf(Uri, null) | |
|
|
||||||
| jsonapi | String(version), meta | | AMS::Adapter::JsonApi::Jsonapi#as_json
|
|
||||||
| error | String(id), links, String(status),<br>String(code), String(title),<br>String(detail), error.source, meta | | AM::S::ErrorSerializer, AMS::Adapter::JsonApi::Error.resource_errors
|
|
||||||
| error.source | String(pointer), String(parameter) | | AMS::Adapter::JsonApi::Error.error_source
|
|
||||||
| pointer | [JSON Pointer RFC6901](https://tools.ietf.org/html/rfc6901) | | AMS::JsonPointer
|
|
||||||
|
|
||||||
|
|
||||||
The [http://jsonapi.org/schema](schema/schema.json) makes a nice roadmap.
|
|
||||||
|
|
||||||
### Success Document
|
|
||||||
- [ ] success
|
|
||||||
- [ ] data: `"$ref": "#/definitions/data"`
|
|
||||||
- [ ] included: array of unique items of type `"$ref": "#/definitions/resource"`
|
|
||||||
- [ ] meta: `"$ref": "#/definitions/meta"`
|
|
||||||
- [ ] links:
|
|
||||||
- [ ] link: `"$ref": "#/definitions/links"`
|
|
||||||
- [ ] pagination: ` "$ref": "#/definitions/pagination"`
|
|
||||||
- [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"`
|
|
||||||
|
|
||||||
### Failure Document
|
|
||||||
|
|
||||||
- [ ] failure
|
|
||||||
- [x] errors: array of unique items of type ` "$ref": "#/definitions/error"`
|
|
||||||
- [ ] meta: `"$ref": "#/definitions/meta"`
|
|
||||||
- [ ] jsonapi: `"$ref": "#/definitions/jsonapi"`
|
|
||||||
|
|
||||||
### Info Document
|
|
||||||
|
|
||||||
- [ ] info
|
|
||||||
- [ ] meta: `"$ref": "#/definitions/meta"`
|
|
||||||
- [ ] links: `"$ref": "#/definitions/links"`
|
|
||||||
- [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"`
|
|
||||||
|
|
||||||
### Definitions
|
|
||||||
|
|
||||||
- [ ] definitions:
|
|
||||||
- [ ] meta
|
|
||||||
- [ ] data: oneOf (resource, array of unique resources)
|
|
||||||
- [ ] resource
|
|
||||||
- [ ] attributes
|
|
||||||
- [ ] relationships
|
|
||||||
- [ ] relationshipToOne
|
|
||||||
- [ ] empty
|
|
||||||
- [ ] linkage
|
|
||||||
- [ ] meta
|
|
||||||
- [ ] relationshipToMany
|
|
||||||
- [ ] linkage
|
|
||||||
- [ ] meta
|
|
||||||
- [ ] links
|
|
||||||
- [ ] meta
|
|
||||||
- [ ] links
|
|
||||||
- [ ] link
|
|
||||||
- [ ] uri
|
|
||||||
- [ ] href, meta
|
|
||||||
- [ ] pagination
|
|
||||||
- [ ] jsonapi
|
|
||||||
- [ ] meta
|
|
||||||
- [ ] error
|
|
||||||
- [ ] id: a unique identifier for this particular occurrence of the problem.
|
|
||||||
- [ ] links: a links object containing the following members:
|
|
||||||
- [ ] about: a link that leads to further details about this particular occurrence of the problem.
|
|
||||||
- [ ] status: the HTTP status code applicable to this problem, expressed as a string value.
|
|
||||||
- [ ] code: an application-specific error code, expressed as a string value.
|
|
||||||
- [ ] title: a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
|
|
||||||
- [x] detail: a human-readable explanation specific to this occurrence of the problem.
|
|
||||||
- [x] source: an object containing references to the source of the error, optionally including any of the following members:
|
|
||||||
- [x] pointer: a JSON Pointer [RFC6901](https://tools.ietf.org/html/rfc6901) to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].
|
|
||||||
- [x] parameter: a string indicating which query parameter caused the error.
|
|
||||||
- [ ] meta: a meta object containing non-standard meta-information about the error.
|
|
||||||
@ -1,366 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
||||||
"title": "JSON API Schema",
|
|
||||||
"description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/failure"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/info"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"definitions": {
|
|
||||||
"success": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"data"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"$ref": "#/definitions/data"
|
|
||||||
},
|
|
||||||
"included": {
|
|
||||||
"description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/resource"
|
|
||||||
},
|
|
||||||
"uniqueItems": true
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"description": "Link members related to the primary data.",
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/links"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/pagination"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jsonapi": {
|
|
||||||
"$ref": "#/definitions/jsonapi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"failure": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"errors"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"errors": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/error"
|
|
||||||
},
|
|
||||||
"uniqueItems": true
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
},
|
|
||||||
"jsonapi": {
|
|
||||||
"$ref": "#/definitions/jsonapi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"info": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"meta"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"$ref": "#/definitions/links"
|
|
||||||
},
|
|
||||||
"jsonapi": {
|
|
||||||
"$ref": "#/definitions/jsonapi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
|
|
||||||
"meta": {
|
|
||||||
"description": "Non-standard meta-information that can not be represented as an attribute or relationship.",
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/resource"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/resource"
|
|
||||||
},
|
|
||||||
"uniqueItems": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"resource": {
|
|
||||||
"description": "\"Resource objects\" appear in a JSON API document to represent resources.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"$ref": "#/definitions/attributes"
|
|
||||||
},
|
|
||||||
"relationships": {
|
|
||||||
"$ref": "#/definitions/relationships"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"$ref": "#/definitions/links"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
|
|
||||||
"links": {
|
|
||||||
"description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"self": {
|
|
||||||
"description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
"related": {
|
|
||||||
"$ref": "#/definitions/link"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"link": {
|
|
||||||
"description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"description": "A string containing the link's URL.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"href"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"href": {
|
|
||||||
"description": "A string containing the link's URL.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"attributes": {
|
|
||||||
"description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.",
|
|
||||||
"type": "object",
|
|
||||||
"patternProperties": {
|
|
||||||
"^(?!relationships$|links$)\\w[-\\w_]*$": {
|
|
||||||
"description": "Attributes may contain any valid JSON value."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
|
|
||||||
"relationships": {
|
|
||||||
"description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.",
|
|
||||||
"type": "object",
|
|
||||||
"patternProperties": {
|
|
||||||
"^\\w[-\\w_]*$": {
|
|
||||||
"properties": {
|
|
||||||
"links": {
|
|
||||||
"$ref": "#/definitions/links"
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"description": "Member, whose value represents \"resource linkage\".",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/relationshipToOne"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/relationshipToMany"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"relationshipToOne": {
|
|
||||||
"description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.",
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/empty"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/linkage"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"relationshipToMany": {
|
|
||||||
"description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/linkage"
|
|
||||||
},
|
|
||||||
"uniqueItems": true
|
|
||||||
},
|
|
||||||
"empty": {
|
|
||||||
"description": "Describes an empty to-one relationship.",
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
"linkage": {
|
|
||||||
"description": "The \"type\" and \"id\" to non-empty members.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"pagination": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"first": {
|
|
||||||
"description": "The first page of data",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "string", "format": "uri" },
|
|
||||||
{ "type": "null" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"last": {
|
|
||||||
"description": "The last page of data",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "string", "format": "uri" },
|
|
||||||
{ "type": "null" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"prev": {
|
|
||||||
"description": "The previous page of data",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "string", "format": "uri" },
|
|
||||||
{ "type": "null" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"description": "The next page of data",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "string", "format": "uri" },
|
|
||||||
{ "type": "null" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"jsonapi": {
|
|
||||||
"description": "An object describing the server's implementation",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"version": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
|
|
||||||
"error": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"$ref": "#/definitions/links"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"description": "The HTTP status code applicable to this problem, expressed as a string value.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"code": {
|
|
||||||
"description": "An application-specific error code, expressed as a string value.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"detail": {
|
|
||||||
"description": "A human-readable explanation specific to this occurrence of the problem.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"source": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"pointer": {
|
|
||||||
"description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"parameter": {
|
|
||||||
"description": "A string indicating which query parameter caused the error.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"$ref": "#/definitions/meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
- Start Date: (2015-10-29)
|
|
||||||
- RFC PR: https://github.com/rails-api/active_model_serializers/pull/1310
|
|
||||||
- ActiveModelSerializers Issue: https://github.com/rails-api/active_model_serializers/issues/1298
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
|
|
||||||
Provide a consistent API for the user of the AMS.
|
|
||||||
|
|
||||||
# Motivation
|
|
||||||
|
|
||||||
The actual public API is defined under `ActiveModelSerializers`,
|
|
||||||
`ActiveModel::Serializer` and `ActiveModel`.
|
|
||||||
|
|
||||||
At the `ActiveModel::Serializer` we have:
|
|
||||||
|
|
||||||
- `ActiveModel::Serializer.config`
|
|
||||||
- `ActiveModel::Serializer`
|
|
||||||
|
|
||||||
At the `ActiveModelSerializers` we have:
|
|
||||||
|
|
||||||
- `ActiveModelSerializers::Model`
|
|
||||||
- `ActiveModelSerializers.logger`
|
|
||||||
|
|
||||||
At `ActiveModel` we have:
|
|
||||||
|
|
||||||
- `ActiveModel::SerializableResource`
|
|
||||||
|
|
||||||
The idea here is to provide a single namespace `ActiveModelSerializers` to the user.
|
|
||||||
Following the same idea we have on other gems like
|
|
||||||
[Devise](https://github.com/plataformatec/devise/blob/e9c82472ffe7c43a448945f77e034a0e47dde0bb/lib/devise.rb),
|
|
||||||
[Refile](https://github.com/refile/refile/blob/6b24c293d044862dafbf1bfa4606672a64903aa2/lib/refile.rb) and
|
|
||||||
[Active Job](https://github.com/rails/rails/blob/30bacc26f8f258b39e12f63fe52389a968d9c1ea/activejob/lib/active_job.rb)
|
|
||||||
for example.
|
|
||||||
|
|
||||||
This way we are clarifing the boundaries of
|
|
||||||
[ActiveModelSerializers and Rails](https://github.com/rails-api/active_model_serializers/blob/master/CHANGELOG.md#prehistory)
|
|
||||||
and make clear that the `ActiveModel::Serializer` class is no longer the primary
|
|
||||||
behavior of the ActiveModelSerializers.
|
|
||||||
|
|
||||||
# Detailed design
|
|
||||||
|
|
||||||
## New classes and modules organization
|
|
||||||
|
|
||||||
Since this will be a big change we can do this on baby steps, read small pull requests. A
|
|
||||||
possible approach is:
|
|
||||||
|
|
||||||
- All new code will be in `lib/active_model_serializers/` using
|
|
||||||
the module namespace `ActiveModelSerializers`.
|
|
||||||
- Move all content under `ActiveModel::Serializer` to be under
|
|
||||||
`ActiveModelSerializers`, the adapter is on this steps;
|
|
||||||
- Move all content under `ActiveModel` to be under `ActiveModelSerializers`,
|
|
||||||
the `SerializableResource` is on this step;
|
|
||||||
- Change all public API that doesn't make sense, keeping in mind only to keep
|
|
||||||
this in the same namespace
|
|
||||||
- Update the README;
|
|
||||||
- Update the docs;
|
|
||||||
|
|
||||||
The following table represents the current and the desired classes and modules
|
|
||||||
at the first moment.
|
|
||||||
|
|
||||||
| Current | Desired | Notes |
|
|
||||||
|--------------------------------------------------------|--------------------------------------------------|--------------------|
|
|
||||||
| `ActiveModelSerializers` and `ActiveModel::Serializer` | `ActiveModelSerializers` | The main namespace |
|
|
||||||
| `ActiveModelSerializers.logger` | `ActiveModelSerializers.logger` ||
|
|
||||||
| `ActiveModelSerializers::Model` | `ActiveModelSerializers::Model` ||
|
|
||||||
| `ActiveModel::SerializableResource` | `ActiveModelSerializers::SerializableResource` ||
|
|
||||||
| `ActiveModel::Serializer` | `ActiveModelSerializers::Serializer` | The name can be discussed in a future pull request. For example, we can rename this to `Resource` [following this idea](https://github.com/rails-api/active_model_serializers/pull/1301/files#r42963185) more info about naming in the next section|
|
|
||||||
| `ActiveModel::Serializer.config` | `ActiveModelSerializers.config` ||
|
|
||||||
|
|
||||||
## Renaming of class and modules
|
|
||||||
|
|
||||||
When moving some content to the new namespace we can find some names that does
|
|
||||||
not make much sense like `ActiveModel::Serializer::Adapter::JsonApi`.
|
|
||||||
Discussion of renaming existing classes / modules and JsonApi objects will
|
|
||||||
happen in separate pull requests, and issues, and in the google doc
|
|
||||||
https://docs.google.com/document/d/1rcrJr0sVcazY2Opd_6Kmv1iIwuHbI84s1P_NzFn-05c/edit?usp=sharing
|
|
||||||
|
|
||||||
Some of names already have a definition.
|
|
||||||
|
|
||||||
- Adapters get their own namespace under ActiveModelSerializers. E.g
|
|
||||||
`ActiveModelSerializers::Adapter`
|
|
||||||
- Serializers get their own namespace under ActiveModelSerializers. E.g
|
|
||||||
`ActiveModelSerializers::Serializer`
|
|
||||||
|
|
||||||
## Keeping compatibility
|
|
||||||
|
|
||||||
All moved classes or modules be aliased to their old name and location with
|
|
||||||
deprecation warnings, such as
|
|
||||||
[was done for CollectionSerializer](https://github.com/rails-api/active_model_serializers/pull/1251).
|
|
||||||
|
|
||||||
# Drawbacks
|
|
||||||
|
|
||||||
This will be a breaking change, so all users serializers will be broken after a
|
|
||||||
major bump.
|
|
||||||
All pull requests will need to rebase since the architeture will change a lot.
|
|
||||||
|
|
||||||
# Alternatives
|
|
||||||
|
|
||||||
We can keep the way it is, and keep in mind to not add another namespace as a
|
|
||||||
public API.
|
|
||||||
|
|
||||||
# Unresolved questions
|
|
||||||
|
|
||||||
What is the better class name to be used to the class that will be inherited at
|
|
||||||
the creation of a serializer. This can be discussed in other RFC or directly via
|
|
||||||
pull request.
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
require 'active_support/core_ext/class/attribute'
|
|
||||||
require 'active_model_serializers/serialization_context'
|
|
||||||
|
|
||||||
module ActionController
|
|
||||||
module Serialization
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
include ActionController::Renderers
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
def serialization_scope(scope)
|
|
||||||
self._serialization_scope = scope
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
included do
|
|
||||||
class_attribute :_serialization_scope
|
|
||||||
self._serialization_scope = :current_user
|
|
||||||
|
|
||||||
attr_writer :namespace_for_serializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def namespace_for_serializer
|
|
||||||
@namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialization_scope
|
|
||||||
return unless _serialization_scope && respond_to?(_serialization_scope, true)
|
|
||||||
|
|
||||||
send(_serialization_scope)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_serializer(resource, options = {})
|
|
||||||
unless use_adapter?
|
|
||||||
warn 'ActionController::Serialization#use_adapter? has been removed. '\
|
|
||||||
"Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
|
|
||||||
options[:adapter] = false
|
|
||||||
end
|
|
||||||
|
|
||||||
options.fetch(:namespace) { options[:namespace] = namespace_for_serializer }
|
|
||||||
|
|
||||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
|
|
||||||
serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
|
|
||||||
serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
|
|
||||||
# For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
|
|
||||||
# Otherwise, since `serializable_resource` is not a string, the renderer would call
|
|
||||||
# `to_json` on a String and given odd results, such as `"".to_json #=> '""'`
|
|
||||||
serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource
|
|
||||||
end
|
|
||||||
|
|
||||||
# Deprecated
|
|
||||||
def use_adapter?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
[:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
|
|
||||||
define_method renderer_method do |resource, options|
|
|
||||||
options.fetch(:serialization_context) do
|
|
||||||
options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options)
|
|
||||||
end
|
|
||||||
serializable_resource = get_serializer(resource, options)
|
|
||||||
super(serializable_resource, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
require 'set'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class SerializableResource
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
|
|
||||||
delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,409 +0,0 @@
|
|||||||
require 'thread_safe'
|
|
||||||
require 'jsonapi/include_directive'
|
|
||||||
require 'active_model/serializer/collection_serializer'
|
|
||||||
require 'active_model/serializer/array_serializer'
|
|
||||||
require 'active_model/serializer/error_serializer'
|
|
||||||
require 'active_model/serializer/errors_serializer'
|
|
||||||
require 'active_model/serializer/concerns/caching'
|
|
||||||
require 'active_model/serializer/fieldset'
|
|
||||||
require 'active_model/serializer/lint'
|
|
||||||
|
|
||||||
# ActiveModel::Serializer is an abstract class that is
|
|
||||||
# reified when subclassed to decorate a resource.
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
undef_method :select, :display # These IO methods, which are mixed into Kernel,
|
|
||||||
# sometimes conflict with attribute names. We don't need these IO methods.
|
|
||||||
|
|
||||||
# @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
|
|
||||||
include ActiveSupport::Configurable
|
|
||||||
include Caching
|
|
||||||
|
|
||||||
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
|
||||||
# @return [ActiveModel::Serializer]
|
|
||||||
# Preferentially returns
|
|
||||||
# 1. resource.serializer_class
|
|
||||||
# 2. ArraySerializer when resource is a collection
|
|
||||||
# 3. options[:serializer]
|
|
||||||
# 4. lookup serializer when resource is a Class
|
|
||||||
def self.serializer_for(resource_or_class, options = {})
|
|
||||||
if resource_or_class.respond_to?(:serializer_class)
|
|
||||||
resource_or_class.serializer_class
|
|
||||||
elsif resource_or_class.respond_to?(:to_ary)
|
|
||||||
config.collection_serializer
|
|
||||||
else
|
|
||||||
resource_class = resource_or_class.class == Class ? resource_or_class : resource_or_class.class
|
|
||||||
options.fetch(:serializer) { get_serializer_for(resource_class, options[:namespace]) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @see ActiveModelSerializers::Adapter.lookup
|
|
||||||
# Deprecated
|
|
||||||
def self.adapter
|
|
||||||
ActiveModelSerializers::Adapter.lookup(config.adapter)
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :adapter, 'ActiveModelSerializers::Adapter.configured_adapter'
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def self.serializer_lookup_chain_for(klass, namespace = nil)
|
|
||||||
lookups = ActiveModelSerializers.config.serializer_lookup_chain
|
|
||||||
Array[*lookups].flat_map do |lookup|
|
|
||||||
lookup.call(klass, self, namespace)
|
|
||||||
end.compact
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used to cache serializer name => serializer class
|
|
||||||
# when looked up by Serializer.get_serializer_for.
|
|
||||||
def self.serializers_cache
|
|
||||||
@serializers_cache ||= ThreadSafe::Cache.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
# Find a serializer from a class and caches the lookup.
|
|
||||||
# Preferentially returns:
|
|
||||||
# 1. class name appended with "Serializer"
|
|
||||||
# 2. try again with superclass, if present
|
|
||||||
# 3. nil
|
|
||||||
def self.get_serializer_for(klass, namespace = nil)
|
|
||||||
return nil unless config.serializer_lookup_enabled
|
|
||||||
|
|
||||||
cache_key = ActiveSupport::Cache.expand_cache_key(klass, namespace)
|
|
||||||
serializers_cache.fetch_or_store(cache_key) do
|
|
||||||
# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs.
|
|
||||||
lookup_chain = serializer_lookup_chain_for(klass, namespace)
|
|
||||||
serializer_class = lookup_chain.map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer }
|
|
||||||
|
|
||||||
if serializer_class
|
|
||||||
serializer_class
|
|
||||||
elsif klass.superclass
|
|
||||||
get_serializer_for(klass.superclass)
|
|
||||||
else
|
|
||||||
nil # No serializer found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def self.include_directive_from_options(options)
|
|
||||||
if options[:include_directive]
|
|
||||||
options[:include_directive]
|
|
||||||
elsif options[:include]
|
|
||||||
JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
|
||||||
else
|
|
||||||
ActiveModelSerializers.default_include_directive
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def self.serialization_adapter_instance
|
|
||||||
@serialization_adapter_instance ||= ActiveModelSerializers::Adapter::Attributes
|
|
||||||
end
|
|
||||||
|
|
||||||
# Preferred interface is ActiveModelSerializers.config
|
|
||||||
# BEGIN DEFAULT CONFIGURATION
|
|
||||||
config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
|
|
||||||
config.serializer_lookup_enabled = true
|
|
||||||
|
|
||||||
# @deprecated Use {#config.collection_serializer=} instead of this. Is
|
|
||||||
# compatibility layer for ArraySerializer.
|
|
||||||
def config.array_serializer=(collection_serializer)
|
|
||||||
self.collection_serializer = collection_serializer
|
|
||||||
end
|
|
||||||
|
|
||||||
# @deprecated Use {#config.collection_serializer} instead of this. Is
|
|
||||||
# compatibility layer for ArraySerializer.
|
|
||||||
def config.array_serializer
|
|
||||||
collection_serializer
|
|
||||||
end
|
|
||||||
|
|
||||||
config.default_includes = '*'
|
|
||||||
config.adapter = :attributes
|
|
||||||
config.key_transform = nil
|
|
||||||
config.jsonapi_pagination_links_enabled = true
|
|
||||||
config.jsonapi_resource_type = :plural
|
|
||||||
config.jsonapi_namespace_separator = '-'.freeze
|
|
||||||
config.jsonapi_version = '1.0'
|
|
||||||
config.jsonapi_toplevel_meta = {}
|
|
||||||
# Make JSON API top-level jsonapi member opt-in
|
|
||||||
# ref: http://jsonapi.org/format/#document-top-level
|
|
||||||
config.jsonapi_include_toplevel_object = false
|
|
||||||
config.include_data_default = true
|
|
||||||
|
|
||||||
# For configuring how serializers are found.
|
|
||||||
# This should be an array of procs.
|
|
||||||
#
|
|
||||||
# The priority of the output is that the first item
|
|
||||||
# in the evaluated result array will take precedence
|
|
||||||
# over other possible serializer paths.
|
|
||||||
#
|
|
||||||
# i.e.: First match wins.
|
|
||||||
#
|
|
||||||
# @example output
|
|
||||||
# => [
|
|
||||||
# "CustomNamespace::ResourceSerializer",
|
|
||||||
# "ParentSerializer::ResourceSerializer",
|
|
||||||
# "ResourceNamespace::ResourceSerializer" ,
|
|
||||||
# "ResourceSerializer"]
|
|
||||||
#
|
|
||||||
# If CustomNamespace::ResourceSerializer exists, it will be used
|
|
||||||
# for serialization
|
|
||||||
config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup
|
|
||||||
|
|
||||||
config.schema_path = 'test/support/schemas'
|
|
||||||
# END DEFAULT CONFIGURATION
|
|
||||||
|
|
||||||
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
||||||
serializer.class_attribute :_attributes_data # @api private
|
|
||||||
self._attributes_data ||= {}
|
|
||||||
end
|
|
||||||
with_options instance_writer: false, instance_reader: true do |serializer|
|
|
||||||
serializer.class_attribute :_reflections
|
|
||||||
self._reflections ||= {}
|
|
||||||
serializer.class_attribute :_links # @api private
|
|
||||||
self._links ||= {}
|
|
||||||
serializer.class_attribute :_meta # @api private
|
|
||||||
serializer.class_attribute :_type # @api private
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.inherited(base)
|
|
||||||
super
|
|
||||||
base._attributes_data = _attributes_data.dup
|
|
||||||
base._reflections = _reflections.dup
|
|
||||||
base._links = _links.dup
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Symbol>] Key names of declared attributes
|
|
||||||
# @see Serializer::attribute
|
|
||||||
def self._attributes
|
|
||||||
_attributes_data.keys
|
|
||||||
end
|
|
||||||
|
|
||||||
# BEGIN SERIALIZER MACROS
|
|
||||||
|
|
||||||
# @example
|
|
||||||
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
||||||
# attributes :id, :name, :recent_edits
|
|
||||||
def self.attributes(*attrs)
|
|
||||||
attrs = attrs.first if attrs.first.class == Array
|
|
||||||
|
|
||||||
attrs.each do |attr|
|
|
||||||
attribute(attr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @example
|
|
||||||
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
||||||
# attributes :id, :recent_edits
|
|
||||||
# attribute :name, key: :title
|
|
||||||
#
|
|
||||||
# attribute :full_name do
|
|
||||||
# "#{object.first_name} #{object.last_name}"
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def recent_edits
|
|
||||||
# object.edits.last(5)
|
|
||||||
# end
|
|
||||||
def self.attribute(attr, options = {}, &block)
|
|
||||||
key = options.fetch(:key, attr)
|
|
||||||
_attributes_data[key] = Attribute.new(attr, options, block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Symbol] name of the association
|
|
||||||
# @param [Hash<Symbol => any>] options for the reflection
|
|
||||||
# @return [void]
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# has_many :comments, serializer: CommentSummarySerializer
|
|
||||||
#
|
|
||||||
def self.has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
||||||
associate(HasManyReflection.new(name, options, block))
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Symbol] name of the association
|
|
||||||
# @param [Hash<Symbol => any>] options for the reflection
|
|
||||||
# @return [void]
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# belongs_to :author, serializer: AuthorSerializer
|
|
||||||
#
|
|
||||||
def self.belongs_to(name, options = {}, &block)
|
|
||||||
associate(BelongsToReflection.new(name, options, block))
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Symbol] name of the association
|
|
||||||
# @param [Hash<Symbol => any>] options for the reflection
|
|
||||||
# @return [void]
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# has_one :author, serializer: AuthorSerializer
|
|
||||||
#
|
|
||||||
def self.has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
||||||
associate(HasOneReflection.new(name, options, block))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add reflection and define {name} accessor.
|
|
||||||
# @param [ActiveModel::Serializer::Reflection] reflection
|
|
||||||
# @return [void]
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
def self.associate(reflection)
|
|
||||||
key = reflection.options[:key] || reflection.name
|
|
||||||
self._reflections[key] = reflection
|
|
||||||
end
|
|
||||||
private_class_method :associate
|
|
||||||
|
|
||||||
# Define a link on a serializer.
|
|
||||||
# @example
|
|
||||||
# link(:self) { resource_url(object) }
|
|
||||||
# @example
|
|
||||||
# link(:self) { "http://example.com/resource/#{object.id}" }
|
|
||||||
# @example
|
|
||||||
# link :resource, "http://example.com/resource"
|
|
||||||
#
|
|
||||||
def self.link(name, value = nil, &block)
|
|
||||||
_links[name] = block || value
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the JSON API meta attribute of a serializer.
|
|
||||||
# @example
|
|
||||||
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
||||||
# meta { stuff: 'value' }
|
|
||||||
# @example
|
|
||||||
# meta do
|
|
||||||
# { comment_count: object.comments.count }
|
|
||||||
# end
|
|
||||||
def self.meta(value = nil, &block)
|
|
||||||
self._meta = block || value
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the JSON API type of a serializer.
|
|
||||||
# @example
|
|
||||||
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
||||||
# type 'authors'
|
|
||||||
def self.type(type)
|
|
||||||
self._type = type && type.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# END SERIALIZER MACROS
|
|
||||||
|
|
||||||
attr_accessor :object, :root, :scope
|
|
||||||
|
|
||||||
# `scope_name` is set as :current_user by default in the controller.
|
|
||||||
# If the instance does not have a method named `scope_name`, it
|
|
||||||
# defines the method so that it calls the +scope+.
|
|
||||||
def initialize(object, options = {})
|
|
||||||
self.object = object
|
|
||||||
self.instance_options = options
|
|
||||||
self.root = instance_options[:root]
|
|
||||||
self.scope = instance_options[:scope]
|
|
||||||
|
|
||||||
return if !(scope_name = instance_options[:scope_name]) || respond_to?(scope_name)
|
|
||||||
|
|
||||||
define_singleton_method scope_name, -> { scope }
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the +attributes+ of +object+ as presented
|
|
||||||
# by the serializer.
|
|
||||||
def attributes(requested_attrs = nil, reload = false)
|
|
||||||
@attributes = nil if reload
|
|
||||||
@attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
|
|
||||||
next if attr.excluded?(self)
|
|
||||||
next unless requested_attrs.nil? || requested_attrs.include?(key)
|
|
||||||
hash[key] = attr.value(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [JSONAPI::IncludeDirective] include_directive (defaults to the
|
|
||||||
# +default_include_directive+ config value when not provided)
|
|
||||||
# @return [Enumerator<Association>]
|
|
||||||
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
|
|
||||||
include_slice ||= include_directive
|
|
||||||
return Enumerator.new unless object
|
|
||||||
|
|
||||||
Enumerator.new do |y|
|
|
||||||
self.class._reflections.each do |key, reflection|
|
|
||||||
next if reflection.excluded?(self)
|
|
||||||
next unless include_directive.key?(key)
|
|
||||||
|
|
||||||
association = reflection.build_association(self, instance_options, include_slice)
|
|
||||||
y.yield association
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Hash] containing the attributes and first level
|
|
||||||
# associations, similar to how ActiveModel::Serializers::JSON is used
|
|
||||||
# in ActiveRecord::Base.
|
|
||||||
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)
|
|
||||||
resource = attributes_hash(adapter_options, options, adapter_instance)
|
|
||||||
relationships = associations_hash(adapter_options, options, adapter_instance)
|
|
||||||
resource.merge(relationships)
|
|
||||||
end
|
|
||||||
alias to_hash serializable_hash
|
|
||||||
alias to_h serializable_hash
|
|
||||||
|
|
||||||
# @see #serializable_hash
|
|
||||||
def as_json(adapter_opts = nil)
|
|
||||||
serializable_hash(adapter_opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used by adapter as resource root.
|
|
||||||
def json_key
|
|
||||||
root || _type || object.class.model_name.to_s.underscore
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_attribute_for_serialization(attr)
|
|
||||||
if respond_to?(attr)
|
|
||||||
send(attr)
|
|
||||||
else
|
|
||||||
object.read_attribute_for_serialization(attr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def attributes_hash(_adapter_options, options, adapter_instance)
|
|
||||||
if self.class.cache_enabled?
|
|
||||||
fetch_attributes(options[:fields], options[:cached_attributes] || {}, adapter_instance)
|
|
||||||
elsif self.class.fragment_cache_enabled?
|
|
||||||
fetch_attributes_fragment(adapter_instance, options[:cached_attributes] || {})
|
|
||||||
else
|
|
||||||
attributes(options[:fields], true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def associations_hash(adapter_options, options, adapter_instance)
|
|
||||||
include_directive = options.fetch(:include_directive)
|
|
||||||
include_slice = options[:include_slice]
|
|
||||||
associations(include_directive, include_slice).each_with_object({}) do |association, relationships|
|
|
||||||
adapter_opts = adapter_options.merge(include_directive: include_directive[association.key], adapter_instance: adapter_instance)
|
|
||||||
relationships[association.key] = association.serializable_hash(adapter_opts, adapter_instance)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_accessor :instance_options
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
require 'active_model_serializers/adapter'
|
|
||||||
require 'active_model_serializers/deprecate'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# @deprecated Use ActiveModelSerializers::Adapter instead
|
|
||||||
module Adapter
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
|
|
||||||
DEPRECATED_METHODS = [:create, :adapter_class, :adapter_map, :adapters, :register, :lookup].freeze
|
|
||||||
DEPRECATED_METHODS.each do |method|
|
|
||||||
delegate_and_deprecate method, ActiveModelSerializers::Adapter
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'active_model/serializer/adapter/base'
|
|
||||||
require 'active_model/serializer/adapter/null'
|
|
||||||
require 'active_model/serializer/adapter/attributes'
|
|
||||||
require 'active_model/serializer/adapter/json'
|
|
||||||
require 'active_model/serializer/adapter/json_api'
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Adapter
|
|
||||||
class Attributes < DelegateClass(ActiveModelSerializers::Adapter::Attributes)
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
super(ActiveModelSerializers::Adapter::Attributes.new(serializer, options))
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :new, 'ActiveModelSerializers::Adapter::Json.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Adapter
|
|
||||||
class Base < DelegateClass(ActiveModelSerializers::Adapter::Base)
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :inherited, 'ActiveModelSerializers::Adapter::Base.'
|
|
||||||
end
|
|
||||||
|
|
||||||
# :nocov:
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
super(ActiveModelSerializers::Adapter::Base.new(serializer, options))
|
|
||||||
end
|
|
||||||
# :nocov:
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Adapter
|
|
||||||
class Json < DelegateClass(ActiveModelSerializers::Adapter::Json)
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
super(ActiveModelSerializers::Adapter::Json.new(serializer, options))
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :new, 'ActiveModelSerializers::Adapter::Json.new'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Adapter
|
|
||||||
class JsonApi < DelegateClass(ActiveModelSerializers::Adapter::JsonApi)
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
super(ActiveModelSerializers::Adapter::JsonApi.new(serializer, options))
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :new, 'ActiveModelSerializers::Adapter::JsonApi.new'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Adapter
|
|
||||||
class Null < DelegateClass(ActiveModelSerializers::Adapter::Null)
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
super(ActiveModelSerializers::Adapter::Null.new(serializer, options))
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :new, 'ActiveModelSerializers::Adapter::Null.new'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
require 'active_model/serializer/collection_serializer'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class ArraySerializer < CollectionSerializer
|
|
||||||
class << self
|
|
||||||
extend ActiveModelSerializers::Deprecate
|
|
||||||
deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
require 'active_model/serializer/lazy_association'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# This class holds all information about serializer's association.
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
Association = Struct.new(:reflection, :association_options) do
|
|
||||||
attr_reader :lazy_association
|
|
||||||
delegate :object, :include_data?, :virtual_value, :collection?, to: :lazy_association
|
|
||||||
|
|
||||||
def initialize(*)
|
|
||||||
super
|
|
||||||
@lazy_association = LazyAssociation.new(reflection, association_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Symbol]
|
|
||||||
delegate :name, to: :reflection
|
|
||||||
|
|
||||||
# @return [Symbol]
|
|
||||||
def key
|
|
||||||
reflection_options.fetch(:key, name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [True,False]
|
|
||||||
def key?
|
|
||||||
reflection_options.key?(:key)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Hash]
|
|
||||||
def links
|
|
||||||
reflection_options.fetch(:links) || {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Hash, nil]
|
|
||||||
# This gets mutated, so cannot use the cached reflection_options
|
|
||||||
def meta
|
|
||||||
reflection.options[:meta]
|
|
||||||
end
|
|
||||||
|
|
||||||
def belongs_to?
|
|
||||||
reflection.foreign_key_on == :self
|
|
||||||
end
|
|
||||||
|
|
||||||
def polymorphic?
|
|
||||||
true == reflection_options[:polymorphic]
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def serializable_hash(adapter_options, adapter_instance)
|
|
||||||
association_serializer = lazy_association.serializer
|
|
||||||
return virtual_value if virtual_value
|
|
||||||
association_object = association_serializer && association_serializer.object
|
|
||||||
return unless association_object
|
|
||||||
|
|
||||||
serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
|
|
||||||
|
|
||||||
if polymorphic? && serialization
|
|
||||||
polymorphic_type = association_object.class.name.underscore
|
|
||||||
serialization = { type: polymorphic_type, polymorphic_type.to_sym => serialization }
|
|
||||||
end
|
|
||||||
|
|
||||||
serialization
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
delegate :reflection_options, to: :lazy_association
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
require 'active_model/serializer/field'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# Holds all the meta-data about an attribute as it was specified in the
|
|
||||||
# ActiveModel::Serializer class.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# class PostSerializer < ActiveModel::Serializer
|
|
||||||
# attribute :content
|
|
||||||
# attribute :name, key: :title
|
|
||||||
# attribute :email, key: :author_email, if: :user_logged_in?
|
|
||||||
# attribute :preview do
|
|
||||||
# truncate(object.content)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def user_logged_in?
|
|
||||||
# current_user.logged_in?
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
class Attribute < Field
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# @api private
|
|
||||||
class BelongsToReflection < Reflection
|
|
||||||
# @api private
|
|
||||||
def foreign_key_on
|
|
||||||
:self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class CollectionSerializer
|
|
||||||
include Enumerable
|
|
||||||
delegate :each, to: :@serializers
|
|
||||||
|
|
||||||
attr_reader :object, :root
|
|
||||||
|
|
||||||
def initialize(resources, options = {})
|
|
||||||
@object = resources
|
|
||||||
@options = options
|
|
||||||
@root = options[:root]
|
|
||||||
@serializers = serializers_from_resources
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
# @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)
|
|
||||||
serializers.map do |serializer|
|
|
||||||
serializer.serializable_hash(adapter_opts, options, adapter_instance)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: unify naming of root, json_key, and _type. Right now, a serializer's
|
|
||||||
# json_key comes from the root option or the object's model name, by default.
|
|
||||||
# But, if a dev defines a custom `json_key` method with an explicit value,
|
|
||||||
# we have no simple way to know that it is safe to call that instance method.
|
|
||||||
# (which is really a class property at this point, anyhow).
|
|
||||||
# rubocop:disable Metrics/CyclomaticComplexity
|
|
||||||
# Disabling cop since it's good to highlight the complexity of this method by
|
|
||||||
# including all the logic right here.
|
|
||||||
def json_key
|
|
||||||
return root if root
|
|
||||||
# 1. get from options[:serializer] for empty resource collection
|
|
||||||
key = object.empty? &&
|
|
||||||
(explicit_serializer_class = options[:serializer]) &&
|
|
||||||
explicit_serializer_class._type
|
|
||||||
# 2. get from first serializer instance in collection
|
|
||||||
key ||= (serializer = serializers.first) && serializer.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
|
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/CyclomaticComplexity
|
|
||||||
|
|
||||||
def paginated?
|
|
||||||
ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
|
|
||||||
object.respond_to?(:current_page) &&
|
|
||||||
object.respond_to?(:total_pages) &&
|
|
||||||
object.respond_to?(:size)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :serializers, :options
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def serializers_from_resources
|
|
||||||
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
|
|
||||||
object.map do |resource|
|
|
||||||
serializer_from_resource(resource, serializer_context_class, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_from_resource(resource, serializer_context_class, options)
|
|
||||||
serializer_class = options.fetch(:serializer) do
|
|
||||||
serializer_context_class.serializer_for(resource, namespace: options[:namespace])
|
|
||||||
end
|
|
||||||
|
|
||||||
if serializer_class.nil?
|
|
||||||
ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}"
|
|
||||||
throw :no_serializer
|
|
||||||
else
|
|
||||||
serializer_class.new(resource, options.except(:serializer))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,300 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
UndefinedCacheKey = Class.new(StandardError)
|
|
||||||
module Caching
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
included do
|
|
||||||
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
||||||
serializer.class_attribute :_cache # @api private : the cache store
|
|
||||||
serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key.
|
|
||||||
serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists fetch_attributes. Cannot combine with except
|
|
||||||
serializer.class_attribute :_cache_except # @api private : when fragment caching, blacklists fetch_attributes. Cannot combine with only
|
|
||||||
serializer.class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch
|
|
||||||
# _cache_options include:
|
|
||||||
# expires_in
|
|
||||||
# compress
|
|
||||||
# force
|
|
||||||
# race_condition_ttl
|
|
||||||
# Passed to ::_cache as
|
|
||||||
# serializer.cache_store.fetch(cache_key, @klass._cache_options)
|
|
||||||
# Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class._cache_options)
|
|
||||||
serializer.class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Matches
|
|
||||||
# "c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"
|
|
||||||
# AND
|
|
||||||
# "/c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"
|
|
||||||
# AS
|
|
||||||
# c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb
|
|
||||||
CALLER_FILE = /
|
|
||||||
\A # start of string
|
|
||||||
.+ # file path (one or more characters)
|
|
||||||
(?= # stop previous match when
|
|
||||||
:\d+ # a colon is followed by one or more digits
|
|
||||||
:in # followed by a colon followed by in
|
|
||||||
)
|
|
||||||
/x
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
def inherited(base)
|
|
||||||
caller_line = caller[1]
|
|
||||||
base._cache_digest_file_path = caller_line
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def _cache_digest
|
|
||||||
return @_cache_digest if defined?(@_cache_digest)
|
|
||||||
@_cache_digest = digest_caller_file(_cache_digest_file_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Hashes contents of file for +_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)
|
|
||||||
rescue TypeError, Errno::ENOENT
|
|
||||||
warn <<-EOF.strip_heredoc
|
|
||||||
Cannot digest non-existent file: '#{caller_line}'.
|
|
||||||
Please set `::_cache_digest` of the serializer
|
|
||||||
if you'd like to cache it.
|
|
||||||
EOF
|
|
||||||
''.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
def _skip_digest?
|
|
||||||
_cache_options && _cache_options[:skip_digest]
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
# maps attribute value to explicit key name
|
|
||||||
# @see Serializer::attribute
|
|
||||||
# @see Serializer::fragmented_attributes
|
|
||||||
def _attributes_keys
|
|
||||||
_attributes_data
|
|
||||||
.each_with_object({}) do |(key, attr), hash|
|
|
||||||
next if key == attr.name
|
|
||||||
hash[attr.name] = { key: key }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fragmented_attributes
|
|
||||||
cached = _cache_only ? _cache_only : _attributes - _cache_except
|
|
||||||
cached = cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
||||||
non_cached = _attributes - cached
|
|
||||||
non_cached = non_cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
||||||
{
|
|
||||||
cached: cached,
|
|
||||||
non_cached: non_cached
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Enables a serializer to be automatically cached
|
|
||||||
#
|
|
||||||
# Sets +::_cache+ object to <tt>ActionController::Base.cache_store</tt>
|
|
||||||
# when Rails.configuration.action_controller.perform_caching
|
|
||||||
#
|
|
||||||
# @param options [Hash] with valid keys:
|
|
||||||
# cache_store : @see ::_cache
|
|
||||||
# key : @see ::_cache_key
|
|
||||||
# only : @see ::_cache_only
|
|
||||||
# except : @see ::_cache_except
|
|
||||||
# skip_digest : does not include digest in cache_key
|
|
||||||
# all else : @see ::_cache_options
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# class PostSerializer < ActiveModel::Serializer
|
|
||||||
# cache key: 'post', expires_in: 3.hours
|
|
||||||
# attributes :title, :body
|
|
||||||
#
|
|
||||||
# has_many :comments
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @todo require less code comments. See
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837
|
|
||||||
def cache(options = {})
|
|
||||||
self._cache =
|
|
||||||
options.delete(:cache_store) ||
|
|
||||||
ActiveModelSerializers.config.cache_store ||
|
|
||||||
ActiveSupport::Cache.lookup_store(:null_store)
|
|
||||||
self._cache_key = options.delete(:key)
|
|
||||||
self._cache_only = options.delete(:only)
|
|
||||||
self._cache_except = options.delete(:except)
|
|
||||||
self._cache_options = options.empty? ? nil : options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Value is from ActiveModelSerializers.config.perform_caching. Is used to
|
|
||||||
# globally enable or disable all serializer caching, just like
|
|
||||||
# Rails.configuration.action_controller.perform_caching, which is its
|
|
||||||
# default value in a Rails application.
|
|
||||||
# @return [true, false]
|
|
||||||
# Memoizes value of config first time it is called with a non-nil value.
|
|
||||||
# rubocop:disable Style/ClassVars
|
|
||||||
def perform_caching
|
|
||||||
return @@perform_caching if defined?(@@perform_caching) && !@@perform_caching.nil?
|
|
||||||
@@perform_caching = ActiveModelSerializers.config.perform_caching
|
|
||||||
end
|
|
||||||
alias perform_caching? perform_caching
|
|
||||||
# rubocop:enable Style/ClassVars
|
|
||||||
|
|
||||||
# The canonical method for getting the cache store for the serializer.
|
|
||||||
#
|
|
||||||
# @return [nil] when _cache is not set (i.e. when `cache` has not been called)
|
|
||||||
# @return [._cache] when _cache is not the NullStore
|
|
||||||
# @return [ActiveModelSerializers.config.cache_store] when _cache is the NullStore.
|
|
||||||
# This is so we can use `cache` being called to mean the serializer should be cached
|
|
||||||
# even if ActiveModelSerializers.config.cache_store has not yet been set.
|
|
||||||
# That means that when _cache is the NullStore and ActiveModelSerializers.config.cache_store
|
|
||||||
# is configured, `cache_store` becomes `ActiveModelSerializers.config.cache_store`.
|
|
||||||
# @return [nil] when _cache is the NullStore and ActiveModelSerializers.config.cache_store is nil.
|
|
||||||
def cache_store
|
|
||||||
return nil if _cache.nil?
|
|
||||||
return _cache if _cache.class != ActiveSupport::Cache::NullStore
|
|
||||||
if ActiveModelSerializers.config.cache_store
|
|
||||||
self._cache = ActiveModelSerializers.config.cache_store
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def cache_enabled?
|
|
||||||
perform_caching? && cache_store && !_cache_only && !_cache_except
|
|
||||||
end
|
|
||||||
|
|
||||||
def fragment_cache_enabled?
|
|
||||||
perform_caching? && cache_store &&
|
|
||||||
(_cache_only && !_cache_except || !_cache_only && _cache_except)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Read cache from cache_store
|
|
||||||
# @return [Hash]
|
|
||||||
# Used in CollectionSerializer to set :cached_attributes
|
|
||||||
def cache_read_multi(collection_serializer, adapter_instance, include_directive)
|
|
||||||
return {} if ActiveModelSerializers.config.cache_store.blank?
|
|
||||||
|
|
||||||
keys = object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
||||||
|
|
||||||
return {} if keys.blank?
|
|
||||||
|
|
||||||
ActiveModelSerializers.config.cache_store.read_multi(*keys)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find all cache_key for the collection_serializer
|
|
||||||
# @param serializers [ActiveModel::Serializer::CollectionSerializer]
|
|
||||||
# @param adapter_instance [ActiveModelSerializers::Adapter::Base]
|
|
||||||
# @param include_directive [JSONAPI::IncludeDirective]
|
|
||||||
# @return [Array] all cache_key of collection_serializer
|
|
||||||
def object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
||||||
cache_keys = []
|
|
||||||
|
|
||||||
collection_serializer.each do |serializer|
|
|
||||||
cache_keys << object_cache_key(serializer, adapter_instance)
|
|
||||||
|
|
||||||
serializer.associations(include_directive).each do |association|
|
|
||||||
# TODO(BF): Process relationship without evaluating lazy_association
|
|
||||||
association_serializer = association.lazy_association.serializer
|
|
||||||
if association_serializer.respond_to?(:each)
|
|
||||||
association_serializer.each do |sub_serializer|
|
|
||||||
cache_keys << object_cache_key(sub_serializer, adapter_instance)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cache_keys << object_cache_key(association_serializer, adapter_instance)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
cache_keys.compact.uniq
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [String, nil] the cache_key of the serializer or nil
|
|
||||||
def object_cache_key(serializer, adapter_instance)
|
|
||||||
return unless serializer.present? && serializer.object.present?
|
|
||||||
|
|
||||||
(serializer.class.cache_enabled? || serializer.class.fragment_cache_enabled?) ? serializer.cache_key(adapter_instance) : nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
### INSTANCE METHODS
|
|
||||||
def fetch_attributes(fields, cached_attributes, adapter_instance)
|
|
||||||
key = cache_key(adapter_instance)
|
|
||||||
cached_attributes.fetch(key) do
|
|
||||||
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
||||||
attributes(fields, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch(adapter_instance, cache_options = serializer_class._cache_options, key = nil)
|
|
||||||
if serializer_class.cache_store
|
|
||||||
key ||= cache_key(adapter_instance)
|
|
||||||
serializer_class.cache_store.fetch(key, cache_options) do
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# 1. Determine cached fields from serializer class options
|
|
||||||
# 2. Get non_cached_fields and fetch cache_fields
|
|
||||||
# 3. Merge the two hashes using adapter_instance#fragment_cache
|
|
||||||
def fetch_attributes_fragment(adapter_instance, cached_attributes = {})
|
|
||||||
serializer_class._cache_options ||= {}
|
|
||||||
serializer_class._cache_options[:key] = serializer_class._cache_key if serializer_class._cache_key
|
|
||||||
fields = serializer_class.fragmented_attributes
|
|
||||||
|
|
||||||
non_cached_fields = fields[:non_cached].dup
|
|
||||||
non_cached_hash = attributes(non_cached_fields, true)
|
|
||||||
include_directive = JSONAPI::IncludeDirective.new(non_cached_fields - non_cached_hash.keys)
|
|
||||||
non_cached_hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
||||||
|
|
||||||
cached_fields = fields[:cached].dup
|
|
||||||
key = cache_key(adapter_instance)
|
|
||||||
cached_hash =
|
|
||||||
cached_attributes.fetch(key) do
|
|
||||||
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
||||||
hash = attributes(cached_fields, true)
|
|
||||||
include_directive = JSONAPI::IncludeDirective.new(cached_fields - hash.keys)
|
|
||||||
hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# Merge both results
|
|
||||||
adapter_instance.fragment_cache(cached_hash, non_cached_hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cache_key(adapter_instance)
|
|
||||||
return @cache_key if defined?(@cache_key)
|
|
||||||
|
|
||||||
parts = []
|
|
||||||
parts << object_cache_key
|
|
||||||
parts << adapter_instance.cache_key
|
|
||||||
parts << serializer_class._cache_digest unless serializer_class._skip_digest?
|
|
||||||
@cache_key = expand_cache_key(parts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def expand_cache_key(parts)
|
|
||||||
ActiveSupport::Cache.expand_cache_key(parts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
object.cache_key
|
|
||||||
elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key]))
|
|
||||||
object_time_safe = object.updated_at
|
|
||||||
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
|
|
||||||
"#{serializer_cache_key}/#{object.id}-#{object_time_safe}"
|
|
||||||
else
|
|
||||||
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_class
|
|
||||||
@serializer_class ||= self.class
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class ErrorSerializer < ActiveModel::Serializer
|
|
||||||
# @return [Hash<field_name,Array<error_message>>]
|
|
||||||
def as_json
|
|
||||||
object.errors.messages
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
require 'active_model/serializer/error_serializer'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class ErrorsSerializer
|
|
||||||
include Enumerable
|
|
||||||
delegate :each, to: :@serializers
|
|
||||||
attr_reader :object, :root
|
|
||||||
|
|
||||||
def initialize(resources, options = {})
|
|
||||||
@root = options[:root]
|
|
||||||
@object = resources
|
|
||||||
@serializers = resources.map do |resource|
|
|
||||||
serializer_class = options.fetch(:serializer) { ActiveModel::Serializer::ErrorSerializer }
|
|
||||||
serializer_class.new(resource, options.except(:serializer))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def json_key
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :serializers
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# Holds all the meta-data about a field (i.e. attribute or association) as it was
|
|
||||||
# specified in the ActiveModel::Serializer class.
|
|
||||||
# Notice that the field block is evaluated in the context of the serializer.
|
|
||||||
Field = Struct.new(:name, :options, :block) do
|
|
||||||
def initialize(*)
|
|
||||||
super
|
|
||||||
|
|
||||||
validate_condition!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Compute the actual value of a field for a given serializer instance.
|
|
||||||
# @param [Serializer] The serializer instance for which the value is computed.
|
|
||||||
# @return [Object] value
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
#
|
|
||||||
def value(serializer)
|
|
||||||
if block
|
|
||||||
serializer.instance_eval(&block)
|
|
||||||
else
|
|
||||||
serializer.read_attribute_for_serialization(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Decide whether the field should be serialized by the given serializer instance.
|
|
||||||
# @param [Serializer] The serializer instance
|
|
||||||
# @return [Bool]
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
#
|
|
||||||
def excluded?(serializer)
|
|
||||||
case condition_type
|
|
||||||
when :if
|
|
||||||
!evaluate_condition(serializer)
|
|
||||||
when :unless
|
|
||||||
evaluate_condition(serializer)
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def validate_condition!
|
|
||||||
return if condition_type == :none
|
|
||||||
|
|
||||||
case condition
|
|
||||||
when Symbol, String, Proc
|
|
||||||
# noop
|
|
||||||
else
|
|
||||||
fail TypeError, "#{condition_type.inspect} should be a Symbol, String or Proc"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def evaluate_condition(serializer)
|
|
||||||
case condition
|
|
||||||
when Symbol
|
|
||||||
serializer.public_send(condition)
|
|
||||||
when String
|
|
||||||
serializer.instance_eval(condition)
|
|
||||||
when Proc
|
|
||||||
if condition.arity.zero?
|
|
||||||
serializer.instance_exec(&condition)
|
|
||||||
else
|
|
||||||
serializer.instance_exec(serializer, &condition)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def condition_type
|
|
||||||
@condition_type ||=
|
|
||||||
if options.key?(:if)
|
|
||||||
:if
|
|
||||||
elsif options.key?(:unless)
|
|
||||||
:unless
|
|
||||||
else
|
|
||||||
:none
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def condition
|
|
||||||
options[condition_type]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class Fieldset
|
|
||||||
def initialize(fields)
|
|
||||||
@raw_fields = fields || {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def fields
|
|
||||||
@fields ||= parsed_fields
|
|
||||||
end
|
|
||||||
|
|
||||||
def fields_for(type)
|
|
||||||
fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :raw_fields
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def parsed_fields
|
|
||||||
if raw_fields.is_a?(Hash)
|
|
||||||
raw_fields.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.map(&:to_sym) }
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# @api private
|
|
||||||
class HasManyReflection < Reflection
|
|
||||||
def collection?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# @api private
|
|
||||||
class HasOneReflection < Reflection
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# @api private
|
|
||||||
LazyAssociation = Struct.new(:reflection, :association_options) do
|
|
||||||
REFLECTION_OPTIONS = %i(key links polymorphic meta serializer virtual_value namespace).freeze
|
|
||||||
|
|
||||||
delegate :collection?, to: :reflection
|
|
||||||
|
|
||||||
def reflection_options
|
|
||||||
@reflection_options ||= reflection.options.dup.reject { |k, _| !REFLECTION_OPTIONS.include?(k) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def object
|
|
||||||
@object ||= reflection.value(
|
|
||||||
association_options.fetch(:parent_serializer),
|
|
||||||
association_options.fetch(:include_slice)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
alias_method :eval_reflection_block, :object
|
|
||||||
|
|
||||||
def include_data?
|
|
||||||
eval_reflection_block if reflection.block
|
|
||||||
reflection.include_data?(
|
|
||||||
association_options.fetch(:include_slice)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [ActiveModel::Serializer, nil]
|
|
||||||
def serializer
|
|
||||||
return @serializer if defined?(@serializer)
|
|
||||||
if serializer_class
|
|
||||||
serialize_object!(object)
|
|
||||||
elsif !object.nil? && !object.instance_of?(Object)
|
|
||||||
cached_result[:virtual_value] = object
|
|
||||||
end
|
|
||||||
@serializer = cached_result[:serializer]
|
|
||||||
end
|
|
||||||
|
|
||||||
def virtual_value
|
|
||||||
cached_result[:virtual_value] || reflection_options[:virtual_value]
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_class
|
|
||||||
return @serializer_class if defined?(@serializer_class)
|
|
||||||
serializer_for_options = { namespace: namespace }
|
|
||||||
serializer_for_options[:serializer] = reflection_options[:serializer] if reflection_options.key?(:serializer)
|
|
||||||
@serializer_class = association_options.fetch(:parent_serializer).class.serializer_for(object, serializer_for_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def cached_result
|
|
||||||
@cached_result ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialize_object!(object)
|
|
||||||
if collection?
|
|
||||||
if (serializer = instantiate_collection_serializer(object)).nil?
|
|
||||||
# BUG: per #2027, JSON API resource relationships are only id and type, and hence either
|
|
||||||
# *require* a serializer or we need to be a little clever about figuring out the id/type.
|
|
||||||
# In either case, returning the raw virtual value will almost always be incorrect.
|
|
||||||
#
|
|
||||||
# Should be reflection_options[:virtual_value] or adapter needs to figure out what to do
|
|
||||||
# with an object that is non-nil and has no defined serializer.
|
|
||||||
cached_result[:virtual_value] = object.try(:as_json) || object
|
|
||||||
else
|
|
||||||
cached_result[:serializer] = serializer
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cached_result[:serializer] = instantiate_serializer(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def instantiate_serializer(object)
|
|
||||||
serializer_options = association_options.fetch(:parent_serializer_options).except(:serializer)
|
|
||||||
serializer_options[:serializer_context_class] = association_options.fetch(:parent_serializer).class
|
|
||||||
serializer = reflection_options.fetch(:serializer, nil)
|
|
||||||
serializer_options[:serializer] = serializer if serializer
|
|
||||||
serializer_class.new(object, serializer_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def instantiate_collection_serializer(object)
|
|
||||||
serializer = catch(:no_serializer) do
|
|
||||||
instantiate_serializer(object)
|
|
||||||
end
|
|
||||||
serializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def namespace
|
|
||||||
reflection_options[:namespace] ||
|
|
||||||
association_options.fetch(:parent_serializer_options)[:namespace]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,150 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
module Lint
|
|
||||||
# == Active \Model \Serializer \Lint \Tests
|
|
||||||
#
|
|
||||||
# You can test whether an object is compliant with the Active \Model \Serializers
|
|
||||||
# API by including <tt>ActiveModel::Serializer::Lint::Tests</tt> in your TestCase.
|
|
||||||
# It will include tests that tell you whether your object is fully compliant,
|
|
||||||
# or if not, which aspects of the API are not implemented.
|
|
||||||
#
|
|
||||||
# Note an object is not required to implement all APIs in order to work
|
|
||||||
# with Active \Model \Serializers. This module only intends to provide guidance in case
|
|
||||||
# you want all features out of the box.
|
|
||||||
#
|
|
||||||
# These tests do not attempt to determine the semantic correctness of the
|
|
||||||
# returned values. For instance, you could implement <tt>serializable_hash</tt> to
|
|
||||||
# always return +{}+, and the tests would pass. It is up to you to ensure
|
|
||||||
# that the values are semantically meaningful.
|
|
||||||
module Tests
|
|
||||||
# Passes if the object responds to <tt>serializable_hash</tt> and if it takes
|
|
||||||
# zero or one arguments.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>serializable_hash</tt> returns a hash representation of a object's attributes.
|
|
||||||
# Typically, it is implemented by including ActiveModel::Serialization.
|
|
||||||
def test_serializable_hash
|
|
||||||
assert_respond_to resource, :serializable_hash, 'The resource should respond to serializable_hash'
|
|
||||||
resource.serializable_hash
|
|
||||||
resource.serializable_hash(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>read_attribute_for_serialization</tt>
|
|
||||||
# and if it requires one argument (the attribute to be read).
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>read_attribute_for_serialization</tt> gets the attribute value for serialization
|
|
||||||
# Typically, it is implemented by including ActiveModel::Serialization.
|
|
||||||
def test_read_attribute_for_serialization
|
|
||||||
assert_respond_to resource, :read_attribute_for_serialization, 'The resource should respond to read_attribute_for_serialization'
|
|
||||||
actual_arity = resource.method(:read_attribute_for_serialization).arity
|
|
||||||
# using absolute value since arity is:
|
|
||||||
# 1 for def read_attribute_for_serialization(name); end
|
|
||||||
# -1 for alias :read_attribute_for_serialization :send
|
|
||||||
assert_equal 1, actual_arity.abs, "expected #{actual_arity.inspect}.abs to be 1 or -1"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>as_json</tt> and if it takes
|
|
||||||
# zero or one arguments.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>as_json</tt> returns a hash representation of a serialized object.
|
|
||||||
# It may delegate to <tt>serializable_hash</tt>
|
|
||||||
# Typically, it is implemented either by including ActiveModel::Serialization
|
|
||||||
# which includes ActiveModel::Serializers::JSON.
|
|
||||||
# or by the JSON gem when required.
|
|
||||||
def test_as_json
|
|
||||||
assert_respond_to resource, :as_json
|
|
||||||
resource.as_json
|
|
||||||
resource.as_json(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>to_json</tt> and if it takes
|
|
||||||
# zero or one arguments.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
|
|
||||||
# It may be called on the result of <tt>as_json</tt>.
|
|
||||||
# Typically, it is implemented on all objects when the JSON gem is required.
|
|
||||||
def test_to_json
|
|
||||||
assert_respond_to resource, :to_json
|
|
||||||
resource.to_json
|
|
||||||
resource.to_json(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>cache_key</tt>
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
|
|
||||||
# and is part of the (self-expiring) cache_key, which is used by the
|
|
||||||
# adapter. It is not required unless caching is enabled.
|
|
||||||
def test_cache_key
|
|
||||||
assert_respond_to resource, :cache_key
|
|
||||||
actual_arity = resource.method(:cache_key).arity
|
|
||||||
assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>updated_at</tt> and if it takes no
|
|
||||||
# arguments.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>updated_at</tt> returns a Time object or iso8601 string and
|
|
||||||
# is part of the (self-expiring) cache_key, which is used by the adapter.
|
|
||||||
# It is not required unless caching is enabled.
|
|
||||||
def test_updated_at
|
|
||||||
assert_respond_to resource, :updated_at
|
|
||||||
actual_arity = resource.method(:updated_at).arity
|
|
||||||
assert_equal 0, actual_arity
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object responds to <tt>id</tt> and if it takes no
|
|
||||||
# arguments.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>id</tt> returns a unique identifier for the object.
|
|
||||||
# It is not required unless caching is enabled.
|
|
||||||
def test_id
|
|
||||||
assert_respond_to resource, :id
|
|
||||||
assert_equal 0, resource.method(:id).arity
|
|
||||||
end
|
|
||||||
|
|
||||||
# Passes if the object's class responds to <tt>model_name</tt> and if it
|
|
||||||
# is in an instance of +ActiveModel::Name+.
|
|
||||||
# Fails otherwise.
|
|
||||||
#
|
|
||||||
# <tt>model_name</tt> returns an ActiveModel::Name instance.
|
|
||||||
# It is used by the serializer to identify the object's type.
|
|
||||||
# It is not required unless caching is enabled.
|
|
||||||
def test_model_name
|
|
||||||
resource_class = resource.class
|
|
||||||
assert_respond_to resource_class, :model_name
|
|
||||||
assert_instance_of resource_class.model_name, ActiveModel::Name
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_active_model_errors
|
|
||||||
assert_respond_to resource, :errors
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_active_model_errors_human_attribute_name
|
|
||||||
assert_respond_to resource.class, :human_attribute_name
|
|
||||||
assert_equal(-2, resource.class.method(:human_attribute_name).arity)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_active_model_errors_lookup_ancestors
|
|
||||||
assert_respond_to resource.class, :lookup_ancestors
|
|
||||||
assert_equal 0, resource.class.method(:lookup_ancestors).arity
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def resource
|
|
||||||
@resource or fail "'@resource' must be set as the linted object"
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_instance_of(result, name)
|
|
||||||
assert result.instance_of?(name), "#{result} should be an instance of #{name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
class Null < Serializer
|
|
||||||
def attributes(*)
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
def associations(*)
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializable_hash(*)
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,207 +0,0 @@
|
|||||||
require 'active_model/serializer/field'
|
|
||||||
require 'active_model/serializer/association'
|
|
||||||
|
|
||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
# Holds all the meta-data about an association as it was specified in the
|
|
||||||
# ActiveModel::Serializer class.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# class PostSerializer < ActiveModel::Serializer
|
|
||||||
# has_one :author, serializer: AuthorSerializer
|
|
||||||
# belongs_to :boss, type: :users, foreign_key: :boss_id
|
|
||||||
# has_many :comments
|
|
||||||
# has_many :comments, key: :last_comments do
|
|
||||||
# object.comments.last(1)
|
|
||||||
# end
|
|
||||||
# has_many :secret_meta_data, if: :is_admin?
|
|
||||||
#
|
|
||||||
# has_one :blog do |serializer|
|
|
||||||
# meta count: object.roles.count
|
|
||||||
# serializer.cached_blog
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# private
|
|
||||||
#
|
|
||||||
# def cached_blog
|
|
||||||
# cache_store.fetch("cached_blog:#{object.updated_at}") do
|
|
||||||
# Blog.find(object.blog_id)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def is_admin?
|
|
||||||
# current_user.admin?
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Specifically, the association 'comments' is evaluated two different ways:
|
|
||||||
# 1) as 'comments' and named 'comments'.
|
|
||||||
# 2) as 'object.comments.last(1)' and named 'last_comments'.
|
|
||||||
#
|
|
||||||
# PostSerializer._reflections # =>
|
|
||||||
# # {
|
|
||||||
# # author: HasOneReflection.new(:author, serializer: AuthorSerializer),
|
|
||||||
# # comments: HasManyReflection.new(:comments)
|
|
||||||
# # last_comments: HasManyReflection.new(:comments, { key: :last_comments }, #<Block>)
|
|
||||||
# # secret_meta_data: HasManyReflection.new(:secret_meta_data, { if: :is_admin? })
|
|
||||||
# # }
|
|
||||||
#
|
|
||||||
# So you can inspect reflections in your Adapters.
|
|
||||||
class Reflection < Field
|
|
||||||
attr_reader :foreign_key, :type
|
|
||||||
|
|
||||||
def initialize(*)
|
|
||||||
super
|
|
||||||
options[:links] = {}
|
|
||||||
options[:include_data_setting] = Serializer.config.include_data_default
|
|
||||||
options[:meta] = nil
|
|
||||||
@type = options.fetch(:type) do
|
|
||||||
class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
|
|
||||||
class_name.underscore.pluralize.to_sym
|
|
||||||
end
|
|
||||||
@foreign_key = options.fetch(:foreign_key) do
|
|
||||||
if collection?
|
|
||||||
"#{name.to_s.singularize}_ids".to_sym
|
|
||||||
else
|
|
||||||
"#{name}_id".to_sym
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api public
|
|
||||||
# @example
|
|
||||||
# has_one :blog do
|
|
||||||
# include_data false
|
|
||||||
# link :self, 'a link'
|
|
||||||
# link :related, 'another link'
|
|
||||||
# link :self, '//example.com/link_author/relationships/bio'
|
|
||||||
# id = object.profile.id
|
|
||||||
# link :related do
|
|
||||||
# "//example.com/profiles/#{id}" if id != 123
|
|
||||||
# end
|
|
||||||
# link :related do
|
|
||||||
# ids = object.likes.map(&:id).join(',')
|
|
||||||
# href "//example.com/likes/#{ids}"
|
|
||||||
# meta ids: ids
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
def link(name, value = nil)
|
|
||||||
options[:links][name] = block_given? ? Proc.new : value
|
|
||||||
:nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api public
|
|
||||||
# @example
|
|
||||||
# has_one :blog do
|
|
||||||
# include_data false
|
|
||||||
# meta(id: object.blog.id)
|
|
||||||
# meta liked: object.likes.any?
|
|
||||||
# link :self do
|
|
||||||
# href object.blog.id.to_s
|
|
||||||
# meta(id: object.blog.id)
|
|
||||||
# end
|
|
||||||
def meta(value = nil)
|
|
||||||
options[:meta] = block_given? ? Proc.new : value
|
|
||||||
:nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api public
|
|
||||||
# @example
|
|
||||||
# has_one :blog do
|
|
||||||
# include_data false
|
|
||||||
# link :self, 'a link'
|
|
||||||
# link :related, 'another link'
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# has_one :blog do
|
|
||||||
# include_data false
|
|
||||||
# link :self, 'a link'
|
|
||||||
# link :related, 'another link'
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# belongs_to :reviewer do
|
|
||||||
# meta name: 'Dan Brown'
|
|
||||||
# include_data true
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# has_many :tags, serializer: TagSerializer do
|
|
||||||
# link :self, '//example.com/link_author/relationships/tags'
|
|
||||||
# include_data :if_sideloaded
|
|
||||||
# end
|
|
||||||
def include_data(value = true)
|
|
||||||
options[:include_data_setting] = value
|
|
||||||
:nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def collection?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
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 true then true
|
|
||||||
when false then false
|
|
||||||
else fail ArgumentError, "Unknown include_data_setting '#{include_data_setting.inspect}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param serializer [ActiveModel::Serializer]
|
|
||||||
# @yield [ActiveModel::Serializer]
|
|
||||||
# @return [:nil, associated resource or resource collection]
|
|
||||||
def value(serializer, include_slice)
|
|
||||||
@object = serializer.object
|
|
||||||
@scope = serializer.scope
|
|
||||||
|
|
||||||
block_value = instance_exec(serializer, &block) if block
|
|
||||||
return unless include_data?(include_slice)
|
|
||||||
|
|
||||||
if block && block_value != :nil
|
|
||||||
block_value
|
|
||||||
else
|
|
||||||
serializer.read_attribute_for_serialization(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def foreign_key_on
|
|
||||||
:related
|
|
||||||
end
|
|
||||||
|
|
||||||
# Build association. This method is used internally to
|
|
||||||
# build serializer's association by its reflection.
|
|
||||||
#
|
|
||||||
# @param [Serializer] parent_serializer for given association
|
|
||||||
# @param [Hash{Symbol => Object}] parent_serializer_options
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# # Given the following serializer defined:
|
|
||||||
# class PostSerializer < ActiveModel::Serializer
|
|
||||||
# has_many :comments, serializer: CommentSummarySerializer
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # Then you instantiate your serializer
|
|
||||||
# post_serializer = PostSerializer.new(post, foo: 'bar') #
|
|
||||||
# # to build association for comments you need to get reflection
|
|
||||||
# comments_reflection = PostSerializer._reflections.detect { |r| r.name == :comments }
|
|
||||||
# # and #build_association
|
|
||||||
# comments_reflection.build_association(post_serializer, foo: 'bar')
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
def build_association(parent_serializer, parent_serializer_options, include_slice = {})
|
|
||||||
association_options = {
|
|
||||||
parent_serializer: parent_serializer,
|
|
||||||
parent_serializer_options: parent_serializer_options,
|
|
||||||
include_slice: include_slice
|
|
||||||
}
|
|
||||||
Association.new(self, association_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# used in instance exec
|
|
||||||
attr_accessor :object, :scope
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
module ActiveModel
|
|
||||||
class Serializer
|
|
||||||
VERSION = '0.10.6'.freeze
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
require 'active_model'
|
|
||||||
require 'active_support'
|
|
||||||
require 'active_support/core_ext/object/with_options'
|
|
||||||
require 'active_support/core_ext/string/inflections'
|
|
||||||
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
|
|
||||||
|
|
||||||
class << self; attr_accessor :logger; end
|
|
||||||
self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
|
||||||
|
|
||||||
def self.config
|
|
||||||
ActiveModel::Serializer.config
|
|
||||||
end
|
|
||||||
|
|
||||||
# The file name and line number of the caller of the caller of this method.
|
|
||||||
def self.location_of_caller
|
|
||||||
caller[1] =~ /(.*?):(\d+).*?$/i
|
|
||||||
file = Regexp.last_match(1)
|
|
||||||
lineno = Regexp.last_match(2).to_i
|
|
||||||
|
|
||||||
[file, lineno]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Memoized default include directive
|
|
||||||
# @return [JSONAPI::IncludeDirective]
|
|
||||||
def self.default_include_directive
|
|
||||||
@default_include_directive ||= JSONAPI::IncludeDirective.new(config.default_includes, allow_wildcard: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.silence_warnings
|
|
||||||
original_verbose = $VERBOSE
|
|
||||||
$VERBOSE = nil
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
$VERBOSE = original_verbose
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'active_model/serializer/version'
|
|
||||||
require 'active_model/serializer'
|
|
||||||
require 'active_model/serializable_resource'
|
|
||||||
require 'active_model_serializers/railtie' if defined?(::Rails)
|
|
||||||
end
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
UnknownAdapterError = Class.new(ArgumentError)
|
|
||||||
ADAPTER_MAP = {} # rubocop:disable Style/MutableConstant
|
|
||||||
private_constant :ADAPTER_MAP if defined?(private_constant)
|
|
||||||
|
|
||||||
class << self # All methods are class functions
|
|
||||||
# :nocov:
|
|
||||||
def new(*args)
|
|
||||||
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
|
||||||
"Adapter.new called with args: '#{args.inspect}', from" \
|
|
||||||
"'caller[0]'."
|
|
||||||
end
|
|
||||||
# :nocov:
|
|
||||||
|
|
||||||
def configured_adapter
|
|
||||||
lookup(ActiveModelSerializers.config.adapter)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create(resource, options = {})
|
|
||||||
override = options.delete(:adapter)
|
|
||||||
klass = override ? adapter_class(override) : configured_adapter
|
|
||||||
klass.new(resource, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @see ActiveModelSerializers::Adapter.lookup
|
|
||||||
def adapter_class(adapter)
|
|
||||||
ActiveModelSerializers::Adapter.lookup(adapter)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Hash<adapter_name, adapter_class>]
|
|
||||||
def adapter_map
|
|
||||||
ADAPTER_MAP
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Symbol>] list of adapter names
|
|
||||||
def adapters
|
|
||||||
adapter_map.keys.sort
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
|
|
||||||
# Names are stringified and underscored
|
|
||||||
# @param name [Symbol, String, Class] name of the registered adapter
|
|
||||||
# @param klass [Class] adapter class itself, optional if name is the class
|
|
||||||
# @example
|
|
||||||
# AMS::Adapter.register(:my_adapter, MyAdapter)
|
|
||||||
# @note The registered name strips out 'ActiveModelSerializers::Adapter::'
|
|
||||||
# so that registering 'ActiveModelSerializers::Adapter::Json' and
|
|
||||||
# 'Json' will both register as 'json'.
|
|
||||||
def register(name, klass = name)
|
|
||||||
name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze)
|
|
||||||
adapter_map[name.underscore] = klass
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def registered_name(adapter_class)
|
|
||||||
ADAPTER_MAP.key adapter_class
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
|
||||||
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
|
||||||
# @raise [UnknownAdapterError]
|
|
||||||
def lookup(adapter)
|
|
||||||
# 1. return if is a class
|
|
||||||
return adapter if adapter.is_a?(Class)
|
|
||||||
adapter_name = adapter.to_s.underscore
|
|
||||||
# 2. return if registered
|
|
||||||
adapter_map.fetch(adapter_name) do
|
|
||||||
# 3. try to find adapter class from environment
|
|
||||||
adapter_class = find_by_name(adapter_name)
|
|
||||||
register(adapter_name, adapter_class)
|
|
||||||
adapter_class
|
|
||||||
end
|
|
||||||
rescue NameError, ArgumentError => e
|
|
||||||
failure_message =
|
|
||||||
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
|
|
||||||
raise UnknownAdapterError, failure_message, e.backtrace
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def find_by_name(adapter_name)
|
|
||||||
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
|
|
||||||
"ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize ||
|
|
||||||
"ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
|
|
||||||
fail UnknownAdapterError
|
|
||||||
end
|
|
||||||
private :find_by_name
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gotta be at the bottom to use the code above it :(
|
|
||||||
extend ActiveSupport::Autoload
|
|
||||||
autoload :Base
|
|
||||||
autoload :Null
|
|
||||||
autoload :Attributes
|
|
||||||
autoload :Json
|
|
||||||
autoload :JsonApi
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class Attributes < Base
|
|
||||||
def serializable_hash(options = nil)
|
|
||||||
options = serialization_options(options)
|
|
||||||
options[:fields] ||= instance_options[:fields]
|
|
||||||
serialized_hash = serializer.serializable_hash(instance_options, options, self)
|
|
||||||
|
|
||||||
self.class.transform_key_casing!(serialized_hash, instance_options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
require 'case_transform'
|
|
||||||
|
|
||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class Base
|
|
||||||
# Automatically register adapters when subclassing
|
|
||||||
def self.inherited(subclass)
|
|
||||||
ActiveModelSerializers::Adapter.register(subclass)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the default transform for the adapter.
|
|
||||||
#
|
|
||||||
# @return [Symbol] the default transform for the adapter
|
|
||||||
def self.default_key_transform
|
|
||||||
:unaltered
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determines the transform to use in order of precedence:
|
|
||||||
# adapter option, global config, adapter default.
|
|
||||||
#
|
|
||||||
# @param options [Object]
|
|
||||||
# @return [Symbol] the transform to use
|
|
||||||
def self.transform(options)
|
|
||||||
return options[:key_transform] if options && options[:key_transform]
|
|
||||||
ActiveModelSerializers.config.key_transform || default_key_transform
|
|
||||||
end
|
|
||||||
|
|
||||||
# Transforms the casing of the supplied value.
|
|
||||||
#
|
|
||||||
# @param value [Object] the value to be transformed
|
|
||||||
# @param options [Object] serializable resource options
|
|
||||||
# @return [Symbol] the default transform for the adapter
|
|
||||||
def self.transform_key_casing!(value, options)
|
|
||||||
CaseTransform.send(transform(options), value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.cache_key
|
|
||||||
@cache_key ||= ActiveModelSerializers::Adapter.registered_name(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.fragment_cache(cached_hash, non_cached_hash)
|
|
||||||
non_cached_hash.merge cached_hash
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :serializer, :instance_options
|
|
||||||
|
|
||||||
def initialize(serializer, options = {})
|
|
||||||
@serializer = serializer
|
|
||||||
@instance_options = options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Subclasses that implement this method must first call
|
|
||||||
# options = serialization_options(options)
|
|
||||||
def serializable_hash(_options = nil)
|
|
||||||
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json(options = nil)
|
|
||||||
serializable_hash(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cache_key
|
|
||||||
self.class.cache_key
|
|
||||||
end
|
|
||||||
|
|
||||||
def fragment_cache(cached_hash, non_cached_hash)
|
|
||||||
self.class.fragment_cache(cached_hash, non_cached_hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# see https://github.com/rails-api/active_model_serializers/pull/965
|
|
||||||
# When <tt>options</tt> is +nil+, sets it to +{}+
|
|
||||||
def serialization_options(options)
|
|
||||||
options ||= {} # rubocop:disable Lint/UselessAssignment
|
|
||||||
end
|
|
||||||
|
|
||||||
def root
|
|
||||||
serializer.json_key.to_sym if serializer.json_key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class Json < Base
|
|
||||||
def serializable_hash(options = nil)
|
|
||||||
options = serialization_options(options)
|
|
||||||
serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
|
|
||||||
serialized_hash[meta_key] = meta unless meta.blank?
|
|
||||||
|
|
||||||
self.class.transform_key_casing!(serialized_hash, instance_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def meta
|
|
||||||
instance_options.fetch(:meta, nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
def meta_key
|
|
||||||
instance_options.fetch(:meta_key, 'meta'.freeze)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,530 +0,0 @@
|
|||||||
# {http://jsonapi.org/format/ JSON API specification}
|
|
||||||
# rubocop:disable Style/AsciiComments
|
|
||||||
# TODO: implement!
|
|
||||||
# ☐ https://github.com/rails-api/active_model_serializers/issues/1235
|
|
||||||
# TODO: use uri_template in link generation?
|
|
||||||
# ☐ https://github.com/rails-api/active_model_serializers/pull/1282#discussion_r42528812
|
|
||||||
# see gem https://github.com/hannesg/uri_template
|
|
||||||
# spec http://tools.ietf.org/html/rfc6570
|
|
||||||
# impl https://developer.github.com/v3/#schema https://api.github.com/
|
|
||||||
# TODO: validate against a JSON schema document?
|
|
||||||
# ☐ https://github.com/rails-api/active_model_serializers/issues/1162
|
|
||||||
# ☑ https://github.com/rails-api/active_model_serializers/pull/1270
|
|
||||||
# TODO: Routing
|
|
||||||
# ☐ https://github.com/rails-api/active_model_serializers/pull/1476
|
|
||||||
# TODO: Query Params
|
|
||||||
# ☑ `include` https://github.com/rails-api/active_model_serializers/pull/1131
|
|
||||||
# ☑ `fields` https://github.com/rails-api/active_model_serializers/pull/700
|
|
||||||
# ☑ `page[number]=3&page[size]=1` https://github.com/rails-api/active_model_serializers/pull/1041
|
|
||||||
# ☐ `filter`
|
|
||||||
# ☐ `sort`
|
|
||||||
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
|
|
||||||
|
|
||||||
def self.default_key_transform
|
|
||||||
:dash
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
|
|
||||||
core_cached = cached_hash.first
|
|
||||||
core_non_cached = non_cached_hash.first
|
|
||||||
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
|
|
||||||
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
|
|
||||||
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
|
|
||||||
hash = root ? { root => cached_resource } : cached_resource
|
|
||||||
|
|
||||||
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
|
|
||||||
end
|
|
||||||
|
|
||||||
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))
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
|
|
||||||
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
|
|
||||||
def serializable_hash(*)
|
|
||||||
document = if serializer.success?
|
|
||||||
success_document
|
|
||||||
else
|
|
||||||
failure_document
|
|
||||||
end
|
|
||||||
self.class.transform_key_casing!(document, instance_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fragment_cache(cached_hash, non_cached_hash)
|
|
||||||
root = !instance_options.include?(:include)
|
|
||||||
self.class.fragment_cache(cached_hash, non_cached_hash, root)
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-top-level Primary data}
|
|
||||||
# definition:
|
|
||||||
# ☐ toplevel_data (required)
|
|
||||||
# ☐ toplevel_included
|
|
||||||
# ☑ toplevel_meta
|
|
||||||
# ☑ toplevel_links
|
|
||||||
# ☑ toplevel_jsonapi
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# data: toplevel_data,
|
|
||||||
# included: toplevel_included,
|
|
||||||
# meta: toplevel_meta,
|
|
||||||
# links: toplevel_links,
|
|
||||||
# jsonapi: toplevel_jsonapi
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
# rubocop:disable Metrics/CyclomaticComplexity
|
|
||||||
def success_document
|
|
||||||
is_collection = serializer.respond_to?(:each)
|
|
||||||
serializers = is_collection ? serializer : [serializer]
|
|
||||||
primary_data, included = resource_objects_for(serializers)
|
|
||||||
|
|
||||||
hash = {}
|
|
||||||
# toplevel_data
|
|
||||||
# definition:
|
|
||||||
# oneOf
|
|
||||||
# resource
|
|
||||||
# array of unique items of type 'resource'
|
|
||||||
# null
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# The document's "primary data" is a representation of the resource or collection of resources
|
|
||||||
# targeted by a request.
|
|
||||||
#
|
|
||||||
# Singular: the resource object.
|
|
||||||
#
|
|
||||||
# Collection: one of an array of resource objects, an array of resource identifier objects, or
|
|
||||||
# an empty array ([]), for requests that target resource collections.
|
|
||||||
#
|
|
||||||
# None: null if the request is one that might correspond to a single resource, but doesn't currently.
|
|
||||||
# structure:
|
|
||||||
# if serializable_resource.resource?
|
|
||||||
# resource
|
|
||||||
# elsif serializable_resource.collection?
|
|
||||||
# [
|
|
||||||
# resource,
|
|
||||||
# resource
|
|
||||||
# ]
|
|
||||||
# else
|
|
||||||
# nil
|
|
||||||
# end
|
|
||||||
hash[:data] = is_collection ? primary_data : primary_data[0]
|
|
||||||
# toplevel_included
|
|
||||||
# alias included
|
|
||||||
# definition:
|
|
||||||
# array of unique items of type 'resource'
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# To reduce the number of HTTP requests, servers **MAY** allow
|
|
||||||
# responses that include related resources along with the requested primary
|
|
||||||
# resources. Such responses are called "compound documents".
|
|
||||||
# structure:
|
|
||||||
# [
|
|
||||||
# resource,
|
|
||||||
# resource
|
|
||||||
# ]
|
|
||||||
hash[:included] = included if included.any?
|
|
||||||
|
|
||||||
Jsonapi.add!(hash)
|
|
||||||
|
|
||||||
if instance_options[:links]
|
|
||||||
hash[:links] ||= {}
|
|
||||||
hash[:links].update(instance_options[:links])
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_collection && serializer.paginated?
|
|
||||||
hash[:links] ||= {}
|
|
||||||
hash[:links].update(pagination_links_for(serializer))
|
|
||||||
end
|
|
||||||
|
|
||||||
hash[:meta] = instance_options[:meta] unless instance_options[:meta].blank?
|
|
||||||
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/CyclomaticComplexity
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#errors JSON API Errors}
|
|
||||||
# TODO: look into caching
|
|
||||||
# definition:
|
|
||||||
# ☑ toplevel_errors array (required)
|
|
||||||
# ☐ toplevel_meta
|
|
||||||
# ☐ toplevel_jsonapi
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# errors: toplevel_errors,
|
|
||||||
# meta: toplevel_meta,
|
|
||||||
# jsonapi: toplevel_jsonapi
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
# prs:
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1004
|
|
||||||
def failure_document
|
|
||||||
hash = {}
|
|
||||||
# PR Please :)
|
|
||||||
# Jsonapi.add!(hash)
|
|
||||||
|
|
||||||
# toplevel_errors
|
|
||||||
# definition:
|
|
||||||
# array of unique items of type 'error'
|
|
||||||
# structure:
|
|
||||||
# [
|
|
||||||
# error,
|
|
||||||
# error
|
|
||||||
# ]
|
|
||||||
if serializer.respond_to?(:each)
|
|
||||||
hash[:errors] = serializer.flat_map do |error_serializer|
|
|
||||||
Error.resource_errors(error_serializer, instance_options)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
hash[:errors] = Error.resource_errors(serializer, instance_options)
|
|
||||||
end
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :fieldset
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-resource-objects Primary data}
|
|
||||||
# resource
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# type (required) : String
|
|
||||||
# id (required) : String
|
|
||||||
# attributes
|
|
||||||
# relationships
|
|
||||||
# links
|
|
||||||
# meta
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# "Resource objects" appear in a JSON API document to represent resources
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# type: 'admin--some-user',
|
|
||||||
# id: '1336',
|
|
||||||
# attributes: attributes,
|
|
||||||
# relationships: relationships,
|
|
||||||
# links: links,
|
|
||||||
# meta: meta,
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
# prs:
|
|
||||||
# type
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1122
|
|
||||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1213
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1216
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1029
|
|
||||||
# links
|
|
||||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1246
|
|
||||||
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
|
|
||||||
# meta
|
|
||||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1340
|
|
||||||
def resource_objects_for(serializers)
|
|
||||||
@primary = []
|
|
||||||
@included = []
|
|
||||||
@resource_identifiers = Set.new
|
|
||||||
serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
|
|
||||||
serializers.each { |serializer| process_relationships(serializer, @include_directive) }
|
|
||||||
|
|
||||||
[@primary, @included]
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_resource(serializer, primary, include_slice = {})
|
|
||||||
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
||||||
return false unless @resource_identifiers.add?(resource_identifier)
|
|
||||||
|
|
||||||
resource_object = resource_object_for(serializer, include_slice)
|
|
||||||
if primary
|
|
||||||
@primary << resource_object
|
|
||||||
else
|
|
||||||
@included << resource_object
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_relationships(serializer, include_slice)
|
|
||||||
serializer.associations(include_slice).each do |association|
|
|
||||||
# TODO(BF): Process relationship without evaluating lazy_association
|
|
||||||
process_relationship(association.lazy_association.serializer, include_slice[association.key])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_relationship(serializer, include_slice)
|
|
||||||
if serializer.respond_to?(:each)
|
|
||||||
serializer.each { |s| process_relationship(s, include_slice) }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return unless serializer && serializer.object
|
|
||||||
return unless process_resource(serializer, false, include_slice)
|
|
||||||
|
|
||||||
process_relationships(serializer, include_slice)
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
|
||||||
# attributes
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# patternProperties:
|
|
||||||
# ^(?!relationships$|links$)\\w[-\\w_]*$
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# Members of the attributes object ("attributes") represent information about the resource
|
|
||||||
# object in which it's defined.
|
|
||||||
# Attributes may contain any valid JSON value
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# foo: 'bar'
|
|
||||||
# }
|
|
||||||
def attributes_for(serializer, fields)
|
|
||||||
serializer.attributes(fields).except(:id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
|
|
||||||
def resource_object_for(serializer, include_slice = {})
|
|
||||||
resource_object = data_for(serializer, include_slice)
|
|
||||||
|
|
||||||
# toplevel_links
|
|
||||||
# definition:
|
|
||||||
# allOf
|
|
||||||
# ☐ links
|
|
||||||
# ☐ pagination
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# Link members related to the primary data.
|
|
||||||
# structure:
|
|
||||||
# links.merge!(pagination)
|
|
||||||
# prs:
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1247
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1018
|
|
||||||
if (links = links_for(serializer)).any?
|
|
||||||
resource_object ||= {}
|
|
||||||
resource_object[:links] = links
|
|
||||||
end
|
|
||||||
|
|
||||||
# toplevel_meta
|
|
||||||
# alias meta
|
|
||||||
# definition:
|
|
||||||
# meta
|
|
||||||
# structure
|
|
||||||
# {
|
|
||||||
# :'git-ref' => 'abc123'
|
|
||||||
# }
|
|
||||||
if (meta = meta_for(serializer)).present?
|
|
||||||
resource_object ||= {}
|
|
||||||
resource_object[:meta] = meta
|
|
||||||
end
|
|
||||||
|
|
||||||
resource_object
|
|
||||||
end
|
|
||||||
|
|
||||||
def data_for(serializer, include_slice)
|
|
||||||
data = serializer.fetch(self) do
|
|
||||||
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
||||||
break nil if resource_object.nil?
|
|
||||||
|
|
||||||
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
|
||||||
attributes = attributes_for(serializer, requested_fields)
|
|
||||||
resource_object[:attributes] = attributes if attributes.any?
|
|
||||||
resource_object
|
|
||||||
end
|
|
||||||
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]) || '*'
|
|
||||||
relationships = relationships_for(serializer, requested_associations, include_slice)
|
|
||||||
resource_object[:relationships] = relationships if relationships.any?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
|
|
||||||
# relationships
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# patternProperties:
|
|
||||||
# ^\\w[-\\w_]*$"
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# data : relationshipsData
|
|
||||||
# links
|
|
||||||
# meta
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
#
|
|
||||||
# Members of the relationships object ("relationships") represent references from the
|
|
||||||
# resource object in which it's defined to other resource objects."
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# links: links,
|
|
||||||
# meta: meta,
|
|
||||||
# data: relationshipsData
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
#
|
|
||||||
# prs:
|
|
||||||
# links
|
|
||||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
|
||||||
# meta
|
|
||||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
|
||||||
# polymorphic
|
|
||||||
# [ ] https://github.com/rails-api/active_model_serializers/pull/1420
|
|
||||||
#
|
|
||||||
# relationshipsData
|
|
||||||
# definition:
|
|
||||||
# oneOf
|
|
||||||
# relationshipToOne
|
|
||||||
# relationshipToMany
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# Member, whose value represents "resource linkage"
|
|
||||||
# structure:
|
|
||||||
# if has_one?
|
|
||||||
# relationshipToOne
|
|
||||||
# else
|
|
||||||
# relationshipToMany
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# definition:
|
|
||||||
# anyOf
|
|
||||||
# null
|
|
||||||
# linkage
|
|
||||||
#
|
|
||||||
# relationshipToOne
|
|
||||||
# description:
|
|
||||||
#
|
|
||||||
# References to other resource objects in a to-one ("relationship"). Relationships can be
|
|
||||||
# specified by including a member in a resource's links object.
|
|
||||||
#
|
|
||||||
# None: Describes an empty to-one relationship.
|
|
||||||
# structure:
|
|
||||||
# if has_related?
|
|
||||||
# linkage
|
|
||||||
# else
|
|
||||||
# nil
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# relationshipToMany
|
|
||||||
# definition:
|
|
||||||
# array of unique items of type 'linkage'
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# An array of objects each containing "type" and "id" members for to-many relationships
|
|
||||||
# structure:
|
|
||||||
# [
|
|
||||||
# linkage,
|
|
||||||
# linkage
|
|
||||||
# ]
|
|
||||||
# prs:
|
|
||||||
# polymorphic
|
|
||||||
# [ ] https://github.com/rails-api/active_model_serializers/pull/1282
|
|
||||||
#
|
|
||||||
# linkage
|
|
||||||
# definition:
|
|
||||||
# type (required) : String
|
|
||||||
# id (required) : String
|
|
||||||
# meta
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# The "type" and "id" to non-empty members.
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# type: 'required-type',
|
|
||||||
# id: 'required-id',
|
|
||||||
# meta: meta
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
def relationships_for(serializer, requested_associations, include_slice)
|
|
||||||
include_directive = JSONAPI::IncludeDirective.new(
|
|
||||||
requested_associations,
|
|
||||||
allow_wildcard: true
|
|
||||||
)
|
|
||||||
serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
|
|
||||||
hash[association.key] = Relationship.new(serializer, instance_options, association).as_json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-links Document Links}
|
|
||||||
# links
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# self : URI
|
|
||||||
# related : link
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# A resource object **MAY** contain references to other resource objects ("relationships").
|
|
||||||
# Relationships may be to-one or to-many. Relationships can be specified by including a member
|
|
||||||
# in a resource's links object.
|
|
||||||
#
|
|
||||||
# A `self` member’s value is a URL for the relationship itself (a "relationship URL"). This
|
|
||||||
# URL allows the client to directly manipulate the relationship. For example, it would allow
|
|
||||||
# a client to remove an `author` from an `article` without deleting the people resource
|
|
||||||
# itself.
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# self: 'http://example.com/etc',
|
|
||||||
# related: link
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
def links_for(serializer)
|
|
||||||
serializer._links.each_with_object({}) do |(name, value), hash|
|
|
||||||
result = Link.new(serializer, value).as_json
|
|
||||||
hash[name] = result if result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#fetching-pagination Pagination Links}
|
|
||||||
# pagination
|
|
||||||
# definition:
|
|
||||||
# first : pageObject
|
|
||||||
# last : pageObject
|
|
||||||
# prev : pageObject
|
|
||||||
# next : pageObject
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# first: pageObject,
|
|
||||||
# last: pageObject,
|
|
||||||
# prev: pageObject,
|
|
||||||
# next: pageObject
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# pageObject
|
|
||||||
# definition:
|
|
||||||
# oneOf
|
|
||||||
# URI
|
|
||||||
# null
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# The <x> page of data
|
|
||||||
# structure:
|
|
||||||
# if has_page?
|
|
||||||
# 'http://example.com/some-page?page[number][x]'
|
|
||||||
# else
|
|
||||||
# nil
|
|
||||||
# end
|
|
||||||
# prs:
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1041
|
|
||||||
def pagination_links_for(serializer)
|
|
||||||
PaginationLinks.new(serializer.object, instance_options).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-meta Docment Meta}
|
|
||||||
def meta_for(serializer)
|
|
||||||
Meta.new(serializer).as_json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop:enable Style/AsciiComments
|
|
||||||
@ -1,213 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi
|
|
||||||
# NOTE(Experimental):
|
|
||||||
# This is an experimental feature. Both the interface and internals could be subject
|
|
||||||
# to changes.
|
|
||||||
module Deserialization
|
|
||||||
InvalidDocument = Class.new(ArgumentError)
|
|
||||||
|
|
||||||
module_function
|
|
||||||
|
|
||||||
# Transform a JSON API document, containing a single data object,
|
|
||||||
# into a hash that is ready for ActiveRecord::Base.new() and such.
|
|
||||||
# Raises InvalidDocument if the payload is not properly formatted.
|
|
||||||
#
|
|
||||||
# @param [Hash|ActionController::Parameters] document
|
|
||||||
# @param [Hash] options
|
|
||||||
# only: Array of symbols of whitelisted fields.
|
|
||||||
# except: Array of symbols of blacklisted fields.
|
|
||||||
# keys: Hash of translated keys (e.g. :author => :user).
|
|
||||||
# polymorphic: Array of symbols of polymorphic fields.
|
|
||||||
# @return [Hash]
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# document = {
|
|
||||||
# data: {
|
|
||||||
# id: 1,
|
|
||||||
# type: 'post',
|
|
||||||
# attributes: {
|
|
||||||
# title: 'Title 1',
|
|
||||||
# date: '2015-12-20'
|
|
||||||
# },
|
|
||||||
# associations: {
|
|
||||||
# author: {
|
|
||||||
# data: {
|
|
||||||
# type: 'user',
|
|
||||||
# id: 2
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# second_author: {
|
|
||||||
# data: nil
|
|
||||||
# },
|
|
||||||
# comments: {
|
|
||||||
# data: [{
|
|
||||||
# type: 'comment',
|
|
||||||
# id: 3
|
|
||||||
# },{
|
|
||||||
# type: 'comment',
|
|
||||||
# id: 4
|
|
||||||
# }]
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# parse(document) #=>
|
|
||||||
# # {
|
|
||||||
# # title: 'Title 1',
|
|
||||||
# # date: '2015-12-20',
|
|
||||||
# # author_id: 2,
|
|
||||||
# # second_author_id: nil
|
|
||||||
# # comment_ids: [3, 4]
|
|
||||||
# # }
|
|
||||||
#
|
|
||||||
# parse(document, only: [:title, :date, :author],
|
|
||||||
# keys: { date: :published_at },
|
|
||||||
# polymorphic: [:author]) #=>
|
|
||||||
# # {
|
|
||||||
# # title: 'Title 1',
|
|
||||||
# # published_at: '2015-12-20',
|
|
||||||
# # author_id: '2',
|
|
||||||
# # author_type: 'people'
|
|
||||||
# # }
|
|
||||||
#
|
|
||||||
def parse!(document, options = {})
|
|
||||||
parse(document, options) do |invalid_payload, reason|
|
|
||||||
fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Same as parse!, but returns an empty hash instead of raising InvalidDocument
|
|
||||||
# on invalid payloads.
|
|
||||||
def parse(document, options = {})
|
|
||||||
document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters)
|
|
||||||
|
|
||||||
validate_payload(document) do |invalid_document, reason|
|
|
||||||
yield invalid_document, reason if block_given?
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
primary_data = document['data']
|
|
||||||
attributes = primary_data['attributes'] || {}
|
|
||||||
attributes['id'] = primary_data['id'] if primary_data['id']
|
|
||||||
relationships = primary_data['relationships'] || {}
|
|
||||||
|
|
||||||
filter_fields(attributes, options)
|
|
||||||
filter_fields(relationships, options)
|
|
||||||
|
|
||||||
hash = {}
|
|
||||||
hash.merge!(parse_attributes(attributes, options))
|
|
||||||
hash.merge!(parse_relationships(relationships, options))
|
|
||||||
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks whether a payload is compliant with the JSON API spec.
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
# rubocop:disable Metrics/CyclomaticComplexity
|
|
||||||
def validate_payload(payload)
|
|
||||||
unless payload.is_a?(Hash)
|
|
||||||
yield payload, 'Expected hash'
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
primary_data = payload['data']
|
|
||||||
unless primary_data.is_a?(Hash)
|
|
||||||
yield payload, { data: 'Expected hash' }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
attributes = primary_data['attributes'] || {}
|
|
||||||
unless attributes.is_a?(Hash)
|
|
||||||
yield payload, { data: { attributes: 'Expected hash or nil' } }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
relationships = primary_data['relationships'] || {}
|
|
||||||
unless relationships.is_a?(Hash)
|
|
||||||
yield payload, { data: { relationships: 'Expected hash or nil' } }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
relationships.each do |(key, value)|
|
|
||||||
unless value.is_a?(Hash) && value.key?('data')
|
|
||||||
yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/CyclomaticComplexity
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def filter_fields(fields, options)
|
|
||||||
if (only = options[:only])
|
|
||||||
fields.slice!(*Array(only).map(&:to_s))
|
|
||||||
elsif (except = options[:except])
|
|
||||||
fields.except!(*Array(except).map(&:to_s))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def field_key(field, options)
|
|
||||||
(options[:keys] || {}).fetch(field.to_sym, field).to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def parse_attributes(attributes, options)
|
|
||||||
transform_keys(attributes, options)
|
|
||||||
.map { |(k, v)| { field_key(k, options) => v } }
|
|
||||||
.reduce({}, :merge)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Given an association name, and a relationship data attribute, build a hash
|
|
||||||
# mapping the corresponding ActiveRecord attribute to the corresponding value.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
|
|
||||||
# { 'id' => '2', 'type' => 'comments' }],
|
|
||||||
# {})
|
|
||||||
# # => { :comment_ids => ['1', '2'] }
|
|
||||||
# parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
|
|
||||||
# # => { :author_id => '1' }
|
|
||||||
# parse_relationship(:author, nil, {})
|
|
||||||
# # => { :author_id => nil }
|
|
||||||
# @param [Symbol] assoc_name
|
|
||||||
# @param [Hash] assoc_data
|
|
||||||
# @param [Hash] options
|
|
||||||
# @return [Hash{Symbol, Object}]
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
def parse_relationship(assoc_name, assoc_data, options)
|
|
||||||
prefix_key = field_key(assoc_name, options).to_s.singularize
|
|
||||||
hash =
|
|
||||||
if assoc_data.is_a?(Array)
|
|
||||||
{ "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } }
|
|
||||||
else
|
|
||||||
{ "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
|
|
||||||
end
|
|
||||||
|
|
||||||
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
|
|
||||||
if polymorphic
|
|
||||||
hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'] : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def parse_relationships(relationships, options)
|
|
||||||
transform_keys(relationships, options)
|
|
||||||
.map { |(k, v)| parse_relationship(k, v['data'], options) }
|
|
||||||
.reduce({}, :merge)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @api private
|
|
||||||
def transform_keys(hash, options)
|
|
||||||
transform = options[:key_transform] || :underscore
|
|
||||||
CaseTransform.send(transform, hash)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi < Base
|
|
||||||
module Error
|
|
||||||
# rubocop:disable Style/AsciiComments
|
|
||||||
UnknownSourceTypeError = Class.new(ArgumentError)
|
|
||||||
|
|
||||||
# Builds a JSON API Errors Object
|
|
||||||
# {http://jsonapi.org/format/#errors JSON API Errors}
|
|
||||||
#
|
|
||||||
# @param [ActiveModel::Serializer::ErrorSerializer] error_serializer
|
|
||||||
# @return [Array<Symbol, Array<String>>] i.e. attribute_name, [attribute_errors]
|
|
||||||
def self.resource_errors(error_serializer, options)
|
|
||||||
error_serializer.as_json.flat_map do |attribute_name, attribute_errors|
|
|
||||||
attribute_name = JsonApi.send(:transform_key_casing!, attribute_name,
|
|
||||||
options)
|
|
||||||
attribute_error_objects(attribute_name, attribute_errors)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# ☐ id : String
|
|
||||||
# ☐ status : String
|
|
||||||
# ☐ code : String
|
|
||||||
# ☐ title : String
|
|
||||||
# ☑ detail : String
|
|
||||||
# ☐ links
|
|
||||||
# ☐ meta
|
|
||||||
# ☑ error_source
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# id : A unique identifier for this particular occurrence of the problem.
|
|
||||||
# status : The HTTP status code applicable to this problem, expressed as a string value
|
|
||||||
# code : An application-specific error code, expressed as a string value.
|
|
||||||
# title : A short, human-readable summary of the problem. It **SHOULD NOT** change from
|
|
||||||
# occurrence to occurrence of the problem, except for purposes of localization.
|
|
||||||
# detail : A human-readable explanation specific to this occurrence of the problem.
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# title: 'SystemFailure',
|
|
||||||
# detail: 'something went terribly wrong',
|
|
||||||
# status: '500'
|
|
||||||
# }.merge!(errorSource)
|
|
||||||
def self.attribute_error_objects(attribute_name, attribute_errors)
|
|
||||||
attribute_errors.map do |attribute_error|
|
|
||||||
{
|
|
||||||
source: error_source(:pointer, attribute_name),
|
|
||||||
detail: attribute_error
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# errorSource
|
|
||||||
# description:
|
|
||||||
# oneOf
|
|
||||||
# ☑ pointer : String
|
|
||||||
# ☑ parameter : String
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# pointer: A JSON Pointer RFC6901 to the associated entity in the request document e.g. "/data"
|
|
||||||
# for a primary data object, or "/data/attributes/title" for a specific attribute.
|
|
||||||
# https://tools.ietf.org/html/rfc6901
|
|
||||||
#
|
|
||||||
# parameter: A string indicating which query parameter caused the error
|
|
||||||
# structure:
|
|
||||||
# if is_attribute?
|
|
||||||
# {
|
|
||||||
# pointer: '/data/attributes/red-button'
|
|
||||||
# }
|
|
||||||
# else
|
|
||||||
# {
|
|
||||||
# parameter: 'pres'
|
|
||||||
# }
|
|
||||||
# end
|
|
||||||
def self.error_source(source_type, attribute_name)
|
|
||||||
case source_type
|
|
||||||
when :pointer
|
|
||||||
{
|
|
||||||
pointer: ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name)
|
|
||||||
}
|
|
||||||
when :parameter
|
|
||||||
{
|
|
||||||
parameter: attribute_name
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fail UnknownSourceTypeError, "Unknown source type '#{source_type}' for attribute_name '#{attribute_name}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop:enable Style/AsciiComments
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi < Base
|
|
||||||
# {http://jsonapi.org/format/#document-jsonapi-object Jsonapi Object}
|
|
||||||
|
|
||||||
# toplevel_jsonapi
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# version : String
|
|
||||||
# meta
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# An object describing the server's implementation
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# version: ActiveModelSerializers.config.jsonapi_version,
|
|
||||||
# meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
|
|
||||||
# }.reject! { |_, v| v.blank? }
|
|
||||||
# prs:
|
|
||||||
# https://github.com/rails-api/active_model_serializers/pull/1050
|
|
||||||
module Jsonapi
|
|
||||||
module_function
|
|
||||||
|
|
||||||
def add!(hash)
|
|
||||||
hash.merge!(object) if include_object?
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_object?
|
|
||||||
ActiveModelSerializers.config.jsonapi_include_toplevel_object
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: see if we can cache this
|
|
||||||
def object
|
|
||||||
object = {
|
|
||||||
jsonapi: {
|
|
||||||
version: ActiveModelSerializers.config.jsonapi_version,
|
|
||||||
meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
object[:jsonapi].reject! { |_, v| v.blank? }
|
|
||||||
|
|
||||||
object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi
|
|
||||||
# link
|
|
||||||
# definition:
|
|
||||||
# oneOf
|
|
||||||
# linkString
|
|
||||||
# linkObject
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# A link **MUST** be represented as either: a string containing the link's URL or a link
|
|
||||||
# object."
|
|
||||||
# structure:
|
|
||||||
# if href?
|
|
||||||
# linkString
|
|
||||||
# else
|
|
||||||
# linkObject
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# linkString
|
|
||||||
# definition:
|
|
||||||
# URI
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# A string containing the link's URL.
|
|
||||||
# structure:
|
|
||||||
# 'http://example.com/link-string'
|
|
||||||
#
|
|
||||||
# linkObject
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# properties:
|
|
||||||
# href (required) : URI
|
|
||||||
# meta
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# href: 'http://example.com/link-object',
|
|
||||||
# meta: meta,
|
|
||||||
# }.reject! {|_,v| v.nil? }
|
|
||||||
class Link
|
|
||||||
include SerializationContext::UrlHelpers
|
|
||||||
|
|
||||||
def initialize(serializer, value)
|
|
||||||
@_routes ||= nil # handles warning
|
|
||||||
# actionpack-4.0.13/lib/action_dispatch/routing/route_set.rb:417: warning: instance variable @_routes not initialized
|
|
||||||
@object = serializer.object
|
|
||||||
@scope = serializer.scope
|
|
||||||
# Use the return value of the block unless it is nil.
|
|
||||||
if value.respond_to?(:call)
|
|
||||||
@value = instance_eval(&value)
|
|
||||||
else
|
|
||||||
@value = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def href(value)
|
|
||||||
@href = value
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def meta(value)
|
|
||||||
@meta = value
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json
|
|
||||||
return @value if @value
|
|
||||||
|
|
||||||
hash = {}
|
|
||||||
hash[:href] = @href if defined?(@href)
|
|
||||||
hash[:meta] = @meta if defined?(@meta)
|
|
||||||
|
|
||||||
hash.any? ? hash : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :object, :scope
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi
|
|
||||||
# meta
|
|
||||||
# definition:
|
|
||||||
# JSON Object
|
|
||||||
#
|
|
||||||
# description:
|
|
||||||
# Non-standard meta-information that can not be represented as an attribute or relationship.
|
|
||||||
# structure:
|
|
||||||
# {
|
|
||||||
# attitude: 'adjustable'
|
|
||||||
# }
|
|
||||||
class Meta
|
|
||||||
def initialize(serializer)
|
|
||||||
@object = serializer.object
|
|
||||||
@scope = serializer.scope
|
|
||||||
|
|
||||||
# Use the return value of the block unless it is nil.
|
|
||||||
if serializer._meta.respond_to?(:call)
|
|
||||||
@value = instance_eval(&serializer._meta)
|
|
||||||
else
|
|
||||||
@value = serializer._meta
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json
|
|
||||||
@value
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :object, :scope
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi < Base
|
|
||||||
class PaginationLinks
|
|
||||||
MissingSerializationContextError = Class.new(KeyError)
|
|
||||||
FIRST_PAGE = 1
|
|
||||||
|
|
||||||
attr_reader :collection, :context
|
|
||||||
|
|
||||||
def initialize(collection, adapter_options)
|
|
||||||
@collection = collection
|
|
||||||
@adapter_options = adapter_options
|
|
||||||
@context = adapter_options.fetch(:serialization_context) do
|
|
||||||
fail MissingSerializationContextError, <<-EOF.freeze
|
|
||||||
JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext.
|
|
||||||
Please pass a ':serialization_context' option or
|
|
||||||
override CollectionSerializer#paginated? to return 'false'.
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :adapter_options
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def pages_from
|
|
||||||
return {} if collection.total_pages <= FIRST_PAGE
|
|
||||||
|
|
||||||
{}.tap do |pages|
|
|
||||||
pages[:self] = collection.current_page
|
|
||||||
|
|
||||||
unless collection.current_page == FIRST_PAGE
|
|
||||||
pages[:first] = FIRST_PAGE
|
|
||||||
pages[:prev] = collection.current_page - FIRST_PAGE
|
|
||||||
end
|
|
||||||
|
|
||||||
unless collection.current_page == collection.total_pages
|
|
||||||
pages[:next] = collection.current_page + FIRST_PAGE
|
|
||||||
pages[:last] = collection.total_pages
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def url(options)
|
|
||||||
@url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
|
|
||||||
end
|
|
||||||
|
|
||||||
def request_url
|
|
||||||
@request_url ||= context.request_url
|
|
||||||
end
|
|
||||||
|
|
||||||
def query_parameters
|
|
||||||
@query_parameters ||= context.query_parameters
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class JsonApi
|
|
||||||
class Relationship
|
|
||||||
# {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
|
|
||||||
# {http://jsonapi.org/format/#document-links Document Links}
|
|
||||||
# {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
|
|
||||||
# {http://jsonapi.org/format/#document-meta Document Meta}
|
|
||||||
def initialize(parent_serializer, serializable_resource_options, association)
|
|
||||||
@parent_serializer = parent_serializer
|
|
||||||
@association = association
|
|
||||||
@serializable_resource_options = serializable_resource_options
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json
|
|
||||||
hash = {}
|
|
||||||
|
|
||||||
hash[:data] = data_for(association) if association.include_data?
|
|
||||||
|
|
||||||
links = links_for(association)
|
|
||||||
hash[:links] = links if links.any?
|
|
||||||
|
|
||||||
meta = meta_for(association)
|
|
||||||
hash[:meta] = meta if meta
|
|
||||||
hash[:meta] = {} if hash.empty?
|
|
||||||
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :parent_serializer, :serializable_resource_options, :association
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self
|
|
||||||
def data_for(association)
|
|
||||||
if association.collection?
|
|
||||||
data_for_many(association)
|
|
||||||
else
|
|
||||||
data_for_one(association)
|
|
||||||
end
|
|
||||||
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
|
|
||||||
ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
|
|
||||||
else
|
|
||||||
# TODO(BF): Process relationship without evaluating lazy_association
|
|
||||||
serializer = association.lazy_association.serializer
|
|
||||||
if (virtual_value = association.virtual_value)
|
|
||||||
virtual_value
|
|
||||||
elsif serializer && association.object
|
|
||||||
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def data_for_many(association)
|
|
||||||
# TODO(BF): Process relationship without evaluating lazy_association
|
|
||||||
collection_serializer = association.lazy_association.serializer
|
|
||||||
if collection_serializer.respond_to?(:each)
|
|
||||||
collection_serializer.map do |serializer|
|
|
||||||
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
|
||||||
end
|
|
||||||
elsif (virtual_value = association.virtual_value)
|
|
||||||
virtual_value
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def links_for(association)
|
|
||||||
association.links.each_with_object({}) do |(key, value), hash|
|
|
||||||
result = Link.new(parent_serializer, value).as_json
|
|
||||||
hash[key] = result if result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def meta_for(association)
|
|
||||||
meta = association.meta
|
|
||||||
meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
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
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
|
|
||||||
def initialize(serializer, options)
|
|
||||||
@id = id_for(serializer)
|
|
||||||
@type = type_for(serializer, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json
|
|
||||||
return nil if id.blank?
|
|
||||||
{ id: id, type: type }
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :id, :type
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def type_for(serializer, transform_options)
|
|
||||||
self.class.type_for(serializer.object.class.name, serializer._type, transform_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def id_for(serializer)
|
|
||||||
serializer.read_attribute_for_serialization(:id).to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Adapter
|
|
||||||
class Null < Base
|
|
||||||
def serializable_hash(*)
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
# Adapted from
|
|
||||||
# https://github.com/rails/rails/blob/7f18ea14c8/activejob/lib/active_job/callbacks.rb
|
|
||||||
require 'active_support/callbacks'
|
|
||||||
|
|
||||||
module ActiveModelSerializers
|
|
||||||
# = ActiveModelSerializers Callbacks
|
|
||||||
#
|
|
||||||
# ActiveModelSerializers provides hooks during the life cycle of serialization and
|
|
||||||
# allow you to trigger logic. Available callbacks are:
|
|
||||||
#
|
|
||||||
# * <tt>around_render</tt>
|
|
||||||
#
|
|
||||||
module Callbacks
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
include ActiveSupport::Callbacks
|
|
||||||
|
|
||||||
included do
|
|
||||||
define_callbacks :render
|
|
||||||
end
|
|
||||||
|
|
||||||
# These methods will be included into any ActiveModelSerializers object, adding
|
|
||||||
# callbacks for +render+.
|
|
||||||
module ClassMethods
|
|
||||||
# Defines a callback that will get called around the render method,
|
|
||||||
# whether it is as_json, to_json, or serializable_hash
|
|
||||||
#
|
|
||||||
# class ActiveModelSerializers::SerializableResource
|
|
||||||
# include ActiveModelSerializers::Callbacks
|
|
||||||
#
|
|
||||||
# around_render do |args, block|
|
|
||||||
# tag_logger do
|
|
||||||
# notify_render do
|
|
||||||
# block.call(args)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def as_json
|
|
||||||
# run_callbacks :render do
|
|
||||||
# adapter.as_json
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# # Note: So that we can re-use the instrumenter for as_json, to_json, and
|
|
||||||
# # serializable_hash, we aren't using the usual format, which would be:
|
|
||||||
# # def render(args)
|
|
||||||
# # adapter.as_json
|
|
||||||
# # end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
def around_render(*filters, &blk)
|
|
||||||
set_callback(:render, :around, *filters, &blk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
##
|
|
||||||
# Provides a single method +deprecate+ to be used to declare when
|
|
||||||
# something is going away.
|
|
||||||
#
|
|
||||||
# class Legacy
|
|
||||||
# def self.klass_method
|
|
||||||
# # ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def instance_method
|
|
||||||
# # ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# extend ActiveModelSerializers::Deprecate
|
|
||||||
# deprecate :instance_method, "ActiveModelSerializers::NewPlace#new_method"
|
|
||||||
#
|
|
||||||
# class << self
|
|
||||||
# extend ActiveModelSerializers::Deprecate
|
|
||||||
# deprecate :klass_method, :none
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Adapted from https://github.com/rubygems/rubygems/blob/1591331/lib/rubygems/deprecate.rb
|
|
||||||
module ActiveModelSerializers
|
|
||||||
module Deprecate
|
|
||||||
##
|
|
||||||
# Simple deprecation method that deprecates +name+ by wrapping it up
|
|
||||||
# in a dummy method. It warns on each call to the dummy method
|
|
||||||
# telling the user of +replacement+ (unless +replacement+ is :none) that it is planned to go away.
|
|
||||||
|
|
||||||
def deprecate(name, replacement)
|
|
||||||
old = "_deprecated_#{name}"
|
|
||||||
alias_method old, name
|
|
||||||
class_eval do
|
|
||||||
define_method(name) do |*args, &block|
|
|
||||||
target = is_a?(Module) ? "#{self}." : "#{self.class}#"
|
|
||||||
msg = ["NOTE: #{target}#{name} is deprecated",
|
|
||||||
replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
|
|
||||||
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(':')}"]
|
|
||||||
warn "#{msg.join}."
|
|
||||||
send old, *args, &block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def delegate_and_deprecate(method, delegee)
|
|
||||||
delegate method, to: delegee
|
|
||||||
deprecate method, "#{delegee.name}."
|
|
||||||
end
|
|
||||||
|
|
||||||
module_function :deprecate
|
|
||||||
module_function :delegate_and_deprecate
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Deserialization
|
|
||||||
module_function
|
|
||||||
|
|
||||||
def jsonapi_parse(*args)
|
|
||||||
Adapter::JsonApi::Deserialization.parse(*args)
|
|
||||||
end
|
|
||||||
|
|
||||||
# :nocov:
|
|
||||||
def jsonapi_parse!(*args)
|
|
||||||
Adapter::JsonApi::Deserialization.parse!(*args)
|
|
||||||
end
|
|
||||||
# :nocov:
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module JsonPointer
|
|
||||||
module_function
|
|
||||||
|
|
||||||
POINTERS = {
|
|
||||||
attribute: '/data/attributes/%s'.freeze,
|
|
||||||
primary_data: '/data%s'.freeze
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
def new(pointer_type, value = nil)
|
|
||||||
format(POINTERS[pointer_type], value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
##
|
|
||||||
# ActiveModelSerializers::Logging
|
|
||||||
#
|
|
||||||
# https://github.com/rails/rails/blob/280654ef88/activejob/lib/active_job/logging.rb
|
|
||||||
#
|
|
||||||
module ActiveModelSerializers
|
|
||||||
module Logging
|
|
||||||
RENDER_EVENT = 'render.active_model_serializers'.freeze
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
included do
|
|
||||||
include ActiveModelSerializers::Callbacks
|
|
||||||
extend Macros
|
|
||||||
instrument_rendering
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
def instrument_rendering
|
|
||||||
around_render do |args, block|
|
|
||||||
tag_logger do
|
|
||||||
notify_render do
|
|
||||||
block.call(args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Macros that can be used to customize the logging of class or instance methods,
|
|
||||||
# by extending the class or its singleton.
|
|
||||||
#
|
|
||||||
# Adapted from:
|
|
||||||
# https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
|
|
||||||
#
|
|
||||||
# Provides a single method +notify+ to be used to declare when
|
|
||||||
# something a method notifies, with the argument +callback_name+ of the notification callback.
|
|
||||||
#
|
|
||||||
# class Adapter
|
|
||||||
# def self.klass_method
|
|
||||||
# # ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def instance_method
|
|
||||||
# # ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# include ActiveModelSerializers::Logging::Macros
|
|
||||||
# notify :instance_method, :render
|
|
||||||
#
|
|
||||||
# class << self
|
|
||||||
# extend ActiveModelSerializers::Logging::Macros
|
|
||||||
# notify :klass_method, :render
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
module Macros
|
|
||||||
##
|
|
||||||
# Simple notify method that wraps up +name+
|
|
||||||
# in a dummy method. It notifies on with the +callback_name+ notifier on
|
|
||||||
# each call to the dummy method, telling what the current serializer and adapter
|
|
||||||
# are being rendered.
|
|
||||||
# Adapted from:
|
|
||||||
# https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
|
|
||||||
def notify(name, callback_name)
|
|
||||||
class_eval do
|
|
||||||
old = "_notifying_#{callback_name}_#{name}"
|
|
||||||
alias_method old, name
|
|
||||||
define_method name do |*args, &block|
|
|
||||||
run_callbacks callback_name do
|
|
||||||
send old, *args, &block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def notify_render(*)
|
|
||||||
event_name = RENDER_EVENT
|
|
||||||
ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def notify_render_payload
|
|
||||||
{
|
|
||||||
serializer: serializer || ActiveModel::Serializer::Null,
|
|
||||||
adapter: adapter || ActiveModelSerializers::Adapter::Null
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def tag_logger(*tags)
|
|
||||||
if ActiveModelSerializers.logger.respond_to?(:tagged)
|
|
||||||
tags.unshift 'active_model_serializers'.freeze unless logger_tagged_by_active_model_serializers?
|
|
||||||
ActiveModelSerializers.logger.tagged(*tags) { yield }
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def logger_tagged_by_active_model_serializers?
|
|
||||||
ActiveModelSerializers.logger.formatter.current_tags.include?('active_model_serializers'.freeze)
|
|
||||||
end
|
|
||||||
|
|
||||||
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
||||||
def render(event)
|
|
||||||
info do
|
|
||||||
serializer = event.payload[:serializer]
|
|
||||||
adapter = event.payload[:adapter]
|
|
||||||
duration = event.duration.round(2)
|
|
||||||
"Rendered #{serializer.name} with #{adapter.class} (#{duration}ms)"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def logger
|
|
||||||
ActiveModelSerializers.logger
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ActiveModelSerializers::Logging::LogSubscriber.attach_to :active_model_serializers
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module LookupChain
|
|
||||||
# Standard appending of Serializer to the resource name.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# Author => AuthorSerializer
|
|
||||||
BY_RESOURCE = lambda do |resource_class, _serializer_class, _namespace|
|
|
||||||
serializer_from(resource_class)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Uses the namespace of the resource to find the serializer
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# British::Author => British::AuthorSerializer
|
|
||||||
BY_RESOURCE_NAMESPACE = lambda do |resource_class, _serializer_class, _namespace|
|
|
||||||
resource_namespace = namespace_for(resource_class)
|
|
||||||
serializer_name = serializer_from(resource_class)
|
|
||||||
|
|
||||||
"#{resource_namespace}::#{serializer_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Uses the controller namespace of the resource to find the serializer
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# Api::V3::AuthorsController => Api::V3::AuthorSerializer
|
|
||||||
BY_NAMESPACE = lambda do |resource_class, _serializer_class, namespace|
|
|
||||||
resource_name = resource_class_name(resource_class)
|
|
||||||
namespace ? "#{namespace}::#{resource_name}Serializer" : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Allows for serializers to be defined in parent serializers
|
|
||||||
# - useful if a relationship only needs a different set of attributes
|
|
||||||
# than if it were rendered independently.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# class BlogSerializer < ActiveModel::Serializer
|
|
||||||
# class AuthorSerialier < ActiveModel::Serializer
|
|
||||||
# ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# belongs_to :author
|
|
||||||
# ...
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# The belongs_to relationship would be rendered with
|
|
||||||
# BlogSerializer::AuthorSerialier
|
|
||||||
BY_PARENT_SERIALIZER = lambda do |resource_class, serializer_class, _namespace|
|
|
||||||
return if serializer_class == ActiveModel::Serializer
|
|
||||||
|
|
||||||
serializer_name = serializer_from(resource_class)
|
|
||||||
"#{serializer_class}::#{serializer_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
DEFAULT = [
|
|
||||||
BY_PARENT_SERIALIZER,
|
|
||||||
BY_NAMESPACE,
|
|
||||||
BY_RESOURCE_NAMESPACE,
|
|
||||||
BY_RESOURCE
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
module_function
|
|
||||||
|
|
||||||
def namespace_for(klass)
|
|
||||||
klass.name.deconstantize
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_class_name(klass)
|
|
||||||
klass.name.demodulize
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_from_resource_name(name)
|
|
||||||
"#{name}Serializer"
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_from(klass)
|
|
||||||
name = resource_class_name(klass)
|
|
||||||
serializer_from_resource_name(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,130 +0,0 @@
|
|||||||
# 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.
|
|
||||||
require 'active_support/core_ext/hash'
|
|
||||||
module ActiveModelSerializers
|
|
||||||
class Model
|
|
||||||
include ActiveModel::Serializers::JSON
|
|
||||||
include ActiveModel::Model
|
|
||||||
|
|
||||||
# Declare names of attributes to be included in +attributes+ hash.
|
|
||||||
# Is only available as a class-method since the ActiveModel::Serialization mixin in Rails
|
|
||||||
# uses an +attribute_names+ local variable, which may conflict if we were to add instance methods here.
|
|
||||||
#
|
|
||||||
# @overload attribute_names
|
|
||||||
# @return [Array<Symbol>]
|
|
||||||
class_attribute :attribute_names, instance_writer: false, instance_reader: false
|
|
||||||
# Initialize +attribute_names+ for all subclasses. The array is usually
|
|
||||||
# mutated in the +attributes+ method, but can be set directly, as well.
|
|
||||||
self.attribute_names = []
|
|
||||||
|
|
||||||
# Easily declare instance attributes with setters and getters for each.
|
|
||||||
#
|
|
||||||
# To initialize an instance, all attributes must have setters.
|
|
||||||
# However, the hash returned by +attributes+ instance method will ALWAYS
|
|
||||||
# be the value of the initial attributes, regardless of what accessors are defined.
|
|
||||||
# The only way to change the change the attributes after initialization is
|
|
||||||
# to mutate the +attributes+ directly.
|
|
||||||
# Accessor methods do NOT mutate the attributes. (This is a bug).
|
|
||||||
#
|
|
||||||
# @note For now, the Model only supports the notion of 'attributes'.
|
|
||||||
# In the tests, there is a special Model that also supports 'associations'. This is
|
|
||||||
# important so that we can add accessors for values that should not appear in the
|
|
||||||
# attributes hash when modeling associations. It is not yet clear if it
|
|
||||||
# makes sense for a PORO to have associations outside of the tests.
|
|
||||||
#
|
|
||||||
# @overload attributes(names)
|
|
||||||
# @param names [Array<String, Symbol>]
|
|
||||||
# @param name [String, Symbol]
|
|
||||||
def self.attributes(*names)
|
|
||||||
self.attribute_names |= names.map(&:to_sym)
|
|
||||||
# Silence redefinition of methods warnings
|
|
||||||
ActiveModelSerializers.silence_warnings do
|
|
||||||
attr_accessor(*names)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Opt-in to breaking change
|
|
||||||
def self.derive_attributes_from_names_and_fix_accessors
|
|
||||||
unless included_modules.include?(DeriveAttributesFromNamesAndFixAccessors)
|
|
||||||
prepend(DeriveAttributesFromNamesAndFixAccessors)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module DeriveAttributesFromNamesAndFixAccessors
|
|
||||||
def self.included(base)
|
|
||||||
# NOTE that +id+ will always be in +attributes+.
|
|
||||||
base.attributes :id
|
|
||||||
end
|
|
||||||
|
|
||||||
# Override the +attributes+ method so that the hash is derived from +attribute_names+.
|
|
||||||
#
|
|
||||||
# The fields in +attribute_names+ determines the returned hash.
|
|
||||||
# +attributes+ are returned frozen to prevent any expectations that mutation affects
|
|
||||||
# the actual values in the model.
|
|
||||||
def attributes
|
|
||||||
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
|
|
||||||
result[attribute_name] = public_send(attribute_name).freeze
|
|
||||||
end.with_indifferent_access.freeze
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Support for validation and other ActiveModel::Errors
|
|
||||||
# @return [ActiveModel::Errors]
|
|
||||||
attr_reader :errors
|
|
||||||
|
|
||||||
# (see #updated_at)
|
|
||||||
attr_writer :updated_at
|
|
||||||
|
|
||||||
# The only way to change the attributes of an instance is to directly mutate the attributes.
|
|
||||||
# @example
|
|
||||||
#
|
|
||||||
# model.attributes[:foo] = :bar
|
|
||||||
# @return [Hash]
|
|
||||||
attr_reader :attributes
|
|
||||||
|
|
||||||
# @param attributes [Hash]
|
|
||||||
def initialize(attributes = {})
|
|
||||||
attributes ||= {} # protect against nil
|
|
||||||
@attributes = attributes.symbolize_keys.with_indifferent_access
|
|
||||||
@errors = ActiveModel::Errors.new(self)
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Defaults to the downcased model name.
|
|
||||||
# This probably isn't a good default, since it's not a unique instance identifier,
|
|
||||||
# but that's what is currently implemented \_('-')_/.
|
|
||||||
#
|
|
||||||
# @note Though +id+ is defined, it will only show up
|
|
||||||
# in +attributes+ when it is passed in to the initializer or added to +attributes+,
|
|
||||||
# such as <tt>attributes[:id] = 5</tt>.
|
|
||||||
# @return [String, Numeric, Symbol]
|
|
||||||
def id
|
|
||||||
attributes.fetch(:id) do
|
|
||||||
defined?(@id) ? @id : self.class.model_name.name && self.class.model_name.name.downcase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# When not set, defaults to the time the file was modified.
|
|
||||||
#
|
|
||||||
# @note Though +updated_at+ and +updated_at=+ are defined, it will only show up
|
|
||||||
# in +attributes+ when it is passed in to the initializer or added to +attributes+,
|
|
||||||
# such as <tt>attributes[:updated_at] = Time.current</tt>.
|
|
||||||
# @return [String, Numeric, Time]
|
|
||||||
def updated_at
|
|
||||||
attributes.fetch(:updated_at) do
|
|
||||||
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# To customize model behavior, this method must be redefined. However,
|
|
||||||
# there are other ways of setting the +cache_key+ a serializer uses.
|
|
||||||
# @return [String]
|
|
||||||
def cache_key
|
|
||||||
ActiveSupport::Cache.expand_cache_key([
|
|
||||||
self.class.model_name.name.downcase,
|
|
||||||
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
|
|
||||||
].compact)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
require 'rails/railtie'
|
|
||||||
require 'action_controller'
|
|
||||||
require 'action_controller/railtie'
|
|
||||||
require 'action_controller/serialization'
|
|
||||||
|
|
||||||
module ActiveModelSerializers
|
|
||||||
class Railtie < Rails::Railtie
|
|
||||||
config.to_prepare do
|
|
||||||
ActiveModel::Serializer.serializers_cache.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
initializer 'active_model_serializers.action_controller' do
|
|
||||||
ActiveSupport.on_load(:action_controller) do
|
|
||||||
include(::ActionController::Serialization)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
initializer 'active_model_serializers.prepare_serialization_context' do
|
|
||||||
SerializationContext.url_helpers = Rails.application.routes.url_helpers
|
|
||||||
SerializationContext.default_url_options = Rails.application.routes.default_url_options
|
|
||||||
end
|
|
||||||
|
|
||||||
# This hook is run after the action_controller railtie has set the configuration
|
|
||||||
# based on the *environment* configuration and before any config/initializers are run
|
|
||||||
# and also before eager_loading (if enabled).
|
|
||||||
initializer 'active_model_serializers.set_configs', after: 'action_controller.set_configs' do
|
|
||||||
ActiveModelSerializers.logger = Rails.configuration.action_controller.logger
|
|
||||||
ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching
|
|
||||||
# We want this hook to run after the config has been set, even if ActionController has already loaded.
|
|
||||||
ActiveSupport.on_load(:action_controller) do
|
|
||||||
ActiveModelSerializers.config.cache_store = ActionController::Base.cache_store
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# :nocov:
|
|
||||||
generators do |app|
|
|
||||||
Rails::Generators.configure!(app.config.generators)
|
|
||||||
Rails::Generators.hidden_namespaces.uniq!
|
|
||||||
require 'generators/rails/resource_override'
|
|
||||||
end
|
|
||||||
# :nocov:
|
|
||||||
|
|
||||||
if Rails.env.test?
|
|
||||||
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema)
|
|
||||||
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
# Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238,
|
|
||||||
# the JSON API media type will have its own format/renderer.
|
|
||||||
#
|
|
||||||
# > We recommend the media type be registered on its own as jsonapi
|
|
||||||
# when a jsonapi Renderer and deserializer (Http::Parameters::DEFAULT_PARSERS) are added.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
#
|
|
||||||
# ActiveSupport.on_load(:action_controller) do
|
|
||||||
# require 'active_model_serializers/register_jsonapi_renderer'
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# And then in controllers, use `render jsonapi: model` rather than `render json: model, adapter: :json_api`.
|
|
||||||
#
|
|
||||||
# For example, in a controller action, we can:
|
|
||||||
# respond_to do |format|
|
|
||||||
# format.jsonapi { render jsonapi: model }
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# or
|
|
||||||
#
|
|
||||||
# render jsonapi: model
|
|
||||||
#
|
|
||||||
# No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`)
|
|
||||||
module ActiveModelSerializers
|
|
||||||
module Jsonapi
|
|
||||||
MEDIA_TYPE = 'application/vnd.api+json'.freeze
|
|
||||||
HEADERS = {
|
|
||||||
response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE },
|
|
||||||
request: { 'ACCEPT'.freeze => MEDIA_TYPE }
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
def self.install
|
|
||||||
# actionpack/lib/action_dispatch/http/mime_types.rb
|
|
||||||
Mime::Type.register MEDIA_TYPE, :jsonapi
|
|
||||||
|
|
||||||
if Rails::VERSION::MAJOR >= 5
|
|
||||||
ActionDispatch::Request.parameter_parsers[:jsonapi] = parser
|
|
||||||
else
|
|
||||||
ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = parser
|
|
||||||
end
|
|
||||||
|
|
||||||
# ref https://github.com/rails/rails/pull/21496
|
|
||||||
ActionController::Renderers.add :jsonapi do |json, options|
|
|
||||||
json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
|
|
||||||
self.content_type ||= Mime[:jsonapi]
|
|
||||||
self.response_body = json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Proposal: should actually deserialize the JSON API params
|
|
||||||
# to the hash format expected by `ActiveModel::Serializers::JSON`
|
|
||||||
# actionpack/lib/action_dispatch/http/parameters.rb
|
|
||||||
def self.parser
|
|
||||||
lambda do |body|
|
|
||||||
data = JSON.parse(body)
|
|
||||||
data = { _json: data } unless data.is_a?(Hash)
|
|
||||||
data.with_indifferent_access
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module ControllerSupport
|
|
||||||
def serialize_jsonapi(json, options)
|
|
||||||
options[:adapter] = :json_api
|
|
||||||
options.fetch(:serialization_context) do
|
|
||||||
options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request)
|
|
||||||
end
|
|
||||||
get_serializer(json, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ActiveModelSerializers::Jsonapi.install
|
|
||||||
|
|
||||||
ActiveSupport.on_load(:action_controller) do
|
|
||||||
include ActiveModelSerializers::Jsonapi::ControllerSupport
|
|
||||||
end
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
require 'set'
|
|
||||||
|
|
||||||
module ActiveModelSerializers
|
|
||||||
class SerializableResource
|
|
||||||
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform])
|
|
||||||
include ActiveModelSerializers::Logging
|
|
||||||
|
|
||||||
delegate :serializable_hash, :as_json, :to_json, to: :adapter
|
|
||||||
notify :serializable_hash, :render
|
|
||||||
notify :as_json, :render
|
|
||||||
notify :to_json, :render
|
|
||||||
|
|
||||||
# Primary interface to composing a resource with a serializer and adapter.
|
|
||||||
# @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] }
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialization_scope=(scope)
|
|
||||||
serializer_opts[:scope] = scope
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialization_scope
|
|
||||||
serializer_opts[:scope]
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialization_scope_name=(scope_name)
|
|
||||||
serializer_opts[:scope_name] = scope_name
|
|
||||||
end
|
|
||||||
|
|
||||||
# NOTE: if no adapter is available, returns the resource itself. (i.e. adapter is a no-op)
|
|
||||||
def adapter
|
|
||||||
@adapter ||= find_adapter
|
|
||||||
end
|
|
||||||
alias adapter_instance adapter
|
|
||||||
|
|
||||||
def find_adapter
|
|
||||||
return resource unless serializer?
|
|
||||||
adapter = catch :no_serializer do
|
|
||||||
ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
|
|
||||||
end
|
|
||||||
adapter || resource
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer_instance
|
|
||||||
@serializer_instance ||= serializer.new(resource, serializer_opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get serializer either explicitly :serializer or implicitly from resource
|
|
||||||
# Remove :serializer key from serializer_opts
|
|
||||||
# Remove :each_serializer if present and set as :serializer key
|
|
||||||
def serializer
|
|
||||||
@serializer ||=
|
|
||||||
begin
|
|
||||||
@serializer = serializer_opts.delete(:serializer)
|
|
||||||
@serializer ||= ActiveModel::Serializer.serializer_for(resource, serializer_opts)
|
|
||||||
|
|
||||||
if serializer_opts.key?(:each_serializer)
|
|
||||||
serializer_opts[:serializer] = serializer_opts.delete(:each_serializer)
|
|
||||||
end
|
|
||||||
@serializer
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias serializer_class serializer
|
|
||||||
|
|
||||||
# True when no explicit adapter given, or explicit appear is truthy (non-nil)
|
|
||||||
# False when explicit adapter is falsy (nil or false)
|
|
||||||
def use_adapter?
|
|
||||||
!(adapter_opts.key?(:adapter) && !adapter_opts[:adapter])
|
|
||||||
end
|
|
||||||
|
|
||||||
def serializer?
|
|
||||||
use_adapter? && !serializer.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_reader :resource, :adapter_opts, :serializer_opts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
require 'active_support/core_ext/array/extract_options'
|
|
||||||
module ActiveModelSerializers
|
|
||||||
class SerializationContext
|
|
||||||
class << self
|
|
||||||
attr_writer :url_helpers, :default_url_options
|
|
||||||
def url_helpers
|
|
||||||
@url_helpers ||= Module.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_url_options
|
|
||||||
@default_url_options ||= {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
module UrlHelpers
|
|
||||||
def self.included(base)
|
|
||||||
base.send(:include, SerializationContext.url_helpers)
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_url_options
|
|
||||||
SerializationContext.default_url_options
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :request_url, :query_parameters, :key_transform
|
|
||||||
|
|
||||||
def initialize(*args)
|
|
||||||
options = args.extract_options!
|
|
||||||
if args.size == 1
|
|
||||||
request = args.pop
|
|
||||||
options[:request_url] = request.original_url[/\A[^?]+/]
|
|
||||||
options[:query_parameters] = request.query_parameters
|
|
||||||
end
|
|
||||||
@request_url = options.delete(:request_url)
|
|
||||||
@query_parameters = options.delete(:query_parameters)
|
|
||||||
@url_helpers = options.delete(:url_helpers) || self.class.url_helpers
|
|
||||||
@default_url_options = options.delete(:default_url_options) || self.class.default_url_options
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module ActiveModelSerializers
|
|
||||||
module Test
|
|
||||||
extend ActiveSupport::Autoload
|
|
||||||
autoload :Serializer
|
|
||||||
autoload :Schema
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user