Clear out master
This commit is contained in:
parent
dff621e174
commit
0ef6ac30fc
105
.rubocop.yml
105
.rubocop.yml
|
@ -1,105 +0,0 @@
|
|||
AllCops:
|
||||
TargetRubyVersion: 2.1
|
||||
Exclude:
|
||||
- !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/
|
||||
DisplayCopNames: true
|
||||
DisplayStyleGuide: true
|
||||
# https://github.com/bbatsov/rubocop/blob/master/manual/caching.md
|
||||
# https://github.com/bbatsov/rubocop/blob/e8680418b351491e111a18cf5b453fc07a3c5239/config/default.yml#L60-L77
|
||||
UseCache: true
|
||||
CacheRootDirectory: tmp
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
Lint/NestedMethodDefinition:
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- test/action_controller/serialization_test.rb
|
||||
|
||||
Style/Alias:
|
||||
EnforcedStyle: prefer_alias
|
||||
|
||||
Style/StringLiterals:
|
||||
EnforcedStyle: single_quotes
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 35 # TODO: Lower to 15
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 261 # TODO: Lower to 100
|
||||
Exclude:
|
||||
- test/**/*.rb
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 7 # TODO: Lower to 6
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 251 # TODO: Lower to 80
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 106 # TODO: Lower to 10
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 9 # TODO: Lower to 7
|
||||
|
||||
Style/AlignParameters:
|
||||
EnforcedStyle: with_fixed_indentation
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
EnforcedStyle: nested
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Style/MissingElse:
|
||||
Enabled: true
|
||||
EnforcedStyle: case
|
||||
|
||||
Style/EmptyElse:
|
||||
EnforcedStyle: empty
|
||||
|
||||
Style/MultilineOperationIndentation:
|
||||
EnforcedStyle: indented
|
||||
|
||||
Style/BlockDelimiters:
|
||||
Enabled: true
|
||||
EnforcedStyle: line_count_based
|
||||
|
||||
Style/SignalException:
|
||||
EnforcedStyle: semantic
|
||||
|
||||
Style/TrailingCommaInLiteral:
|
||||
EnforcedStyleForMultiline: no_comma
|
||||
|
||||
Style/ConditionalAssignment:
|
||||
Enabled: false
|
||||
|
||||
Style/DotPosition:
|
||||
EnforcedStyle: leading
|
||||
|
||||
########## test_helper.rb sanity
|
||||
Style/EndBlock:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
||||
|
||||
Style/SpecialGlobalVars:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
||||
|
||||
Style/GlobalVars:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
||||
|
||||
Style/AndOr:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
||||
- 'lib/active_model/serializer/lint.rb'
|
||||
|
||||
Style/Not:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
||||
|
||||
Style/ClassCheck:
|
||||
Exclude:
|
||||
- test/test_helper.rb
|
110
.simplecov
110
.simplecov
|
@ -1,110 +0,0 @@
|
|||
# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config
|
||||
# see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb
|
||||
# vim: set ft=ruby
|
||||
|
||||
## DEFINE VARIABLES
|
||||
@minimum_coverage = ENV.fetch('COVERAGE_MINIMUM') {
|
||||
case (defined?(RUBY_ENGINE) && RUBY_ENGINE) || "ruby"
|
||||
when 'jruby', 'rbx'
|
||||
96.0
|
||||
else
|
||||
98.1
|
||||
end
|
||||
}.to_f.round(2)
|
||||
# rubocop:disable Style/DoubleNegation
|
||||
ENV['FULL_BUILD'] ||= ENV['CI']
|
||||
@running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i)
|
||||
@generate_report = @running_ci || !!(ENV['COVERAGE'] =~ /\Atrue\z/i)
|
||||
@output = STDOUT
|
||||
# rubocop:enable Style/DoubleNegation
|
||||
|
||||
## CONFIGURE SIMPLECOV
|
||||
|
||||
SimpleCov.profiles.define 'app' do
|
||||
coverage_dir 'coverage'
|
||||
load_profile 'test_frameworks'
|
||||
|
||||
add_group 'Libraries', 'lib'
|
||||
|
||||
add_group 'Long files' do |src_file|
|
||||
src_file.lines.count > 100
|
||||
end
|
||||
class MaxLinesFilter < SimpleCov::Filter
|
||||
def matches?(source_file)
|
||||
source_file.lines.count < filter_argument
|
||||
end
|
||||
end
|
||||
add_group 'Short files', MaxLinesFilter.new(5)
|
||||
|
||||
# Exclude these paths from analysis
|
||||
add_filter '/config/'
|
||||
add_filter '/db/'
|
||||
add_filter 'tasks'
|
||||
add_filter '/.bundle/'
|
||||
end
|
||||
|
||||
## START TRACKING COVERAGE (before activating SimpleCov)
|
||||
require 'coverage'
|
||||
Coverage.start
|
||||
|
||||
## ADD SOME CUSTOM REPORTING AT EXIT
|
||||
SimpleCov.at_exit do
|
||||
next if $! and not ($!.kind_of? SystemExit and $!.success?)
|
||||
|
||||
header = "#{'*' * 20} SimpleCov Results #{'*' * 20}"
|
||||
results = SimpleCov.result.format!.join("\n")
|
||||
exit_message = <<-EOF
|
||||
|
||||
#{header}
|
||||
{{RESULTS}}
|
||||
{{FAILURE_MESSAGE}}
|
||||
|
||||
#{'*' * header.size}
|
||||
EOF
|
||||
percent = Float(SimpleCov.result.covered_percent)
|
||||
if percent < @minimum_coverage
|
||||
failure_message = <<-EOF
|
||||
Spec coverage was not high enough: #{percent.round(2)}% is < #{@minimum_coverage}%
|
||||
EOF
|
||||
exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', failure_message)
|
||||
@output.puts exit_message
|
||||
abort(failure_message) if @generate_report
|
||||
elsif @running_ci
|
||||
exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', <<-EOF)
|
||||
Nice job! Spec coverage (#{percent.round(2)}%) is still at or above #{@minimum_coverage}%
|
||||
EOF
|
||||
@output.puts exit_message
|
||||
end
|
||||
end
|
||||
|
||||
## CAPTURE CONFIG IN CLOSURE 'AppCoverage.start'
|
||||
## to defer running until test/test_helper.rb is loaded.
|
||||
# rubocop:disable Style/MultilineBlockChain
|
||||
AppCoverage = Class.new do
|
||||
def initialize(&block)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def start
|
||||
@block.call
|
||||
end
|
||||
end.new do
|
||||
SimpleCov.start 'app'
|
||||
if @generate_report
|
||||
if @running_ci
|
||||
require 'codeclimate-test-reporter'
|
||||
@output.puts '[COVERAGE] Running with SimpleCov Simple Formatter and CodeClimate Test Reporter'
|
||||
formatters = [
|
||||
SimpleCov::Formatter::SimpleFormatter,
|
||||
CodeClimate::TestReporter::Formatter
|
||||
]
|
||||
else
|
||||
@output.puts '[COVERAGE] Running with SimpleCov HTML Formatter'
|
||||
formatters = [SimpleCov::Formatter::HTMLFormatter]
|
||||
end
|
||||
else
|
||||
formatters = []
|
||||
end
|
||||
SimpleCov.formatters = formatters
|
||||
end
|
||||
# rubocop:enable Style/MultilineBlockChain
|
47
.travis.yml
47
.travis.yml
|
@ -2,54 +2,9 @@ language: ruby
|
|||
|
||||
sudo: false
|
||||
|
||||
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
|
||||
|
|
|
@ -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).
|
|
@ -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)
|
|
@ -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).
|
|
@ -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)
|
||||
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|
268
README.md
268
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)
|
||||
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
|
||||
- [Guides](docs)
|
||||
|
@ -101,203 +36,8 @@ been released yet. Please see below for the documentation relevant to you.
|
|||
|
||||
## 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)
|
||||
|
||||
[![JSON API 1.0](https://img.shields.io/badge/JSON%20API-1.0-lightgrey.svg)](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