mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 13:56:53 +00:00
Merge pull request #2121 from bf4/new_master
Clear out master, move 0.10.x dev to the stable branch
This commit is contained in:
commit
e3e5a41715
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
|
||||
|
||||
rvm:
|
||||
- 2.1
|
||||
- 2.2.6
|
||||
- 2.3.3
|
||||
- ruby-head
|
||||
- jruby-9.1.5.0 # is precompiled per http://rubies.travis-ci.org/
|
||||
- jruby-head
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
before_install:
|
||||
- gem update --system
|
||||
- rvm @global do gem uninstall bundler -a -x
|
||||
- rvm @global do gem install bundler -v 1.13.7
|
||||
install: bundle install --path=vendor/bundle --retry=3 --jobs=3
|
||||
cache:
|
||||
directories:
|
||||
- vendor/bundle
|
||||
|
||||
script:
|
||||
- bundle exec rake ci
|
||||
after_success:
|
||||
- codeclimate-test-reporter
|
||||
env:
|
||||
global:
|
||||
- "JRUBY_OPTS='--dev -J-Xmx1024M --debug'"
|
||||
matrix:
|
||||
- "RAILS_VERSION=4.1"
|
||||
- "RAILS_VERSION=4.2"
|
||||
- "RAILS_VERSION=5.0"
|
||||
- "RAILS_VERSION=master"
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- rvm: 2.1
|
||||
env: RAILS_VERSION=master
|
||||
- rvm: jruby-9.1.5.0
|
||||
env: RAILS_VERSION=master
|
||||
- rvm: jruby-head
|
||||
env: RAILS_VERSION=master
|
||||
- rvm: 2.1
|
||||
env: RAILS_VERSION=5.0
|
||||
- rvm: jruby-9.1.5.0
|
||||
env: RAILS_VERSION=5.0
|
||||
- rvm: jruby-head
|
||||
env: RAILS_VERSION=5.0
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: jruby-head
|
||||
fast_finish: true
|
||||
- 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:
|
||||
|
||||
@ -10,641 +10,10 @@ Fixes:
|
||||
|
||||
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)
|
||||
- [#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)
|
||||
## [0.08.x](CHANGELOG-0-08.md)
|
||||
|
||||
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).
|
||||
|
||||
## 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)
|
||||
## [Prehistory](CHANGELOG-prehistory.md)
|
||||
|
||||
@ -4,13 +4,7 @@ Before opening an issue, try the following:
|
||||
|
||||
##### Consult the documentation
|
||||
|
||||
See if your issue can be resolved by information in the documentation.
|
||||
|
||||
- [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)
|
||||
See if your issue can be resolved by information in the [documentation](README.md).
|
||||
|
||||
##### 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,
|
||||
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)
|
||||
or
|
||||
[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.
|
||||
- Include tests.
|
||||
- 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.
|
||||
- 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.
|
||||
- Do not change the VERSION file.
|
||||
|
||||
@ -102,4 +98,3 @@ fi
|
||||
unset RAILS_VERSION
|
||||
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
|
||||
290
README.md
290
README.md
@ -1,77 +1,11 @@
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```
|
||||
gem 'active_model_serializers', '~> 0.10.0'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
```
|
||||
$ bundle
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
been released yet. Please see below for the documentation relevant to you.
|
||||
reading documentation for our `master`, which is not yet released.
|
||||
|
||||
- [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)
|
||||
- [](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
|
||||
- [Guides](docs)
|
||||
@ -99,205 +34,32 @@ been released yet. Please see below for the documentation relevant to you.
|
||||
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable)
|
||||
|
||||
|
||||
## Status of AMS
|
||||
|
||||
*Status*:
|
||||
|
||||
- ❗️ All existing PRs against master will need to be closed and re-opened against 0-10-stable, if so desired
|
||||
- ❗️ Master, for the moment, won't have any released version of AMS on it.
|
||||
|
||||
*Changes to 0.10.x maintenance*:
|
||||
|
||||
- The 0.10.x version has become a huge maintenance version. We had hoped to get it in shape for a 1.0 release, but it is clear that isn't going to happen. Almost none of the maintainers from 0.8, 0.9, or earlier 0.10 are still working on AMS. We'll continue to maintain 0.10.x on the 0-10-stable branch, but maintainers won't otherwise be actively developing on it
|
||||
- We may choose to make a 0.11.x ( 0-11-stable) release based on 0-10-stable that just removes the deprecations.
|
||||
|
||||
*What's happening to AMS*:
|
||||
|
||||
- There's been a lot of churn around AMS since it began back in [Rails 3.2](CHANGELOG-prehistory.md) and a lot of new libraries are around and the JSON:API spec has reached 1.0.
|
||||
- If there is to be a 1.0 release of AMS, it will need to address the general needs of serialization in much the way ActiveJob can be used with different workers.
|
||||
- The next major release *is* in development. We're starting simple and avoiding, at least at the outset, all the complications in AMS version, especially all the implicit behavior from guessing the serializer, to the association's serializer, to the serialization type, etc.
|
||||
- The basic idea is that models to serializers are a one to many relationship. Everything will need to be explicit. If you want to serialize a User with a UserSerializer, you'll need to call it directly. The serializer will essentially be for defining a basic JSON:API resource object: id, type, attributes, and relationships. The serializer will have an as_json method and can be told which fields (attributes/relationships) to serialize to JSON and will likely *not* know serialize any more than the relations id and type. Serializing anything more about the relations would require code that called a serializer. (This is still somewhat in discussion).
|
||||
- If this works out, the idea is to get something into Rails that existing libraries can use.
|
||||
|
||||
See [PR 2121](https://github.com/rails-api/active_model_serializers/pull/2121) where these changes were introduced for more information and any discussion.
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
This project adheres to [semver](http://semver.org/)
|
||||
|
||||
69
Rakefile
69
Rakefile
@ -3,72 +3,3 @@ begin
|
||||
rescue LoadError
|
||||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||
end
|
||||
begin
|
||||
require 'simplecov'
|
||||
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
||||
end
|
||||
import('lib/tasks/rubocop.rake')
|
||||
|
||||
Bundler::GemHelper.install_tasks
|
||||
|
||||
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
|
||||
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|
|
||||
spec.name = 'active_model_serializers'
|
||||
spec.version = ActiveModel::Serializer::VERSION
|
||||
spec.version = "1.0.0-dev"
|
||||
spec.platform = Gem::Platform::RUBY
|
||||
spec.authors = ['Steve Klabnik']
|
||||
spec.email = ['steve@steveklabnik.com']
|
||||
@ -20,44 +17,4 @@ Gem::Specification.new do |spec|
|
||||
spec.executables = []
|
||||
|
||||
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
|
||||
|
||||
21
appveyor.yml
21
appveyor.yml
@ -2,29 +2,10 @@ version: 1.0.{build}-{branch}
|
||||
|
||||
skip_tags: true
|
||||
|
||||
environment:
|
||||
JRUBY_OPTS: "--dev -J-Xmx1024M --debug"
|
||||
matrix:
|
||||
- ruby_version: "Ruby21"
|
||||
- ruby_version: "Ruby21-x64"
|
||||
|
||||
cache:
|
||||
- vendor/bundle
|
||||
|
||||
install:
|
||||
- SET PATH=C:\%ruby_version%\bin;%PATH%
|
||||
- gem update --system
|
||||
- gem uninstall bundler -a -x
|
||||
- gem install bundler -v 1.13.7
|
||||
- bundle env
|
||||
- bundle install --path=vendor/bundle --retry=3 --jobs=3
|
||||
|
||||
before_test:
|
||||
- ruby -v
|
||||
- gem -v
|
||||
- bundle -v
|
||||
|
||||
test_script:
|
||||
- bundle exec rake ci
|
||||
- true
|
||||
|
||||
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