diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 82f07656..00000000 --- a/.rubocop.yml +++ /dev/null @@ -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 diff --git a/.simplecov b/.simplecov deleted file mode 100644 index 955a6060..00000000 --- a/.simplecov +++ /dev/null @@ -1,110 +0,0 @@ -# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config -# see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb -# vim: set ft=ruby - -## DEFINE VARIABLES -@minimum_coverage = ENV.fetch('COVERAGE_MINIMUM') { - case (defined?(RUBY_ENGINE) && RUBY_ENGINE) || "ruby" - when 'jruby', 'rbx' - 96.0 - else - 98.1 - end -}.to_f.round(2) -# rubocop:disable Style/DoubleNegation -ENV['FULL_BUILD'] ||= ENV['CI'] -@running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i) -@generate_report = @running_ci || !!(ENV['COVERAGE'] =~ /\Atrue\z/i) -@output = STDOUT -# rubocop:enable Style/DoubleNegation - -## CONFIGURE SIMPLECOV - -SimpleCov.profiles.define 'app' do - coverage_dir 'coverage' - load_profile 'test_frameworks' - - add_group 'Libraries', 'lib' - - add_group 'Long files' do |src_file| - src_file.lines.count > 100 - end - class MaxLinesFilter < SimpleCov::Filter - def matches?(source_file) - source_file.lines.count < filter_argument - end - end - add_group 'Short files', MaxLinesFilter.new(5) - - # Exclude these paths from analysis - add_filter '/config/' - add_filter '/db/' - add_filter 'tasks' - add_filter '/.bundle/' -end - -## START TRACKING COVERAGE (before activating SimpleCov) -require 'coverage' -Coverage.start - -## ADD SOME CUSTOM REPORTING AT EXIT -SimpleCov.at_exit do - next if $! and not ($!.kind_of? SystemExit and $!.success?) - - header = "#{'*' * 20} SimpleCov Results #{'*' * 20}" - results = SimpleCov.result.format!.join("\n") - exit_message = <<-EOF - -#{header} -{{RESULTS}} -{{FAILURE_MESSAGE}} - -#{'*' * header.size} - EOF - percent = Float(SimpleCov.result.covered_percent) - if percent < @minimum_coverage - failure_message = <<-EOF -Spec coverage was not high enough: #{percent.round(2)}% is < #{@minimum_coverage}% - EOF - exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', failure_message) - @output.puts exit_message - abort(failure_message) if @generate_report - elsif @running_ci - exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', <<-EOF) -Nice job! Spec coverage (#{percent.round(2)}%) is still at or above #{@minimum_coverage}% - EOF - @output.puts exit_message - end -end - -## CAPTURE CONFIG IN CLOSURE 'AppCoverage.start' -## to defer running until test/test_helper.rb is loaded. -# rubocop:disable Style/MultilineBlockChain -AppCoverage = Class.new do - def initialize(&block) - @block = block - end - - def start - @block.call - end -end.new do - SimpleCov.start 'app' - if @generate_report - if @running_ci - require 'codeclimate-test-reporter' - @output.puts '[COVERAGE] Running with SimpleCov Simple Formatter and CodeClimate Test Reporter' - formatters = [ - SimpleCov::Formatter::SimpleFormatter, - CodeClimate::TestReporter::Formatter - ] - else - @output.puts '[COVERAGE] Running with SimpleCov HTML Formatter' - formatters = [SimpleCov::Formatter::HTMLFormatter] - end - else - formatters = [] - end - SimpleCov.formatters = formatters -end -# rubocop:enable Style/MultilineBlockChain diff --git a/.travis.yml b/.travis.yml index 9aff1edc..b48fe610 100644 --- a/.travis.yml +++ b/.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 diff --git a/CHANGELOG-0-08.md b/CHANGELOG-0-08.md new file mode 100644 index 00000000..eec4e286 --- /dev/null +++ b/CHANGELOG-0-08.md @@ -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). diff --git a/CHANGELOG-0-09.md b/CHANGELOG-0-09.md new file mode 100644 index 00000000..a36e8e9b --- /dev/null +++ b/CHANGELOG-0-09.md @@ -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) diff --git a/CHANGELOG-0-10.md b/CHANGELOG-0-10.md new file mode 100644 index 00000000..fbe0bd21 --- /dev/null +++ b/CHANGELOG-0-10.md @@ -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). diff --git a/CHANGELOG-prehistory.md b/CHANGELOG-prehistory.md new file mode 100644 index 00000000..a2758830 --- /dev/null +++ b/CHANGELOG-prehistory.md @@ -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) diff --git a/CHANGELOG.md b/CHANGELOG.md index c975e473..11070ef7 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c006f45..3ea519c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.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 ``` - diff --git a/Gemfile b/Gemfile deleted file mode 100644 index e854a204..00000000 --- a/Gemfile +++ /dev/null @@ -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 diff --git a/README.md b/README.md index 5bdcd20d..ced76428 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,11 @@ # ActiveModelSerializers - - - - - - - - - - - - - -
Build Status - Build Status - Build status -
Code Quality - Code Quality - codebeat - Test Coverage -
Issue Stats - Pulse -
- ## 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) @@ -99,205 +34,32 @@ been released yet. Please see below for the documentation relevant to you. - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable) +## Status of AMS + +*Status*: + +- ❗️ All existing PRs against master will need to be closed and re-opened against 0-10-stable, if so desired +- ❗️ Master, for the moment, won't have any released version of AMS on it. + +*Changes to 0.10.x maintenance*: + +- The 0.10.x version has become a huge maintenance version. We had hoped to get it in shape for a 1.0 release, but it is clear that isn't going to happen. Almost none of the maintainers from 0.8, 0.9, or earlier 0.10 are still working on AMS. We'll continue to maintain 0.10.x on the 0-10-stable branch, but maintainers won't otherwise be actively developing on it + - We may choose to make a 0.11.x ( 0-11-stable) release based on 0-10-stable that just removes the deprecations. + +*What's happening to AMS*: + +- There's been a lot of churn around AMS since it began back in [Rails 3.2](CHANGELOG-prehistory.md) and a lot of new libraries are around and the JSON:API spec has reached 1.0. +- If there is to be a 1.0 release of AMS, it will need to address the general needs of serialization in much the way ActiveJob can be used with different workers. +- The next major release *is* in development. We're starting simple and avoiding, at least at the outset, all the complications in AMS version, especially all the implicit behavior from guessing the serializer, to the association's serializer, to the serialization type, etc. +- The basic idea is that models to serializers are a one to many relationship. Everything will need to be explicit. If you want to serialize a User with a UserSerializer, you'll need to call it directly. The serializer will essentially be for defining a basic JSON:API resource object: id, type, attributes, and relationships. The serializer will have an as_json method and can be told which fields (attributes/relationships) to serialize to JSON and will likely *not* know serialize any more than the relations id and type. Serializing anything more about the relations would require code that called a serializer. (This is still somewhat in discussion). +- If this works out, the idea is to get something into Rails that existing libraries can use. + +See [PR 2121](https://github.com/rails-api/active_model_serializers/pull/2121) where these changes were introduced for more information and any discussion. + ## High-level behavior -Choose an adapter from [adapters](lib/active_model_serializers/adapter): - -``` ruby -ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes` -``` - -Given a [serializable model](lib/active_model/serializer/lint.rb): - -```ruby -# either -class SomeResource < ActiveRecord::Base - # columns: title, body -end -# or -class SomeResource < ActiveModelSerializers::Model - attributes :title, :body -end -``` - -And initialized as: - -```ruby -resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration') -``` - -Given a serializer for the serializable model: - -```ruby -class SomeSerializer < ActiveModel::Serializer - attribute :title, key: :name - attributes :body -end -``` - -The model can be serialized as: - -```ruby -options = {} -serialization = ActiveModelSerializers::SerializableResource.new(resource, options) -serialization.to_json -serialization.as_json -``` - -SerializableResource delegates to the adapter, which it builds as: - -```ruby -adapter_options = {} -adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options) -adapter.to_json -adapter.as_json -adapter.serializable_hash -``` - -The adapter formats the serializer's attributes and associations (a.k.a. includes): - -```ruby -serializer_options = {} -serializer = SomeSerializer.new(resource, serializer_options) -serializer.attributes -serializer.associations -``` - ## Architecture -This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, -please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or -[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md). - -The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile). - -### ActiveModel::Serializer - -An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) -and exposes an `attributes` method, among a few others. -It allows you to specify which attributes and associations should be represented in the serializatation of the resource. -It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself. -It may be useful to think of it as a -[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters). - -#### ActiveModel::CollectionSerializer - -The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers -and, if there is no serializer, primitives. - -### ActiveModelSerializers::Adapter::Base - -The **`ActiveModelSerializeres::Adapter::Base`** describes the structure of the JSON document generated from a -serializer. For example, the `Attributes` example represents each serializer as its -unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON -API](http://jsonapi.org/) document. - -### ActiveModelSerializers::SerializableResource - -The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter -to an object that responds to `to_json`, and `as_json`. It is used in the controller to -encapsulate the serialization resource when rendered. However, it can also be used on its own -to serialize a resource outside of a controller, as well. - -### Primitive handling - -Definitions: A primitive is usually a String or Array. There is no serializer -defined for them; they will be serialized when the resource is converted to JSON (`as_json` or -`to_json`). (The below also applies for any object with no serializer.) - -- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all. - -Internally, if no serializer can be found in the controller, the resource is not decorated by -ActiveModelSerializers. - -- However, when a primitive value is an attribute or in a collection, it is not modified. - -When serializing a collection and the collection serializer (CollectionSerializer) cannot -identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128). -For example, when caught by `Reflection#build_association`, and the association value is set directly: - -```ruby -reflection_options[:virtual_value] = association_value.try(:as_json) || association_value -``` - -(which is called by the adapter as `serializer.associations(*)`.) - -### How options are parsed - -High-level overview: - -- For a **collection** - - `:serializer` specifies the collection serializer and - - `:each_serializer` specifies the serializer for each resource in the collection. -- For a **single resource**, the `:serializer` option is the resource serializer. -- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by - [`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5). - The remaining options are serializer options. - -Details: - -1. **ActionController::Serialization** - 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)` - 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`). - The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5). -1. **ActiveModelSerializers::SerializableResource** - 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.) - - Where `serializer?` is `use_adapter? && !!(serializer)` - - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil); - False when explicit adapter is falsy (nil or false)' - - Where `serializer`: - 1. from explicit `:serializer` option, else - 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)` - 1. A side-effect of checking `serializer` is: - - The `:serializer` option is removed from the serializer_opts hash - - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option - 1. The serializer and adapter are created as - 1. `serializer_instance = serializer.new(resource, serializer_opts)` - 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)` -1. **ActiveModel::Serializer::CollectionSerializer#new** - 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts - is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16). -1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for - resource as defined by the serializer. - -(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)` -methods on the resource serialization by the Rails JSON renderer. They are, therefore, important -to know about, but not part of ActiveModelSerializers.) - -### What does a 'serializable resource' look like? - -- An `ActiveRecord::Base` object. -- Any Ruby object that passes the - [Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests) - [code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb). - -ActiveModelSerializers provides a -[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb), -which is a simple serializable PORO (Plain-Old Ruby Object). - -`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code. - -```ruby -class MyModel < ActiveModelSerializers::Model - attributes :id, :name, :level -end -``` - -The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an -ActiveRecord::Base object or not. - -Outside of the controller the rules are **exactly** the same as for records. For example: - -```ruby -render json: MyModel.new(level: 'awesome'), adapter: :json -``` - -would be serialized the same as - -```ruby -ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json -``` - ## Semantic Versioning This project adheres to [semver](http://semver.org/) diff --git a/Rakefile b/Rakefile index 6ba0c2bc..fbfba082 100644 --- a/Rakefile +++ b/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] diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec index 805c99c8..02578620 100644 --- a/active_model_serializers.gemspec +++ b/active_model_serializers.gemspec @@ -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 diff --git a/appveyor.yml b/appveyor.yml index 7ecfa13a..aabf26a6 100644 --- a/appveyor.yml +++ b/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 diff --git a/bin/bench b/bin/bench deleted file mode 100755 index 8e8d5a49..00000000 --- a/bin/bench +++ /dev/null @@ -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 ', 'Benchmark name pattern') do |value| - options[:pattern] = value.split(',') - end - - opts.on('-e', '--env ', '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__ diff --git a/bin/bench_regression b/bin/bench_regression deleted file mode 100755 index c4f00cba..00000000 --- a/bin/bench_regression +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env ruby -require 'fileutils' -require 'pathname' -require 'shellwords' -require 'English' - -############################ -# USAGE -# -# bundle exec bin/bench_regression -# defaults to the current branch -# 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 diff --git a/bin/rubocop b/bin/rubocop deleted file mode 100755 index 269f8954..00000000 --- a/bin/rubocop +++ /dev/null @@ -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 diff --git a/bin/serve_benchmark b/bin/serve_benchmark deleted file mode 100755 index 3f292d18..00000000 --- a/bin/serve_benchmark +++ /dev/null @@ -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 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 94460ec1..00000000 --- a/docs/README.md +++ /dev/null @@ -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/ diff --git a/docs/STYLE.md b/docs/STYLE.md deleted file mode 100644 index ccd75dd4..00000000 --- a/docs/STYLE.md +++ /dev/null @@ -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). - diff --git a/docs/general/adapters.md b/docs/general/adapters.md deleted file mode 100644 index 84fc4e62..00000000 --- a/docs/general/adapters.md +++ /dev/null @@ -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) diff --git a/docs/general/caching.md b/docs/general/caching.md deleted file mode 100644 index 9ab9d71a..00000000 --- a/docs/general/caching.md +++ /dev/null @@ -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 -``` diff --git a/docs/general/configuration_options.md b/docs/general/configuration_options.md deleted file mode 100644 index 83f8890d..00000000 --- a/docs/general/configuration_options.md +++ /dev/null @@ -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` diff --git a/docs/general/deserialization.md b/docs/general/deserialization.md deleted file mode 100644 index 995abea9..00000000 --- a/docs/general/deserialization.md +++ /dev/null @@ -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. diff --git a/docs/general/fields.md b/docs/general/fields.md deleted file mode 100644 index a1a12be6..00000000 --- a/docs/general/fields.md +++ /dev/null @@ -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. diff --git a/docs/general/getting_started.md b/docs/general/getting_started.md deleted file mode 100644 index b39cd283..00000000 --- a/docs/general/getting_started.md +++ /dev/null @@ -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' -} -``` diff --git a/docs/general/instrumentation.md b/docs/general/instrumentation.md deleted file mode 100644 index 560494ac..00000000 --- a/docs/general/instrumentation.md +++ /dev/null @@ -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`. diff --git a/docs/general/key_transforms.md b/docs/general/key_transforms.md deleted file mode 100644 index fd1be2d7..00000000 --- a/docs/general/key_transforms.md +++ /dev/null @@ -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` | diff --git a/docs/general/logging.md b/docs/general/logging.md deleted file mode 100644 index 321bf5d8..00000000 --- a/docs/general/logging.md +++ /dev/null @@ -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) -``` diff --git a/docs/general/rendering.md b/docs/general/rendering.md deleted file mode 100644 index af2d886f..00000000 --- a/docs/general/rendering.md +++ /dev/null @@ -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). diff --git a/docs/general/serializers.md b/docs/general/serializers.md deleted file mode 100644 index 5b23ba0f..00000000 --- a/docs/general/serializers.md +++ /dev/null @@ -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`
`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`
`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 -``` diff --git a/docs/how-open-source-maintained.jpg b/docs/how-open-source-maintained.jpg deleted file mode 100644 index 7e4fa53a..00000000 Binary files a/docs/how-open-source-maintained.jpg and /dev/null differ diff --git a/docs/howto/add_pagination_links.md b/docs/howto/add_pagination_links.md deleted file mode 100644 index e2792383..00000000 --- a/docs/howto/add_pagination_links.md +++ /dev/null @@ -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. diff --git a/docs/howto/add_relationship_links.md b/docs/howto/add_relationship_links.md deleted file mode 100644 index ba8f7f8a..00000000 --- a/docs/howto/add_relationship_links.md +++ /dev/null @@ -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" - } - } - } - } -} -``` diff --git a/docs/howto/add_root_key.md b/docs/howto/add_root_key.md deleted file mode 100644 index 82a4ab6f..00000000 --- a/docs/howto/add_root_key.md +++ /dev/null @@ -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. diff --git a/docs/howto/grape_integration.md b/docs/howto/grape_integration.md deleted file mode 100644 index 9ff1353e..00000000 --- a/docs/howto/grape_integration.md +++ /dev/null @@ -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 -``` diff --git a/docs/howto/outside_controller_use.md b/docs/howto/outside_controller_use.md deleted file mode 100644 index cb6d9b5e..00000000 --- a/docs/howto/outside_controller_use.md +++ /dev/null @@ -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 -``` diff --git a/docs/howto/passing_arbitrary_options.md b/docs/howto/passing_arbitrary_options.md deleted file mode 100644 index 4e0fc9a0..00000000 --- a/docs/howto/passing_arbitrary_options.md +++ /dev/null @@ -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 -``` diff --git a/docs/howto/serialize_poro.md b/docs/howto/serialize_poro.md deleted file mode 100644 index 3b98267c..00000000 --- a/docs/howto/serialize_poro.md +++ /dev/null @@ -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). diff --git a/docs/howto/test.md b/docs/howto/test.md deleted file mode 100644 index c66e0e9a..00000000 --- a/docs/howto/test.md +++ /dev/null @@ -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" - } - } -} -``` diff --git a/docs/howto/upgrade_from_0_8_to_0_10.md b/docs/howto/upgrade_from_0_8_to_0_10.md deleted file mode 100644 index ea51e81c..00000000 --- a/docs/howto/upgrade_from_0_8_to_0_10.md +++ /dev/null @@ -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": }, 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": [ - # , - # ... - # ] - # } - 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. - diff --git a/docs/integrations/ember-and-json-api.md b/docs/integrations/ember-and-json-api.md deleted file mode 100644 index eb7f1ade..00000000 --- a/docs/integrations/ember-and-json-api.md +++ /dev/null @@ -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'` diff --git a/docs/integrations/grape.md b/docs/integrations/grape.md deleted file mode 100644 index 7c855ebf..00000000 --- a/docs/integrations/grape.md +++ /dev/null @@ -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. diff --git a/docs/jsonapi/errors.md b/docs/jsonapi/errors.md deleted file mode 100644 index d19e2f9c..00000000 --- a/docs/jsonapi/errors.md +++ /dev/null @@ -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' } -# ] -# } -``` diff --git a/docs/jsonapi/schema.md b/docs/jsonapi/schema.md deleted file mode 100644 index baffe358..00000000 --- a/docs/jsonapi/schema.md +++ /dev/null @@ -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),
attributes, relationships,
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(
`"^(?!relationships$|links$)\\w[-\\w_]*$"`),
any valid JSON | | AM::Serializer#attributes, AMS::Adapter::JsonApi#resource_object_for -| relationships | patternProperties(
`"^\\w[-\\w_]*$"`);
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),
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),
String(code), String(title),
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. diff --git a/docs/jsonapi/schema/schema.json b/docs/jsonapi/schema/schema.json deleted file mode 100644 index ef3ea351..00000000 --- a/docs/jsonapi/schema/schema.json +++ /dev/null @@ -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 - } - } -} diff --git a/docs/rfcs/0000-namespace.md b/docs/rfcs/0000-namespace.md deleted file mode 100644 index da07c4c1..00000000 --- a/docs/rfcs/0000-namespace.md +++ /dev/null @@ -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. diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb deleted file mode 100644 index ea84c674..00000000 --- a/lib/action_controller/serialization.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializable_resource.rb b/lib/active_model/serializable_resource.rb deleted file mode 100644 index 0e1c8e2d..00000000 --- a/lib/active_model/serializable_resource.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'set' - -module ActiveModel - class SerializableResource - class << self - extend ActiveModelSerializers::Deprecate - - delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource - end - end -end diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb deleted file mode 100644 index 9d00e6fb..00000000 --- a/lib/active_model/serializer.rb +++ /dev/null @@ -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] 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 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 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 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] - 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 diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb deleted file mode 100644 index 6b5f30ca..00000000 --- a/lib/active_model/serializer/adapter.rb +++ /dev/null @@ -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' diff --git a/lib/active_model/serializer/adapter/attributes.rb b/lib/active_model/serializer/adapter/attributes.rb deleted file mode 100644 index e04e5fd8..00000000 --- a/lib/active_model/serializer/adapter/attributes.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/base.rb b/lib/active_model/serializer/adapter/base.rb deleted file mode 100644 index 013a9705..00000000 --- a/lib/active_model/serializer/adapter/base.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json.rb b/lib/active_model/serializer/adapter/json.rb deleted file mode 100644 index 1998a4c6..00000000 --- a/lib/active_model/serializer/adapter/json.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb deleted file mode 100644 index 13777cdc..00000000 --- a/lib/active_model/serializer/adapter/json_api.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/null.rb b/lib/active_model/serializer/adapter/null.rb deleted file mode 100644 index 906953d1..00000000 --- a/lib/active_model/serializer/adapter/null.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb deleted file mode 100644 index 2e768deb..00000000 --- a/lib/active_model/serializer/array_serializer.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/association.rb b/lib/active_model/serializer/association.rb deleted file mode 100644 index 7ce82316..00000000 --- a/lib/active_model/serializer/association.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/attribute.rb b/lib/active_model/serializer/attribute.rb deleted file mode 100644 index d3e006fa..00000000 --- a/lib/active_model/serializer/attribute.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/belongs_to_reflection.rb b/lib/active_model/serializer/belongs_to_reflection.rb deleted file mode 100644 index 04bbc6fc..00000000 --- a/lib/active_model/serializer/belongs_to_reflection.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ActiveModel - class Serializer - # @api private - class BelongsToReflection < Reflection - # @api private - def foreign_key_on - :self - end - end - end -end diff --git a/lib/active_model/serializer/collection_serializer.rb b/lib/active_model/serializer/collection_serializer.rb deleted file mode 100644 index 44b806a1..00000000 --- a/lib/active_model/serializer/collection_serializer.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/concerns/caching.rb b/lib/active_model/serializer/concerns/caching.rb deleted file mode 100644 index 2a030b68..00000000 --- a/lib/active_model/serializer/concerns/caching.rb +++ /dev/null @@ -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 `'" - # AND - # "/c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `'" - # 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 ActionController::Base.cache_store - # 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 diff --git a/lib/active_model/serializer/error_serializer.rb b/lib/active_model/serializer/error_serializer.rb deleted file mode 100644 index d0e70809..00000000 --- a/lib/active_model/serializer/error_serializer.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ActiveModel - class Serializer - class ErrorSerializer < ActiveModel::Serializer - # @return [Hash>] - def as_json - object.errors.messages - end - - def success? - false - end - end - end -end diff --git a/lib/active_model/serializer/errors_serializer.rb b/lib/active_model/serializer/errors_serializer.rb deleted file mode 100644 index 1fd924d5..00000000 --- a/lib/active_model/serializer/errors_serializer.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/field.rb b/lib/active_model/serializer/field.rb deleted file mode 100644 index 6299b099..00000000 --- a/lib/active_model/serializer/field.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/fieldset.rb b/lib/active_model/serializer/fieldset.rb deleted file mode 100644 index efa3187c..00000000 --- a/lib/active_model/serializer/fieldset.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/has_many_reflection.rb b/lib/active_model/serializer/has_many_reflection.rb deleted file mode 100644 index 99f6f63c..00000000 --- a/lib/active_model/serializer/has_many_reflection.rb +++ /dev/null @@ -1,10 +0,0 @@ -module ActiveModel - class Serializer - # @api private - class HasManyReflection < Reflection - def collection? - true - end - end - end -end diff --git a/lib/active_model/serializer/has_one_reflection.rb b/lib/active_model/serializer/has_one_reflection.rb deleted file mode 100644 index a385009b..00000000 --- a/lib/active_model/serializer/has_one_reflection.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActiveModel - class Serializer - # @api private - class HasOneReflection < Reflection - end - end -end diff --git a/lib/active_model/serializer/lazy_association.rb b/lib/active_model/serializer/lazy_association.rb deleted file mode 100644 index 8c4dad61..00000000 --- a/lib/active_model/serializer/lazy_association.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/lint.rb b/lib/active_model/serializer/lint.rb deleted file mode 100644 index c40cebeb..00000000 --- a/lib/active_model/serializer/lint.rb +++ /dev/null @@ -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 ActiveModel::Serializer::Lint::Tests 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 serializable_hash 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 serializable_hash and if it takes - # zero or one arguments. - # Fails otherwise. - # - # serializable_hash 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 read_attribute_for_serialization - # and if it requires one argument (the attribute to be read). - # Fails otherwise. - # - # read_attribute_for_serialization 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 as_json and if it takes - # zero or one arguments. - # Fails otherwise. - # - # as_json returns a hash representation of a serialized object. - # It may delegate to serializable_hash - # 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 to_json and if it takes - # zero or one arguments. - # Fails otherwise. - # - # to_json returns a string representation (JSON) of a serialized object. - # It may be called on the result of as_json. - # 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 cache_key - # Fails otherwise. - # - # cache_key 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 updated_at and if it takes no - # arguments. - # Fails otherwise. - # - # updated_at 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 id and if it takes no - # arguments. - # Fails otherwise. - # - # id 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 model_name and if it - # is in an instance of +ActiveModel::Name+. - # Fails otherwise. - # - # model_name 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 diff --git a/lib/active_model/serializer/null.rb b/lib/active_model/serializer/null.rb deleted file mode 100644 index 818bbbfa..00000000 --- a/lib/active_model/serializer/null.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ActiveModel - class Serializer - class Null < Serializer - def attributes(*) - {} - end - - def associations(*) - {} - end - - def serializable_hash(*) - {} - end - end - end -end diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb deleted file mode 100644 index 2e5cc2a1..00000000 --- a/lib/active_model/serializer/reflection.rb +++ /dev/null @@ -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 }, #) - # # 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 diff --git a/lib/active_model/serializer/version.rb b/lib/active_model/serializer/version.rb deleted file mode 100644 index e692240a..00000000 --- a/lib/active_model/serializer/version.rb +++ /dev/null @@ -1,5 +0,0 @@ -module ActiveModel - class Serializer - VERSION = '0.10.6'.freeze - end -end diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb deleted file mode 100644 index 18cdd9f7..00000000 --- a/lib/active_model_serializers.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter.rb b/lib/active_model_serializers/adapter.rb deleted file mode 100644 index 98caab44..00000000 --- a/lib/active_model_serializers/adapter.rb +++ /dev/null @@ -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] - def adapter_map - ADAPTER_MAP - end - - # @return [Array] 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 diff --git a/lib/active_model_serializers/adapter/attributes.rb b/lib/active_model_serializers/adapter/attributes.rb deleted file mode 100644 index 79ca7b5f..00000000 --- a/lib/active_model_serializers/adapter/attributes.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/base.rb b/lib/active_model_serializers/adapter/base.rb deleted file mode 100644 index 85158328..00000000 --- a/lib/active_model_serializers/adapter/base.rb +++ /dev/null @@ -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 options 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 diff --git a/lib/active_model_serializers/adapter/json.rb b/lib/active_model_serializers/adapter/json.rb deleted file mode 100644 index 423cfb9f..00000000 --- a/lib/active_model_serializers/adapter/json.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api.rb b/lib/active_model_serializers/adapter/json_api.rb deleted file mode 100644 index b225416b..00000000 --- a/lib/active_model_serializers/adapter/json_api.rb +++ /dev/null @@ -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 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 diff --git a/lib/active_model_serializers/adapter/json_api/deserialization.rb b/lib/active_model_serializers/adapter/json_api/deserialization.rb deleted file mode 100644 index b79125ac..00000000 --- a/lib/active_model_serializers/adapter/json_api/deserialization.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/error.rb b/lib/active_model_serializers/adapter/json_api/error.rb deleted file mode 100644 index c7b18716..00000000 --- a/lib/active_model_serializers/adapter/json_api/error.rb +++ /dev/null @@ -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>] 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 diff --git a/lib/active_model_serializers/adapter/json_api/jsonapi.rb b/lib/active_model_serializers/adapter/json_api/jsonapi.rb deleted file mode 100644 index e94578af..00000000 --- a/lib/active_model_serializers/adapter/json_api/jsonapi.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/link.rb b/lib/active_model_serializers/adapter/json_api/link.rb deleted file mode 100644 index 64e15071..00000000 --- a/lib/active_model_serializers/adapter/json_api/link.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/meta.rb b/lib/active_model_serializers/adapter/json_api/meta.rb deleted file mode 100644 index d889b3eb..00000000 --- a/lib/active_model_serializers/adapter/json_api/meta.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/pagination_links.rb b/lib/active_model_serializers/adapter/json_api/pagination_links.rb deleted file mode 100644 index b15f5ba6..00000000 --- a/lib/active_model_serializers/adapter/json_api/pagination_links.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb deleted file mode 100644 index 5d7399a3..00000000 --- a/lib/active_model_serializers/adapter/json_api/relationship.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb deleted file mode 100644 index 3a235f2b..00000000 --- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/adapter/null.rb b/lib/active_model_serializers/adapter/null.rb deleted file mode 100644 index 9e5faf5c..00000000 --- a/lib/active_model_serializers/adapter/null.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ActiveModelSerializers - module Adapter - class Null < Base - def serializable_hash(*) - {} - end - end - end -end diff --git a/lib/active_model_serializers/callbacks.rb b/lib/active_model_serializers/callbacks.rb deleted file mode 100644 index 71237e4a..00000000 --- a/lib/active_model_serializers/callbacks.rb +++ /dev/null @@ -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: - # - # * around_render - # - 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 diff --git a/lib/active_model_serializers/deprecate.rb b/lib/active_model_serializers/deprecate.rb deleted file mode 100644 index e173321d..00000000 --- a/lib/active_model_serializers/deprecate.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/deserialization.rb b/lib/active_model_serializers/deserialization.rb deleted file mode 100644 index 878dd98d..00000000 --- a/lib/active_model_serializers/deserialization.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/json_pointer.rb b/lib/active_model_serializers/json_pointer.rb deleted file mode 100644 index a262f3b2..00000000 --- a/lib/active_model_serializers/json_pointer.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/logging.rb b/lib/active_model_serializers/logging.rb deleted file mode 100644 index 943e937e..00000000 --- a/lib/active_model_serializers/logging.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/lookup_chain.rb b/lib/active_model_serializers/lookup_chain.rb deleted file mode 100644 index 25db8e13..00000000 --- a/lib/active_model_serializers/lookup_chain.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/model.rb b/lib/active_model_serializers/model.rb deleted file mode 100644 index 2ff3d60c..00000000 --- a/lib/active_model_serializers/model.rb +++ /dev/null @@ -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] - 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] - # @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 attributes[:id] = 5. - # @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 attributes[:updated_at] = Time.current. - # @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 diff --git a/lib/active_model_serializers/railtie.rb b/lib/active_model_serializers/railtie.rb deleted file mode 100644 index d6843c9c..00000000 --- a/lib/active_model_serializers/railtie.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/register_jsonapi_renderer.rb b/lib/active_model_serializers/register_jsonapi_renderer.rb deleted file mode 100644 index 715c6ab3..00000000 --- a/lib/active_model_serializers/register_jsonapi_renderer.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/serializable_resource.rb b/lib/active_model_serializers/serializable_resource.rb deleted file mode 100644 index f67cf238..00000000 --- a/lib/active_model_serializers/serializable_resource.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/serialization_context.rb b/lib/active_model_serializers/serialization_context.rb deleted file mode 100644 index 9ef604f2..00000000 --- a/lib/active_model_serializers/serialization_context.rb +++ /dev/null @@ -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 diff --git a/lib/active_model_serializers/test.rb b/lib/active_model_serializers/test.rb deleted file mode 100644 index bec452ec..00000000 --- a/lib/active_model_serializers/test.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActiveModelSerializers - module Test - extend ActiveSupport::Autoload - autoload :Serializer - autoload :Schema - end -end diff --git a/lib/active_model_serializers/test/schema.rb b/lib/active_model_serializers/test/schema.rb deleted file mode 100644 index a0001586..00000000 --- a/lib/active_model_serializers/test/schema.rb +++ /dev/null @@ -1,138 +0,0 @@ -module ActiveModelSerializers - module Test - module Schema - # A Minitest Assertion that test the response is valid against a schema. - # @param schema_path [String] a custom schema path - # @param message [String] a custom error message - # @return [Boolean] true when the response is valid - # @return [Minitest::Assertion] when the response is invalid - # @example - # get :index - # assert_response_schema - def assert_response_schema(schema_path = nil, message = nil) - matcher = AssertResponseSchema.new(schema_path, request, response, message) - assert(matcher.call, matcher.message) - end - - def assert_request_schema(schema_path = nil, message = nil) - matcher = AssertRequestSchema.new(schema_path, request, response, message) - assert(matcher.call, matcher.message) - end - - # May be renamed - def assert_request_response_schema(schema_path = nil, message = nil) - assert_request_schema(schema_path, message) - assert_response_schema(schema_path, message) - end - - def assert_schema(payload, schema_path = nil, message = nil) - matcher = AssertSchema.new(schema_path, request, response, message, payload) - assert(matcher.call, matcher.message) - end - - MissingSchema = Class.new(Minitest::Assertion) - InvalidSchemaError = Class.new(Minitest::Assertion) - - class AssertSchema - attr_reader :schema_path, :request, :response, :message, :payload - - # Interface may change. - def initialize(schema_path, request, response, message, payload = nil) - require_json_schema! - @request = request - @response = response - @payload = payload - @schema_path = schema_path || schema_path_default - @message = message - @document_store = JsonSchema::DocumentStore.new - add_schema_to_document_store - end - - def call - json_schema.expand_references!(store: document_store) - status, errors = json_schema.validate(response_body) - @message = [message, errors.map(&:to_s).to_sentence].compact.join(': ') - status - end - - protected - - attr_reader :document_store - - def controller_path - request.filtered_parameters.with_indifferent_access[:controller] - end - - def action - request.filtered_parameters.with_indifferent_access[:action] - end - - def schema_directory - ActiveModelSerializers.config.schema_path - end - - def schema_full_path - "#{schema_directory}/#{schema_path}" - end - - def schema_path_default - "#{controller_path}/#{action}.json" - end - - def schema_data - load_json_file(schema_full_path) - end - - def response_body - load_json(response.body) - end - - def request_params - request.env['action_dispatch.request.request_parameters'] - end - - def json_schema - @json_schema ||= JsonSchema.parse!(schema_data) - end - - def add_schema_to_document_store - Dir.glob("#{schema_directory}/**/*.json").each do |path| - schema_data = load_json_file(path) - extra_schema = JsonSchema.parse!(schema_data) - document_store.add_schema(extra_schema) - end - end - - def load_json(json) - JSON.parse(json) - rescue JSON::ParserError => ex - raise InvalidSchemaError, ex.message - end - - def load_json_file(path) - load_json(File.read(path)) - rescue Errno::ENOENT - raise MissingSchema, "No Schema file at #{schema_full_path}" - end - - def require_json_schema! - require 'json_schema' - rescue LoadError - raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install" - end - end - class AssertResponseSchema < AssertSchema - def initialize(*) - super - @payload = response_body - end - end - class AssertRequestSchema < AssertSchema - def initialize(*) - super - @payload = request_params - end - end - end - end -end diff --git a/lib/active_model_serializers/test/serializer.rb b/lib/active_model_serializers/test/serializer.rb deleted file mode 100644 index dc812c55..00000000 --- a/lib/active_model_serializers/test/serializer.rb +++ /dev/null @@ -1,125 +0,0 @@ -require 'set' -module ActiveModelSerializers - module Test - module Serializer - extend ActiveSupport::Concern - - included do - setup :setup_serialization_subscriptions - teardown :teardown_serialization_subscriptions - end - - # Asserts that the request was rendered with the appropriate serializers. - # - # # assert that the "PostSerializer" serializer was rendered - # assert_serializer "PostSerializer" - # - # # return a custom error message - # assert_serializer "PostSerializer", "PostSerializer not rendered" - # - # # assert that the instance of PostSerializer was rendered - # assert_serializer PostSerializer - # - # # assert that the "PostSerializer" serializer was rendered - # assert_serializer :post_serializer - # - # # assert that the rendered serializer starts with "Post" - # assert_serializer %r{\APost.+\Z} - # - # # assert that no serializer was rendered - # assert_serializer nil - # - def assert_serializer(expectation, message = nil) - @assert_serializer.expectation = expectation - @assert_serializer.message = message - @assert_serializer.response = response - assert(@assert_serializer.matches?, @assert_serializer.message) - end - - class AssertSerializer - attr_reader :serializers, :message - attr_accessor :response, :expectation - - def initialize - @serializers = Set.new - @_subscribers = [] - end - - def message=(message) - @message = message || "expecting <#{expectation.inspect}> but rendering with <#{serializers.to_a}>" - end - - def matches? - # Force body to be read in case the template is being streamed. - response.body - - case expectation - when a_serializer? then matches_class? - when Symbol then matches_symbol? - when String then matches_string? - when Regexp then matches_regexp? - when NilClass then matches_nil? - else fail ArgumentError, 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil' - end - end - - def subscribe - @_subscribers << ActiveSupport::Notifications.subscribe(event_name) do |_name, _start, _finish, _id, payload| - serializer = payload[:serializer].name - serializers << serializer - end - end - - def unsubscribe - @_subscribers.each do |subscriber| - ActiveSupport::Notifications.unsubscribe(subscriber) - end - end - - private - - def matches_class? - serializers.include?(expectation.name) - end - - def matches_symbol? - camelize_expectation = expectation.to_s.camelize - serializers.include?(camelize_expectation) - end - - def matches_string? - !expectation.empty? && serializers.include?(expectation) - end - - def matches_regexp? - serializers.any? do |serializer| - serializer.match(expectation) - end - end - - def matches_nil? - serializers.empty? - end - - def a_serializer? - ->(exp) { exp.is_a?(Class) && exp < ActiveModel::Serializer } - end - - def event_name - ::ActiveModelSerializers::Logging::RENDER_EVENT - end - end - - private - - def setup_serialization_subscriptions - @assert_serializer = AssertSerializer.new - @assert_serializer.subscribe - end - - def teardown_serialization_subscriptions - @assert_serializer.unsubscribe - end - end - end -end diff --git a/lib/generators/rails/USAGE b/lib/generators/rails/USAGE deleted file mode 100644 index 85de6ad3..00000000 --- a/lib/generators/rails/USAGE +++ /dev/null @@ -1,6 +0,0 @@ -Description: - Generates a serializer for the given resource. - -Example: - `rails generate serializer Account name created_at` - diff --git a/lib/generators/rails/resource_override.rb b/lib/generators/rails/resource_override.rb deleted file mode 100644 index 5177a636..00000000 --- a/lib/generators/rails/resource_override.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'rails/generators' -require 'rails/generators/rails/resource/resource_generator' - -module Rails - module Generators - class ResourceGenerator - hook_for :serializer, default: true, type: :boolean - end - end -end diff --git a/lib/generators/rails/serializer_generator.rb b/lib/generators/rails/serializer_generator.rb deleted file mode 100644 index e670d5cf..00000000 --- a/lib/generators/rails/serializer_generator.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Rails - module Generators - class SerializerGenerator < NamedBase - source_root File.expand_path('../templates', __FILE__) - check_class_collision suffix: 'Serializer' - - argument :attributes, type: :array, default: [], banner: 'field:type field:type' - - class_option :parent, type: :string, desc: 'The parent class for the generated serializer' - - def create_serializer_file - template 'serializer.rb.erb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb") - end - - private - - def attributes_names - [:id] + attributes.reject(&:reference?).map! { |a| a.name.to_sym } - end - - def association_names - attributes.select(&:reference?).map! { |a| a.name.to_sym } - end - - def parent_class_name - if options[:parent] - options[:parent] - elsif 'ApplicationSerializer'.safe_constantize - 'ApplicationSerializer' - else - 'ActiveModel::Serializer' - end - end - end - end -end diff --git a/lib/generators/rails/templates/serializer.rb.erb b/lib/generators/rails/templates/serializer.rb.erb deleted file mode 100644 index 4ebb004e..00000000 --- a/lib/generators/rails/templates/serializer.rb.erb +++ /dev/null @@ -1,8 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Serializer < <%= parent_class_name %> - attributes <%= attributes_names.map(&:inspect).join(", ") %> -<% association_names.each do |attribute| -%> - has_one :<%= attribute %> -<% end -%> -end -<% end -%> diff --git a/lib/grape/active_model_serializers.rb b/lib/grape/active_model_serializers.rb deleted file mode 100644 index 8dc7a314..00000000 --- a/lib/grape/active_model_serializers.rb +++ /dev/null @@ -1,16 +0,0 @@ -# To add Grape support, require 'grape/active_model_serializers' in the base of your Grape endpoints -# Then add 'include Grape::ActiveModelSerializers' to enable the formatter and helpers -require 'active_model_serializers' -require 'grape/formatters/active_model_serializers' -require 'grape/helpers/active_model_serializers' - -module Grape - module ActiveModelSerializers - extend ActiveSupport::Concern - - included do - formatter :json, Grape::Formatters::ActiveModelSerializers - helpers Grape::Helpers::ActiveModelSerializers - end - end -end diff --git a/lib/grape/formatters/active_model_serializers.rb b/lib/grape/formatters/active_model_serializers.rb deleted file mode 100644 index 534c5bab..00000000 --- a/lib/grape/formatters/active_model_serializers.rb +++ /dev/null @@ -1,32 +0,0 @@ -# A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers' -# -# Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options], -# or better yet user the render helper in Grape::Helpers::ActiveModelSerializers - -require 'active_model_serializers/serialization_context' - -module Grape - module Formatters - module ActiveModelSerializers - def self.call(resource, env) - serializer_options = build_serializer_options(env) - ::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json - end - - def self.build_serializer_options(env) - ams_options = env[:active_model_serializer_options] || {} - - # Add serialization context - ams_options.fetch(:serialization_context) do - request = env['grape.request'] - ams_options[:serialization_context] = ::ActiveModelSerializers::SerializationContext.new( - request_url: request.url[/\A[^?]+/], - query_parameters: request.params - ) - end - - ams_options - end - end - end -end diff --git a/lib/grape/helpers/active_model_serializers.rb b/lib/grape/helpers/active_model_serializers.rb deleted file mode 100644 index afbdab85..00000000 --- a/lib/grape/helpers/active_model_serializers.rb +++ /dev/null @@ -1,17 +0,0 @@ -# Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers - -module Grape - module Helpers - module ActiveModelSerializers - # A convenience method for passing ActiveModelSerializers serializer options - # - # Example: To include relationships in the response: render(post, include: ['comments']) - # - # Example: To include pagination meta data: render(posts, meta: { page: posts.page, total_pages: posts.total_pages }) - def render(resource, active_model_serializer_options = {}) - env[:active_model_serializer_options] = active_model_serializer_options - resource - end - end - end -end diff --git a/lib/tasks/rubocop.rake b/lib/tasks/rubocop.rake deleted file mode 100644 index 5c9a1242..00000000 --- a/lib/tasks/rubocop.rake +++ /dev/null @@ -1,53 +0,0 @@ -begin - require 'rubocop' - require 'rubocop/rake_task' -rescue LoadError # rubocop:disable Lint/HandleExceptions -else - require 'rbconfig' - # https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2 - windows_platforms = /(msdos|mswin|djgpp|mingw)/ - if RbConfig::CONFIG['host_os'] =~ windows_platforms - desc 'No-op rubocop on Windows-- unsupported platform' - task :rubocop do - puts 'Skipping rubocop on Windows' - end - elsif defined?(::Rubinius) - desc 'No-op rubocop to avoid rbx segfault' - task :rubocop do - puts 'Skipping rubocop on rbx due to segfault' - puts 'https://github.com/rubinius/rubinius/issues/3499' - end - else - Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop) - patterns = [ - 'Gemfile', - 'Rakefile', - 'lib/**/*.{rb,rake}', - 'config/**/*.rb', - 'app/**/*.rb', - 'test/**/*.rb' - ] - desc 'Execute rubocop' - RuboCop::RakeTask.new(:rubocop) do |task| - task.options = ['--rails', '--display-cop-names', '--display-style-guide'] - task.formatters = ['progress'] - task.patterns = patterns - task.fail_on_error = true - end - - namespace :rubocop do - desc 'Auto-gen rubocop config' - task :auto_gen_config do - options = ['--auto-gen-config'].concat patterns - require 'benchmark' - result = 0 - cli = RuboCop::CLI.new - time = Benchmark.realtime do - result = cli.run(options) - end - puts "Finished in #{time} seconds" if cli.options[:debug] - abort('RuboCop failed!') if result.nonzero? - end - end - end -end diff --git a/test/action_controller/adapter_selector_test.rb b/test/action_controller/adapter_selector_test.rb deleted file mode 100644 index 3373de7c..00000000 --- a/test/action_controller/adapter_selector_test.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class AdapterSelectorTest < ActionController::TestCase - class Profile < Model - attributes :id, :name, :description - associations :comments - end - class ProfileSerializer < ActiveModel::Serializer - type 'profiles' - attributes :name, :description - end - - class AdapterSelectorTestController < ActionController::Base - def render_using_default_adapter - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile - end - - def render_using_adapter_override - @profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile, adapter: :json_api - end - - def render_skipping_adapter - @profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile, adapter: false - end - end - - tests AdapterSelectorTestController - - def test_render_using_default_adapter - get :render_using_default_adapter - assert_equal '{"name":"Name 1","description":"Description 1"}', response.body - end - - def test_render_using_adapter_override - get :render_using_adapter_override - - expected = { - data: { - id: 'render_using_adapter_override', - type: 'profiles', - attributes: { - name: 'Name 1', - description: 'Description 1' - } - } - } - - assert_equal expected.to_json, response.body - end - - def test_render_skipping_adapter - get :render_skipping_adapter - assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body - end - end - end -end diff --git a/test/action_controller/explicit_serializer_test.rb b/test/action_controller/explicit_serializer_test.rb deleted file mode 100644 index a23b6f6b..00000000 --- a/test/action_controller/explicit_serializer_test.rb +++ /dev/null @@ -1,135 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class ExplicitSerializerTest < ActionController::TestCase - class ExplicitSerializerTestController < ActionController::Base - def render_using_explicit_serializer - @profile = Profile.new(name: 'Name 1', - description: 'Description 1', - comments: 'Comments 1') - render json: @profile, serializer: ProfilePreviewSerializer - end - - def render_array_using_explicit_serializer - array = [ - Profile.new(name: 'Name 1', - description: 'Description 1', - comments: 'Comments 1'), - Profile.new(name: 'Name 2', - description: 'Description 2', - comments: 'Comments 2') - ] - render json: array, - serializer: PaginatedSerializer, - each_serializer: ProfilePreviewSerializer - end - - def render_array_using_implicit_serializer - array = [ - Profile.new(name: 'Name 1', - description: 'Description 1', - comments: 'Comments 1'), - Profile.new(name: 'Name 2', - description: 'Description 2', - comments: 'Comments 2') - ] - render json: array, - each_serializer: ProfilePreviewSerializer - end - - def render_array_using_explicit_serializer_and_custom_serializers - @post = Post.new(title: 'New Post', body: 'Body') - @author = Author.new(name: 'Jane Blogger') - @author.posts = [@post] - @post.author = @author - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @first_comment.author = nil - @second_comment.post = @post - @second_comment.author = nil - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post.blog = @blog - - render json: [@post], each_serializer: PostPreviewSerializer - end - - def render_using_explicit_each_serializer - location = Location.new(id: 42, lat: '-23.550520', lng: '-46.633309') - place = Place.new(id: 1337, name: 'Amazing Place', locations: [location]) - - render json: place, each_serializer: PlaceSerializer - end - end - - tests ExplicitSerializerTestController - - def test_render_using_explicit_serializer - get :render_using_explicit_serializer - - assert_equal 'application/json', @response.content_type - assert_equal '{"name":"Name 1"}', @response.body - end - - def test_render_array_using_explicit_serializer - get :render_array_using_explicit_serializer - assert_equal 'application/json', @response.content_type - - expected = [ - { 'name' => 'Name 1' }, - { 'name' => 'Name 2' } - ] - - assert_equal expected.to_json, @response.body - end - - def test_render_array_using_implicit_serializer - get :render_array_using_implicit_serializer - assert_equal 'application/json', @response.content_type - - expected = [ - { 'name' => 'Name 1' }, - { 'name' => 'Name 2' } - ] - assert_equal expected.to_json, @response.body - end - - def test_render_array_using_explicit_serializer_and_custom_serializers - get :render_array_using_explicit_serializer_and_custom_serializers - - expected = [ - { - 'title' => 'New Post', - 'body' => 'Body', - 'id' => @controller.instance_variable_get(:@post).id, - 'comments' => [{ 'id' => 1 }, { 'id' => 2 }], - 'author' => { 'id' => @controller.instance_variable_get(:@author).id } - } - ] - - assert_equal expected.to_json, @response.body - end - - def test_render_using_explicit_each_serializer - get :render_using_explicit_each_serializer - - expected = { - id: 1337, - name: 'Amazing Place', - locations: [ - { - id: 42, - lat: '-23.550520', - lng: '-46.633309', - address: 'Nowhere' # is a virtual attribute on LocationSerializer - } - ] - } - - assert_equal expected.to_json, response.body - end - end - end -end diff --git a/test/action_controller/json/include_test.rb b/test/action_controller/json/include_test.rb deleted file mode 100644 index 1fc8863e..00000000 --- a/test/action_controller/json/include_test.rb +++ /dev/null @@ -1,246 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class Json - class IncludeTest < ActionController::TestCase - INCLUDE_STRING = 'posts.comments'.freeze - INCLUDE_HASH = { posts: :comments }.freeze - DEEP_INCLUDE = 'posts.comments.author'.freeze - - class IncludeTestController < ActionController::Base - def setup_data - ActionController::Base.cache_store.clear - - @author = Author.new(id: 1, name: 'Steve K.') - - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - - @post.comments = [@first_comment, @second_comment] - @post.author = @author - - @first_comment.post = @post - @second_comment.post = @post - - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @author.posts = [@post] - - @first_comment.author = @author - @second_comment.author = @author - @author.comments = [@first_comment, @second_comment] - @author.roles = [] - @author.bio = {} - end - - def render_without_include - setup_data - render json: @author, adapter: :json - end - - def render_resource_with_include_hash - setup_data - render json: @author, include: INCLUDE_HASH, adapter: :json - end - - def render_resource_with_include_string - setup_data - render json: @author, include: INCLUDE_STRING, adapter: :json - end - - def render_resource_with_deep_include - setup_data - render json: @author, include: DEEP_INCLUDE, adapter: :json - end - - def render_without_recursive_relationships - # testing recursive includes ('**') can't have any cycles in the - # relationships, or we enter an infinite loop. - author = Author.new(id: 11, name: 'Jane Doe') - post = Post.new(id: 12, title: 'Hello World', body: 'My first post') - comment = Comment.new(id: 13, body: 'Commentary') - author.posts = [post] - post.comments = [comment] - render json: author - end - end - - tests IncludeTestController - - def test_render_without_include - get :render_without_include - response = JSON.parse(@response.body) - expected = { - 'author' => { - 'id' => 1, - 'name' => 'Steve K.', - 'posts' => [ - { - 'id' => 42, 'title' => 'New Post', 'body' => 'Body' - } - ], - 'roles' => [], - 'bio' => {} - } - } - - assert_equal(expected, response) - end - - def test_render_resource_with_include_hash - get :render_resource_with_include_hash - response = JSON.parse(@response.body) - - assert_equal(expected_include_response, response) - end - - def test_render_resource_with_include_string - get :render_resource_with_include_string - - response = JSON.parse(@response.body) - - assert_equal(expected_include_response, response) - end - - def test_render_resource_with_deep_include - get :render_resource_with_deep_include - - response = JSON.parse(@response.body) - - assert_equal(expected_deep_include_response, response) - end - - def test_render_with_empty_default_includes - with_default_includes '' do - get :render_without_include - response = JSON.parse(@response.body) - expected = { - 'author' => { - 'id' => 1, - 'name' => 'Steve K.' - } - } - assert_equal(expected, response) - end - end - - def test_render_with_recursive_default_includes - with_default_includes '**' do - get :render_without_recursive_relationships - response = JSON.parse(@response.body) - - expected = { - 'id' => 11, - 'name' => 'Jane Doe', - 'roles' => nil, - 'bio' => nil, - 'posts' => [ - { - 'id' => 12, - 'title' => 'Hello World', - 'body' => 'My first post', - 'comments' => [ - { - 'id' => 13, - 'body' => 'Commentary', - 'post' => nil, # not set to avoid infinite recursion - 'author' => nil, # not set to avoid infinite recursion - } - ], - 'blog' => { - 'id' => 999, - 'name' => 'Custom blog', - 'writer' => nil, - 'articles' => nil - }, - 'author' => nil # not set to avoid infinite recursion - } - ] - } - assert_equal(expected, response) - end - end - - def test_render_with_includes_overrides_default_includes - with_default_includes '' do - get :render_resource_with_include_hash - response = JSON.parse(@response.body) - - assert_equal(expected_include_response, response) - end - end - - private - - def expected_include_response - { - 'author' => { - 'id' => 1, - 'name' => 'Steve K.', - 'posts' => [ - { - 'id' => 42, 'title' => 'New Post', 'body' => 'Body', - 'comments' => [ - { - 'id' => 1, 'body' => 'ZOMG A COMMENT' - }, - { - 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT' - } - ] - } - ] - } - } - end - - def expected_deep_include_response - { - 'author' => { - 'id' => 1, - 'name' => 'Steve K.', - 'posts' => [ - { - 'id' => 42, 'title' => 'New Post', 'body' => 'Body', - 'comments' => [ - { - 'id' => 1, 'body' => 'ZOMG A COMMENT', - 'author' => { - 'id' => 1, - 'name' => 'Steve K.' - } - }, - { - 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT', - 'author' => { - 'id' => 1, - 'name' => 'Steve K.' - } - } - ] - } - ] - } - } - end - - def with_default_includes(include_directive) - original = ActiveModelSerializers.config.default_includes - ActiveModelSerializers.config.default_includes = include_directive - clear_include_directive_cache - yield - ensure - ActiveModelSerializers.config.default_includes = original - clear_include_directive_cache - end - - def clear_include_directive_cache - ActiveModelSerializers - .instance_variable_set(:@default_include_directive, nil) - end - end - end - end -end diff --git a/test/action_controller/json_api/deserialization_test.rb b/test/action_controller/json_api/deserialization_test.rb deleted file mode 100644 index 025f857b..00000000 --- a/test/action_controller/json_api/deserialization_test.rb +++ /dev/null @@ -1,112 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class JsonApi - class DeserializationTest < ActionController::TestCase - class DeserializationTestController < ActionController::Base - def render_parsed_payload - parsed_hash = ActiveModelSerializers::Deserialization.jsonapi_parse(params) - render json: parsed_hash - end - - def render_polymorphic_parsed_payload - parsed_hash = ActiveModelSerializers::Deserialization.jsonapi_parse( - params, - polymorphic: [:restriction_for, :restricted_to] - ) - render json: parsed_hash - end - end - - tests DeserializationTestController - - def test_deserialization_of_relationship_only_object - hash = { - 'data' => { - 'type' => 'restraints', - 'relationships' => { - 'restriction_for' => { - 'data' => { - 'type' => 'discounts', - 'id' => '67' - } - }, - 'restricted_to' => { - 'data' => nil - } - } - }, - 'restraint' => {} - } - - post :render_polymorphic_parsed_payload, params: hash - - response = JSON.parse(@response.body) - expected = { - 'restriction_for_id' => '67', - 'restriction_for_type' => 'discounts', - 'restricted_to_id' => nil, - 'restricted_to_type' => nil - } - - assert_equal(expected, response) - end - - def test_deserialization - hash = { - 'data' => { - 'type' => 'photos', - 'id' => 'zorglub', - 'attributes' => { - 'title' => 'Ember Hamster', - 'src' => 'http://example.com/images/productivity.png', - 'image-width' => '200', - 'imageHeight' => '200', - 'ImageSize' => '1024' - }, - 'relationships' => { - 'author' => { - 'data' => nil - }, - 'photographer' => { - 'data' => { 'type' => 'people', 'id' => '9' } - }, - 'comments' => { - 'data' => [ - { 'type' => 'comments', 'id' => '1' }, - { 'type' => 'comments', 'id' => '2' } - ] - }, - 'related-images' => { - 'data' => [ - { 'type' => 'image', 'id' => '7' }, - { 'type' => 'image', 'id' => '8' } - ] - } - } - } - } - - post :render_parsed_payload, params: hash - - response = JSON.parse(@response.body) - expected = { - 'id' => 'zorglub', - 'title' => 'Ember Hamster', - 'src' => 'http://example.com/images/productivity.png', - 'image_width' => '200', - 'image_height' => '200', - 'image_size' => '1024', - 'author_id' => nil, - 'photographer_id' => '9', - 'comment_ids' => %w(1 2), - 'related_image_ids' => %w(7 8) - } - - assert_equal(expected, response) - end - end - end - end -end diff --git a/test/action_controller/json_api/errors_test.rb b/test/action_controller/json_api/errors_test.rb deleted file mode 100644 index 6da3c9ad..00000000 --- a/test/action_controller/json_api/errors_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class JsonApi - class ErrorsTest < ActionController::TestCase - def test_active_model_with_multiple_errors - get :render_resource_with_errors - - expected_errors_object = { - 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 - assert_equal json_response_body.to_json, expected_errors_object - end - - def json_response_body - JSON.load(@response.body) - end - - class ErrorsTestController < ActionController::Base - def render_resource_with_errors - 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, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer - end - end - - tests ErrorsTestController - end - end - end -end diff --git a/test/action_controller/json_api/fields_test.rb b/test/action_controller/json_api/fields_test.rb deleted file mode 100644 index af87ad39..00000000 --- a/test/action_controller/json_api/fields_test.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class JsonApi - class FieldsTest < ActionController::TestCase - class FieldsTestController < ActionController::Base - class AuthorWithName < Author - attributes :first_name, :last_name - end - class AuthorWithNameSerializer < AuthorSerializer - type 'authors' - end - class PostWithPublishAt < Post - attributes :publish_at - end - class PostWithPublishAtSerializer < ActiveModel::Serializer - type 'posts' - attributes :title, :body, :publish_at - belongs_to :author - has_many :comments - end - - def setup_post - ActionController::Base.cache_store.clear - @author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones') - @comment1 = Comment.new(id: 7, body: 'cool', author: @author) - @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) - @post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1', - author: @author, comments: [@comment1, @comment2], - publish_at: '2020-03-16T03:55:25.291Z') - @comment1.post = @post - @comment2.post = @post - end - - def render_fields_works_on_relationships - setup_post - render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] } - end - end - - tests FieldsTestController - - test 'fields works on relationships' do - get :render_fields_works_on_relationships - response = JSON.parse(@response.body) - expected = { - 'data' => { - 'id' => '1337', - 'type' => 'posts', - 'relationships' => { - 'author' => { - 'data' => { - 'id' => '1', - 'type' => 'authors' - } - } - } - } - } - assert_equal expected, response - end - end - end - end -end diff --git a/test/action_controller/json_api/linked_test.rb b/test/action_controller/json_api/linked_test.rb deleted file mode 100644 index 12019768..00000000 --- a/test/action_controller/json_api/linked_test.rb +++ /dev/null @@ -1,202 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class JsonApi - class LinkedTest < ActionDispatch::IntegrationTest - class LinkedTestController < ActionController::Base - def setup_post - ActionController::Base.cache_store.clear - @role1 = Role.new(id: 1, name: 'admin') - @role2 = Role.new(id: 2, name: 'colab') - @author = Author.new(id: 1, name: 'Steve K.') - @author.posts = [] - @author.bio = nil - @author.roles = [@role1, @role2] - @role1.author = @author - @role2.author = @author - @author2 = Author.new(id: 2, name: 'Anonymous') - @author2.posts = [] - @author2.bio = nil - @author2.roles = [] - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @post.author = @author - @first_comment.post = @post - @first_comment.author = @author2 - @second_comment.post = @post - @second_comment.author = nil - @post2 = Post.new(id: 2, title: 'Another Post', body: 'Body') - @post2.author = @author - @post2.comments = [] - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @post2.blog = @blog - end - - def render_resource_without_include - setup_post - render json: @post - end - - def render_resource_with_include - setup_post - render json: @post, adapter: :json_api, include: [:author] - end - - def render_resource_with_include_of_custom_key_by_original - setup_post - render json: @post, adapter: :json_api, include: [:reviews], serializer: PostWithCustomKeysSerializer - end - - def render_resource_with_nested_include - setup_post - render json: @post, adapter: :json_api, include: [comments: [:author]] - end - - def render_resource_with_nested_has_many_include_wildcard - setup_post - render json: @post, adapter: :json_api, include: 'author.*' - end - - def render_resource_with_missing_nested_has_many_include - setup_post - @post.author = @author2 # author2 has no roles. - render json: @post, adapter: :json_api, include: [author: [:roles]] - end - - def render_collection_with_missing_nested_has_many_include - setup_post - @post.author = @author2 - render json: [@post, @post2], adapter: :json_api, include: [author: [:roles]] - end - - def render_collection_without_include - setup_post - render json: [@post], adapter: :json_api - end - - def render_collection_with_include - setup_post - render json: [@post], adapter: :json_api, include: 'author, comments' - end - end - - setup do - @routes = Rails.application.routes.draw do - ActiveSupport::Deprecation.silence do - match ':action', to: LinkedTestController, via: [:get, :post] - end - end - end - - def test_render_resource_without_include - get '/render_resource_without_include' - response = JSON.parse(@response.body) - refute response.key? 'included' - end - - def test_render_resource_with_include - get '/render_resource_with_include' - response = JSON.parse(@response.body) - assert response.key? 'included' - assert_equal 1, response['included'].size - assert_equal 'Steve K.', response['included'].first['attributes']['name'] - end - - def test_render_resource_with_nested_has_many_include - get '/render_resource_with_nested_has_many_include_wildcard' - response = JSON.parse(@response.body) - expected_linked = [ - { - 'id' => '1', - 'type' => 'authors', - 'attributes' => { - 'name' => 'Steve K.' - }, - 'relationships' => { - 'posts' => { 'data' => [] }, - 'roles' => { 'data' => [{ 'type' => 'roles', 'id' => '1' }, { 'type' => 'roles', 'id' => '2' }] }, - 'bio' => { 'data' => nil } - } - }, { - 'id' => '1', - 'type' => 'roles', - 'attributes' => { - 'name' => 'admin', - 'description' => nil, - 'slug' => 'admin-1' - }, - 'relationships' => { - 'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } } - } - }, { - 'id' => '2', - 'type' => 'roles', - 'attributes' => { - 'name' => 'colab', - 'description' => nil, - 'slug' => 'colab-2' - }, - 'relationships' => { - 'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } } - } - } - ] - assert_equal expected_linked, response['included'] - end - - def test_render_resource_with_include_of_custom_key_by_original - get '/render_resource_with_include_of_custom_key_by_original' - response = JSON.parse(@response.body) - assert response.key? 'included' - - relationships = response['data']['relationships'] - - assert_includes relationships, 'reviews' - assert_includes relationships, 'writer' - assert_includes relationships, 'site' - end - - def test_render_resource_with_nested_include - get '/render_resource_with_nested_include' - response = JSON.parse(@response.body) - assert response.key? 'included' - assert_equal 3, response['included'].size - end - - def test_render_collection_without_include - get '/render_collection_without_include' - response = JSON.parse(@response.body) - refute response.key? 'included' - end - - def test_render_collection_with_include - get '/render_collection_with_include' - response = JSON.parse(@response.body) - assert response.key? 'included' - end - - def test_render_resource_with_nested_attributes_even_when_missing_associations - get '/render_resource_with_missing_nested_has_many_include' - response = JSON.parse(@response.body) - assert response.key? 'included' - refute include_type?(response['included'], 'roles') - end - - def test_render_collection_with_missing_nested_has_many_include - get '/render_collection_with_missing_nested_has_many_include' - response = JSON.parse(@response.body) - assert response.key? 'included' - assert include_type?(response['included'], 'roles') - end - - def include_type?(collection, value) - collection.detect { |i| i['type'] == value } - end - end - end - end -end diff --git a/test/action_controller/json_api/pagination_test.rb b/test/action_controller/json_api/pagination_test.rb deleted file mode 100644 index 0af086b7..00000000 --- a/test/action_controller/json_api/pagination_test.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'test_helper' -require 'will_paginate/array' -require 'kaminari' -require 'kaminari/hooks' -::Kaminari::Hooks.init - -module ActionController - module Serialization - class JsonApi - class PaginationTest < ActionController::TestCase - KAMINARI_URI = 'http://test.host/action_controller/serialization/json_api/pagination_test/pagination_test/render_pagination_using_kaminari'.freeze - WILL_PAGINATE_URI = 'http://test.host/action_controller/serialization/json_api/pagination_test/pagination_test/render_pagination_using_will_paginate'.freeze - - class PaginationTestController < ActionController::Base - def setup - @array = [ - Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), - Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), - Profile.new(name: 'Name 3', description: 'Description 3', comments: 'Comments 3') - ] - end - - def using_kaminari - setup - Kaminari.paginate_array(@array).page(params[:page][:number]).per(params[:page][:size]) - end - - def using_will_paginate - setup - @array.paginate(page: params[:page][:number], per_page: params[:page][:size]) - end - - def render_pagination_using_kaminari - render json: using_kaminari, adapter: :json_api - end - - def render_pagination_using_will_paginate - render json: using_will_paginate, adapter: :json_api - end - - def render_array_without_pagination_links - setup - render json: @array, adapter: :json_api - end - end - - tests PaginationTestController - - def test_render_pagination_links_with_will_paginate - expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", - 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - 'prev' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", - 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } - - get :render_pagination_using_will_paginate, params: { page: { number: 2, size: 1 } } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_render_only_last_and_next_pagination_links - expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", - 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2", - 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2" } - get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 } } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_render_pagination_links_with_kaminari - expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", - 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - 'next' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", - 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } - get :render_pagination_using_kaminari, params: { page: { number: 2, size: 1 } } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_render_only_prev_and_first_pagination_links - expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", - 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1" } - get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 } } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_render_only_last_and_next_pagination_links_with_additional_params - expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2&teste=additional", - 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional", - 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional" } - get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 }, teste: 'additional' } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_render_only_prev_and_first_pagination_links_with_additional_params - expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1&teste=additional", - 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1&teste=additional", - 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1&teste=additional" } - get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 }, teste: 'additional' } - response = JSON.parse(@response.body) - assert_equal expected_links, response['links'] - end - - def test_array_without_pagination_links - get :render_array_without_pagination_links, params: { page: { number: 2, size: 1 } } - response = JSON.parse(@response.body) - refute response.key? 'links' - end - end - end - end -end diff --git a/test/action_controller/json_api/transform_test.rb b/test/action_controller/json_api/transform_test.rb deleted file mode 100644 index 69212f32..00000000 --- a/test/action_controller/json_api/transform_test.rb +++ /dev/null @@ -1,189 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class JsonApi - class KeyTransformTest < ActionController::TestCase - class KeyTransformTestController < ActionController::Base - class Post < ::Model - attributes :title, :body, :publish_at - associations :author, :top_comments - end - class Author < ::Model - attributes :first_name, :last_name - end - class TopComment < ::Model - attributes :body - associations :author, :post - end - class PostSerializer < ActiveModel::Serializer - type 'posts' - attributes :title, :body, :publish_at - belongs_to :author - has_many :top_comments - - link(:post_authors) { 'https://example.com/post_authors' } - - meta do - { - rating: 5, - favorite_count: 10 - } - end - end - - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - attributes :first_name, :last_name - end - - class TopCommentSerializer < ActiveModel::Serializer - type 'top_comments' - attributes :body - belongs_to :author - end - - def setup_post - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') - @comment1 = TopComment.new(id: 7, body: 'cool', author: @author) - @comment2 = TopComment.new(id: 12, body: 'awesome', author: @author) - @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', - author: @author, top_comments: [@comment1, @comment2], - publish_at: '2020-03-16T03:55:25.291Z') - @comment1.post = @post - @comment2.post = @post - end - - def render_resource_with_transform - setup_post - render json: @post, serializer: PostSerializer, adapter: :json_api, - key_transform: :camel - end - - def render_resource_with_transform_nil - setup_post - render json: @post, serializer: PostSerializer, adapter: :json_api, - key_transform: nil - end - - def render_resource_with_transform_with_global_config - old_transform = ActiveModelSerializers.config.key_transform - setup_post - ActiveModelSerializers.config.key_transform = :camel_lower - render json: @post, serializer: PostSerializer, adapter: :json_api - ensure - ActiveModelSerializers.config.key_transform = old_transform - end - end - - tests KeyTransformTestController - - def test_render_resource_with_transform - get :render_resource_with_transform - response = JSON.parse(@response.body) - expected = { - 'Data' => { - 'Id' => '1337', - 'Type' => 'Posts', - 'Attributes' => { - 'Title' => 'Title 1', - 'Body' => 'Body 1', - 'PublishAt' => '2020-03-16T03:55:25.291Z' - }, - 'Relationships' => { - 'Author' => { - 'Data' => { - 'Id' => '1', - 'Type' => 'Authors' - } - }, - 'TopComments' => { - 'Data' => [ - { 'Id' => '7', 'Type' => 'TopComments' }, - { 'Id' => '12', 'Type' => 'TopComments' } - ] - } - }, - 'Links' => { - 'PostAuthors' => 'https://example.com/post_authors' - }, - 'Meta' => { 'Rating' => 5, 'FavoriteCount' => 10 } - } - } - assert_equal expected, response - end - - def test_render_resource_with_transform_nil - get :render_resource_with_transform_nil - response = JSON.parse(@response.body) - expected = { - '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' - } - }, - 'top-comments' => { - 'data' => [ - { 'id' => '7', 'type' => 'top-comments' }, - { 'id' => '12', 'type' => 'top-comments' } - ] - } - }, - 'links' => { - 'post-authors' => 'https://example.com/post_authors' - }, - 'meta' => { 'rating' => 5, 'favorite-count' => 10 } - } - } - assert_equal expected, response - end - - def test_render_resource_with_transform_with_global_config - get :render_resource_with_transform_with_global_config - response = JSON.parse(@response.body) - expected = { - 'data' => { - 'id' => '1337', - 'type' => 'posts', - 'attributes' => { - 'title' => 'Title 1', - 'body' => 'Body 1', - 'publishAt' => '2020-03-16T03:55:25.291Z' - }, - 'relationships' => { - 'author' => { - 'data' => { - 'id' => '1', - 'type' => 'authors' - } - }, - 'topComments' => { - 'data' => [ - { 'id' => '7', 'type' => 'topComments' }, - { 'id' => '12', 'type' => 'topComments' } - ] - } - }, - 'links' => { - 'postAuthors' => 'https://example.com/post_authors' - }, - 'meta' => { 'rating' => 5, 'favoriteCount' => 10 } - } - } - assert_equal expected, response - end - end - end - end -end diff --git a/test/action_controller/lookup_proc_test.rb b/test/action_controller/lookup_proc_test.rb deleted file mode 100644 index 4d2ad0b1..00000000 --- a/test/action_controller/lookup_proc_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class LookupProcTest < ActionController::TestCase - module Api - module V3 - class PostCustomSerializer < ActiveModel::Serializer - attributes :title, :body - - belongs_to :author - end - - class AuthorCustomSerializer < ActiveModel::Serializer - attributes :name - end - - class LookupProcTestController < ActionController::Base - def implicit_namespaced_serializer - author = Author.new(name: 'Bob') - post = Post.new(title: 'New Post', body: 'Body', author: author) - - render json: post - end - end - end - end - - tests Api::V3::LookupProcTestController - - test 'implicitly uses namespaced serializer' do - controller_namespace = lambda do |resource_class, _parent_serializer_class, namespace| - "#{namespace}::#{resource_class}CustomSerializer" if namespace - end - - with_prepended_lookup(controller_namespace) do - get :implicit_namespaced_serializer - - assert_serializer Api::V3::PostCustomSerializer - - expected = { 'title' => 'New Post', 'body' => 'Body', 'author' => { 'name' => 'Bob' } } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - end - end - end -end diff --git a/test/action_controller/namespace_lookup_test.rb b/test/action_controller/namespace_lookup_test.rb deleted file mode 100644 index b5c8f496..00000000 --- a/test/action_controller/namespace_lookup_test.rb +++ /dev/null @@ -1,232 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class NamespaceLookupTest < ActionController::TestCase - class Book < ::Model - attributes :id, :title, :body - associations :writer, :chapters - end - class Chapter < ::Model - attributes :title - end - class Writer < ::Model - attributes :name - end - - module Api - module V2 - class BookSerializer < ActiveModel::Serializer - attributes :title - end - end - - module VHeader - class BookSerializer < ActiveModel::Serializer - attributes :title, :body - - def body - 'header' - end - end - end - - module V3 - class BookSerializer < ActiveModel::Serializer - attributes :title, :body - - belongs_to :writer - has_many :chapters - end - - class ChapterSerializer < ActiveModel::Serializer - attribute :title do - "Chapter - #{object.title}" - end - end - - class WriterSerializer < ActiveModel::Serializer - attributes :name - end - - class LookupTestController < ActionController::Base - before_action only: [:namespace_set_in_before_filter] do - self.namespace_for_serializer = Api::V2 - end - - def implicit_namespaced_serializer - writer = Writer.new(name: 'Bob') - book = Book.new(title: 'New Post', body: 'Body', writer: writer, chapters: []) - - render json: book - end - - def implicit_namespaced_collection_serializer - chapter1 = Chapter.new(title: 'Oh') - chapter2 = Chapter.new(title: 'Oh my') - - render json: [chapter1, chapter2] - end - - def implicit_has_many_namespaced_serializer - chapter1 = Chapter.new(title: 'Odd World') - chapter2 = Chapter.new(title: 'New World') - book = Book.new(title: 'New Post', body: 'Body', chapters: [chapter1, chapter2]) - - render json: book - end - - def explicit_namespace_as_module - book = Book.new(title: 'New Post', body: 'Body') - - render json: book, namespace: Api::V2 - end - - def explicit_namespace_as_string - book = Book.new(title: 'New Post', body: 'Body') - - # because this is a string, ruby can't auto-lookup the constant, so otherwise - # the lookup thinks we mean ::Api::V2 - render json: book, namespace: 'ActionController::Serialization::NamespaceLookupTest::Api::V2' - end - - def explicit_namespace_as_symbol - book = Book.new(title: 'New Post', body: 'Body') - - # because this is a string, ruby can't auto-lookup the constant, so otherwise - # the lookup thinks we mean ::Api::V2 - render json: book, namespace: :'ActionController::Serialization::NamespaceLookupTest::Api::V2' - end - - def invalid_namespace - book = Book.new(id: 'invalid_namespace_book_id', title: 'New Post', body: 'Body') - - render json: book, namespace: :api_v2 - end - - def namespace_set_in_before_filter - book = Book.new(title: 'New Post', body: 'Body') - render json: book - end - - def namespace_set_by_request_headers - book = Book.new(title: 'New Post', body: 'Body') - version_from_header = request.headers['X-API_VERSION'] - namespace = "ActionController::Serialization::NamespaceLookupTest::#{version_from_header}" - - render json: book, namespace: namespace - end - end - end - end - - tests Api::V3::LookupTestController - - setup do - @test_namespace = self.class.parent - end - - test 'uses request headers to determine the namespace' do - request.env['X-API_VERSION'] = 'Api::VHeader' - get :namespace_set_by_request_headers - - assert_serializer Api::VHeader::BookSerializer - end - - test 'implicitly uses namespaced serializer' do - get :implicit_namespaced_serializer - - assert_serializer Api::V3::BookSerializer - - expected = { 'title' => 'New Post', 'body' => 'Body', 'writer' => { 'name' => 'Bob' }, 'chapters' => [] } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'implicitly uses namespaced serializer for collection' do - get :implicit_namespaced_collection_serializer - - assert_serializer 'ActiveModel::Serializer::CollectionSerializer' - - expected = [{ 'title' => 'Chapter - Oh' }, { 'title' => 'Chapter - Oh my' }] - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'implicitly uses namespaced serializer for has_many' do - get :implicit_has_many_namespaced_serializer - - assert_serializer Api::V3::BookSerializer - - expected = { - 'title' => 'New Post', - 'body' => 'Body', 'writer' => nil, - 'chapters' => [ - { 'title' => 'Chapter - Odd World' }, - { 'title' => 'Chapter - New World' } - ] - } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'explicit namespace as module' do - get :explicit_namespace_as_module - - assert_serializer Api::V2::BookSerializer - - expected = { 'title' => 'New Post' } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'explicit namespace as string' do - get :explicit_namespace_as_string - - assert_serializer Api::V2::BookSerializer - - expected = { 'title' => 'New Post' } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'explicit namespace as symbol' do - get :explicit_namespace_as_symbol - - assert_serializer Api::V2::BookSerializer - - expected = { 'title' => 'New Post' } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'invalid namespace' do - get :invalid_namespace - - assert_serializer ActiveModel::Serializer::Null - - expected = { 'id' => 'invalid_namespace_book_id', 'title' => 'New Post', 'body' => 'Body' } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - - test 'namespace set in before filter' do - get :namespace_set_in_before_filter - - assert_serializer Api::V2::BookSerializer - - expected = { 'title' => 'New Post' } - actual = JSON.parse(@response.body) - - assert_equal expected, actual - end - end - end -end diff --git a/test/action_controller/serialization_scope_name_test.rb b/test/action_controller/serialization_scope_name_test.rb deleted file mode 100644 index 3d767d04..00000000 --- a/test/action_controller/serialization_scope_name_test.rb +++ /dev/null @@ -1,235 +0,0 @@ -require 'test_helper' - -module SerializationScopeTesting - class User < ActiveModelSerializers::Model - attributes :id, :name, :admin - def admin? - admin - end - end - class Comment < ActiveModelSerializers::Model - attributes :id, :body - end - class Post < ActiveModelSerializers::Model - attributes :id, :title, :body, :comments - end - class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body, :comments - - def body - "The 'scope' is the 'current_user': #{scope == current_user}" - end - - def comments - if current_user.admin? - [Comment.new(id: 1, body: 'Admin')] - else - [Comment.new(id: 2, body: 'Scoped')] - end - end - - def json_key - 'post' - end - end - class PostTestController < ActionController::Base - attr_writer :current_user - - def render_post_by_non_admin - self.current_user = User.new(id: 3, name: 'Pete', admin: false) - render json: new_post, serializer: serializer, adapter: :json - end - - def render_post_by_admin - self.current_user = User.new(id: 3, name: 'Pete', admin: true) - render json: new_post, serializer: serializer, adapter: :json - end - - def current_user - defined?(@current_user) ? @current_user : :current_user_not_set - end - - private - - def new_post - Post.new(id: 4, title: 'Title') - end - - def serializer - PostSerializer - end - end - class PostViewContextSerializer < PostSerializer - def body - "The 'scope' is the 'view_context': #{scope == view_context}" - end - - def comments - if view_context.controller.current_user.admin? - [Comment.new(id: 1, body: 'Admin')] - else - [Comment.new(id: 2, body: 'Scoped')] - end - end - end - class DefaultScopeTest < ActionController::TestCase - tests PostTestController - - def test_default_serialization_scope - assert_equal :current_user, @controller._serialization_scope - end - - def test_default_serialization_scope_object - assert_equal :current_user_not_set, @controller.current_user - assert_equal :current_user_not_set, @controller.serialization_scope - end - - def test_default_scope_non_admin - get :render_post_by_non_admin - expected_json = { - post: { - id: 4, - title: 'Title', - body: "The 'scope' is the 'current_user': true", - comments: [ - { id: 2, body: 'Scoped' } - ] - } - }.to_json - assert_equal expected_json, @response.body - end - - def test_default_scope_admin - get :render_post_by_admin - expected_json = { - post: { - id: 4, - title: 'Title', - body: "The 'scope' is the 'current_user': true", - comments: [ - { id: 1, body: 'Admin' } - ] - } - }.to_json - assert_equal expected_json, @response.body - end - end - class SerializationScopeTest < ActionController::TestCase - class PostViewContextTestController < PostTestController - serialization_scope :view_context - - private - - def serializer - PostViewContextSerializer - end - end - tests PostViewContextTestController - - def test_defined_serialization_scope - assert_equal :view_context, @controller._serialization_scope - end - - def test_defined_serialization_scope_object - assert_equal @controller.view_context.controller, @controller.serialization_scope.controller - end - - def test_serialization_scope_non_admin - get :render_post_by_non_admin - expected_json = { - post: { - id: 4, - title: 'Title', - body: "The 'scope' is the 'view_context': true", - comments: [ - { id: 2, body: 'Scoped' } - ] - } - }.to_json - assert_equal expected_json, @response.body - end - - def test_serialization_scope_admin - get :render_post_by_admin - expected_json = { - post: { - id: 4, - title: 'Title', - body: "The 'scope' is the 'view_context': true", - comments: [ - { id: 1, body: 'Admin' } - ] - } - }.to_json - assert_equal expected_json, @response.body - end - end - class NilSerializationScopeTest < ActionController::TestCase - class PostViewContextTestController < ActionController::Base - serialization_scope nil - - attr_accessor :current_user - - def render_post_with_no_scope - self.current_user = User.new(id: 3, name: 'Pete', admin: false) - render json: new_post, serializer: PostSerializer, adapter: :json - end - - def render_post_with_passed_in_scope - self.current_user = User.new(id: 3, name: 'Pete', admin: false) - render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user, scope_name: :current_user - end - - def render_post_with_passed_in_scope_without_scope_name - self.current_user = User.new(id: 3, name: 'Pete', admin: false) - render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user - end - - private - - def new_post - Post.new(id: 4, title: 'Title') - end - end - tests PostViewContextTestController - - def test_nil_serialization_scope - assert_nil @controller._serialization_scope - end - - def test_nil_serialization_scope_object - assert_nil @controller.serialization_scope - end - - def test_nil_scope - exception_matcher = /current_user/ - exception = assert_raises(NameError) do - get :render_post_with_no_scope - end - assert_match exception_matcher, exception.message - end - - def test_serialization_scope_is_and_nil_scope_passed_in_current_user - get :render_post_with_passed_in_scope - expected_json = { - post: { - id: 4, - title: 'Title', - body: "The 'scope' is the 'current_user': true", - comments: [ - { id: 2, body: 'Scoped' } - ] - } - }.to_json - assert_equal expected_json, @response.body - end - - def test_serialization_scope_is_nil_and_scope_passed_in_current_user_without_scope_name - exception_matcher = /current_user/ - exception = assert_raises(NameError) do - get :render_post_with_passed_in_scope_without_scope_name - end - assert_match exception_matcher, exception.message - end - end -end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb deleted file mode 100644 index dfd72b42..00000000 --- a/test/action_controller/serialization_test.rb +++ /dev/null @@ -1,472 +0,0 @@ -require 'test_helper' - -module ActionController - module Serialization - class ImplicitSerializerTest < ActionController::TestCase - class ImplicitSerializationTestController < ActionController::Base - include SerializationTesting - def render_using_implicit_serializer - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile - end - - def render_using_default_adapter_root - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile - end - - def render_array_using_custom_root - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: [@profile], root: 'custom_root' - end - - def render_array_that_is_empty_using_custom_root - render json: [], root: 'custom_root' - end - - def render_object_using_custom_root - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - render json: @profile, root: 'custom_root' - end - - def render_array_using_implicit_serializer - array = [ - Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), - Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2') - ] - render json: array - end - - def render_array_using_implicit_serializer_and_meta - @profiles = [ - Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - ] - render json: @profiles, meta: { total: 10 } - end - - def render_array_using_implicit_serializer_and_links - with_adapter ActiveModelSerializers::Adapter::JsonApi do - @profiles = [ - Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - ] - - render json: @profiles, links: { self: 'http://example.com/api/profiles/1' } - end - end - - def render_object_with_cache_enabled - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @author = Author.new(id: 1, name: 'Joao Moura.') - @post = Post.new(id: 1, title: 'New Post', body: 'Body', comments: [@comment], author: @author) - - generate_cached_serializer(@post) - - @post.title = 'ZOMG a New Post' - render json: @post - end - - def render_json_object_without_serializer - render json: { error: 'Result is Invalid' } - end - - def render_json_array_object_without_serializer - render json: [{ error: 'Result is Invalid' }] - end - - def update_and_render_object_with_cache_enabled - @post.updated_at = Time.zone.now - - generate_cached_serializer(@post) - render json: @post - end - - def render_object_expired_with_cache_enabled - comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - author = Author.new(id: 1, name: 'Joao Moura.') - post = Post.new(id: 1, title: 'New Post', body: 'Body', comments: [comment], author: author) - - generate_cached_serializer(post) - - post.title = 'ZOMG a New Post' - - expires_in = [ - PostSerializer._cache_options[:expires_in], - CommentSerializer._cache_options[:expires_in] - ].max + 200 - - Timecop.travel(Time.zone.now + expires_in) do - render json: post - end - end - - def render_changed_object_with_cache_enabled - comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - author = Author.new(id: 1, name: 'Joao Moura.') - post = Post.new(id: 1, title: 'ZOMG a New Post', body: 'Body', comments: [comment], author: author) - - render json: post - end - - def render_fragment_changed_object_with_only_cache_enabled - author = Author.new(id: 1, name: 'Joao Moura.') - role = Role.new(id: 42, name: 'ZOMG A ROLE', description: 'DESCRIPTION HERE', author: author) - - generate_cached_serializer(role) - role.name = 'lol' - role.description = 'HUEHUEBRBR' - - render json: role - end - - def render_fragment_changed_object_with_except_cache_enabled - author = Author.new(id: 1, name: 'Joao Moura.') - bio = Bio.new(id: 42, content: 'ZOMG A ROLE', rating: 5, author: author) - - generate_cached_serializer(bio) - bio.content = 'lol' - bio.rating = 0 - - render json: bio - end - - def render_fragment_changed_object_with_relationship - comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - comment2 = Comment.new(id: 1, body: 'ZOMG AN UPDATED-BUT-NOT-CACHE-EXPIRED COMMENT') - like = Like.new(id: 1, likeable: comment, time: 3.days.ago) - - generate_cached_serializer(like) - like.likeable = comment2 - like.time = Time.zone.now.to_s - - render json: like - end - end - - tests ImplicitSerializationTestController - - # We just have Null for now, this will change - def test_render_using_implicit_serializer - get :render_using_implicit_serializer - - expected = { - name: 'Name 1', - description: 'Description 1' - } - - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_using_default_root - with_adapter :json_api do - get :render_using_default_adapter_root - end - expected = { - data: { - id: @controller.instance_variable_get(:@profile).id.to_s, - type: 'profiles', - attributes: { - name: 'Name 1', - description: 'Description 1' - } - } - } - - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_array_using_custom_root - with_adapter :json do - get :render_array_using_custom_root - end - expected = { custom_root: [{ name: 'Name 1', description: 'Description 1' }] } - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_array_that_is_empty_using_custom_root - with_adapter :json do - get :render_array_that_is_empty_using_custom_root - end - - expected = { custom_root: [] } - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_object_using_custom_root - with_adapter :json do - get :render_object_using_custom_root - end - - expected = { custom_root: { name: 'Name 1', description: 'Description 1' } } - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_json_object_without_serializer - get :render_json_object_without_serializer - - assert_equal 'application/json', @response.content_type - expected_body = { error: 'Result is Invalid' } - assert_equal expected_body.to_json, @response.body - end - - def test_render_json_array_object_without_serializer - get :render_json_array_object_without_serializer - - assert_equal 'application/json', @response.content_type - expected_body = [{ error: 'Result is Invalid' }] - assert_equal expected_body.to_json, @response.body - end - - def test_render_array_using_implicit_serializer - get :render_array_using_implicit_serializer - assert_equal 'application/json', @response.content_type - - expected = [ - { - name: 'Name 1', - description: 'Description 1' - }, - { - name: 'Name 2', - description: 'Description 2' - } - ] - - assert_equal expected.to_json, @response.body - end - - def test_render_array_using_implicit_serializer_and_meta - with_adapter :json_api do - get :render_array_using_implicit_serializer_and_meta - end - expected = { - data: [ - { - id: @controller.instance_variable_get(:@profiles).first.id.to_s, - type: 'profiles', - attributes: { - name: 'Name 1', - description: 'Description 1' - } - } - ], - meta: { - total: 10 - } - } - - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_array_using_implicit_serializer_and_links - get :render_array_using_implicit_serializer_and_links - - expected = { - data: [ - { - id: @controller.instance_variable_get(:@profiles).first.id.to_s, - type: 'profiles', - attributes: { - name: 'Name 1', - description: 'Description 1' - } - } - ], - links: { - self: 'http://example.com/api/profiles/1' - } - } - - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - end - - def test_render_with_cache_enable - expected = { - id: 1, - title: 'New Post', - body: 'Body', - comments: [ - { - id: 1, - body: 'ZOMG A COMMENT' - } - ], - blog: { - id: 999, - name: 'Custom blog' - }, - author: { - id: 1, - name: 'Joao Moura.' - } - } - - ActionController::Base.cache_store.clear - Timecop.freeze(Time.zone.now) do - get :render_object_with_cache_enabled - - assert_equal 'application/json', @response.content_type - assert_equal expected.to_json, @response.body - - get :render_changed_object_with_cache_enabled - assert_equal expected.to_json, @response.body - end - - ActionController::Base.cache_store.clear - get :render_changed_object_with_cache_enabled - assert_not_equal expected.to_json, @response.body - end - - def test_render_with_cache_enable_and_expired - ActionController::Base.cache_store.clear - get :render_object_expired_with_cache_enabled - - expected = { - id: 1, - title: 'ZOMG a New Post', - body: 'Body', - comments: [ - { - id: 1, - body: 'ZOMG A COMMENT' - } - ], - blog: { - id: 999, - name: 'Custom blog' - }, - author: { - id: 1, - name: 'Joao Moura.' - } - } - - assert_equal 'application/json', @response.content_type - actual = @response.body - expected = expected.to_json - if ENV['APPVEYOR'] && actual != expected - skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') - else - assert_equal actual, expected - end - end - - def test_render_with_fragment_only_cache_enable - ActionController::Base.cache_store.clear - get :render_fragment_changed_object_with_only_cache_enabled - response = JSON.parse(@response.body) - - assert_equal 'application/json', @response.content_type - assert_equal 'ZOMG A ROLE', response['name'] - assert_equal 'HUEHUEBRBR', response['description'] - end - - def test_render_with_fragment_except_cache_enable - ActionController::Base.cache_store.clear - get :render_fragment_changed_object_with_except_cache_enabled - response = JSON.parse(@response.body) - - assert_equal 'application/json', @response.content_type - assert_equal 5, response['rating'] - assert_equal 'lol', response['content'] - end - - def test_render_fragment_changed_object_with_relationship - ActionController::Base.cache_store.clear - - Timecop.freeze(Time.zone.now) do - get :render_fragment_changed_object_with_relationship - response = JSON.parse(@response.body) - - expected_return = { - 'id' => 1, - 'time' => Time.zone.now.to_s, - 'likeable' => { - 'id' => 1, - 'body' => 'ZOMG A COMMENT' - } - } - - assert_equal 'application/json', @response.content_type - assert_equal expected_return, response - end - end - - def test_cache_expiration_on_update - ActionController::Base.cache_store.clear - get :render_object_with_cache_enabled - - expected = { - id: 1, - title: 'ZOMG a New Post', - body: 'Body', - comments: [ - { - id: 1, - body: 'ZOMG A COMMENT' - } - ], - blog: { - id: 999, - name: 'Custom blog' - }, - author: { - id: 1, - name: 'Joao Moura.' - } - } - - get :update_and_render_object_with_cache_enabled - - assert_equal 'application/json', @response.content_type - actual = @response.body - expected = expected.to_json - if ENV['APPVEYOR'] && actual != expected - skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') - else - assert_equal actual, expected - end - end - - def test_warn_overridding_use_adapter_as_falsy_on_controller_instance - controller = Class.new(ImplicitSerializationTestController) do - def use_adapter? - false - end - end.new - assert_output(nil, /adapter: false/) do - controller.get_serializer(Profile.new) - end - end - - def test_dont_warn_overridding_use_adapter_as_truthy_on_controller_instance - controller = Class.new(ImplicitSerializationTestController) do - def use_adapter? - true - end - end.new - assert_output(nil, '') do - controller.get_serializer(Profile.new) - end - end - - def test_render_event_is_emitted - subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |name| - @name = name - end - - get :render_using_implicit_serializer - - assert_equal 'render.active_model_serializers', @name - ensure - ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber - end - end - end -end diff --git a/test/active_model_serializers/adapter_for_test.rb b/test/active_model_serializers/adapter_for_test.rb deleted file mode 100644 index 1439b987..00000000 --- a/test/active_model_serializers/adapter_for_test.rb +++ /dev/null @@ -1,208 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class AdapterForTest < ::ActiveSupport::TestCase - UnknownAdapterError = ::ActiveModelSerializers::Adapter::UnknownAdapterError - - def test_serializer_adapter_returns_configured_adapter - assert_output(nil, /ActiveModelSerializers::Adapter.configured_adapter/) do - assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModel::Serializer.adapter - end - end - - def test_returns_default_adapter - with_adapter_config_setup do - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::Attributes, adapter - end - end - - def test_overwrite_adapter_with_symbol - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = :null - - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::Null, adapter - end - end - - def test_overwrite_adapter_with_camelcased_symbol - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = :JsonApi - - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter - end - end - - def test_overwrite_adapter_with_string - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = 'json_api' - - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter - end - end - - def test_overwrite_adapter_with_a_camelcased_string - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = 'JsonApi' - - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter - end - end - - def test_overwrite_adapter_with_class - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::Null - - adapter = ActiveModelSerializers::Adapter.configured_adapter - assert_equal ActiveModelSerializers::Adapter::Null, adapter - end - end - - def test_raises_exception_if_invalid_symbol_given - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = :unknown - - assert_raises UnknownAdapterError do - ActiveModelSerializers::Adapter.configured_adapter - end - end - end - - def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter - with_adapter_config_setup do - ActiveModelSerializers.config.adapter = 42 - - assert_raises UnknownAdapterError do - ActiveModelSerializers::Adapter.configured_adapter - end - end - end - - def test_adapter_class_for_known_adapter - klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) - assert_equal ActiveModelSerializers::Adapter::JsonApi, klass - end - - def test_adapter_class_for_unknown_adapter - assert_raises UnknownAdapterError do - ActiveModelSerializers::Adapter.adapter_class(:json_simple) - end - end - - def test_adapter_map - expected_adapter_map = { - 'null'.freeze => ActiveModelSerializers::Adapter::Null, - 'json'.freeze => ActiveModelSerializers::Adapter::Json, - 'attributes'.freeze => ActiveModelSerializers::Adapter::Attributes, - 'json_api'.freeze => ActiveModelSerializers::Adapter::JsonApi - } - actual = ActiveModelSerializers::Adapter.adapter_map - assert_equal actual, expected_adapter_map - end - - def test_adapters - assert_equal ActiveModelSerializers::Adapter.adapters.sort, [ - 'attributes'.freeze, - 'json'.freeze, - 'json_api'.freeze, - 'null'.freeze - ] - end - - def test_lookup_adapter_by_string_name - assert_equal ActiveModelSerializers::Adapter.lookup('json'.freeze), ActiveModelSerializers::Adapter::Json - end - - def test_lookup_adapter_by_symbol_name - assert_equal ActiveModelSerializers::Adapter.lookup(:json), ActiveModelSerializers::Adapter::Json - end - - def test_lookup_adapter_by_class - klass = ActiveModelSerializers::Adapter::Json - assert_equal ActiveModelSerializers::Adapter.lookup(klass), klass - end - - def test_lookup_adapter_from_environment_registers_adapter - ActiveModelSerializers::Adapter.const_set(:AdapterFromEnvironment, Class.new) - klass = ::ActiveModelSerializers::Adapter::AdapterFromEnvironment - name = 'adapter_from_environment'.freeze - assert_equal ActiveModelSerializers::Adapter.lookup(name), klass - assert ActiveModelSerializers::Adapter.adapters.include?(name) - ensure - ActiveModelSerializers::Adapter.adapter_map.delete(name) - ActiveModelSerializers::Adapter.send(:remove_const, :AdapterFromEnvironment) - end - - def test_lookup_adapter_for_unknown_name - assert_raises UnknownAdapterError do - ActiveModelSerializers::Adapter.lookup(:json_simple) - end - end - - def test_adapter - assert_equal ActiveModelSerializers.config.adapter, :attributes - assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModelSerializers::Adapter::Attributes - end - - def test_register_adapter - new_adapter_name = :foo - new_adapter_klass = Class.new - ActiveModelSerializers::Adapter.register(new_adapter_name, new_adapter_klass) - assert ActiveModelSerializers::Adapter.adapters.include?('foo'.freeze) - assert ActiveModelSerializers::Adapter.lookup(:foo), new_adapter_klass - ensure - ActiveModelSerializers::Adapter.adapter_map.delete(new_adapter_name.to_s) - end - - def test_inherited_adapter_hooks_register_adapter - Object.const_set(:MyAdapter, Class.new) - my_adapter = MyAdapter - ActiveModelSerializers::Adapter::Base.inherited(my_adapter) - assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter - ensure - ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) - Object.send(:remove_const, :MyAdapter) - end - - def test_inherited_adapter_hooks_register_namespaced_adapter - Object.const_set(:MyNamespace, Module.new) - MyNamespace.const_set(:MyAdapter, Class.new) - my_adapter = MyNamespace::MyAdapter - ActiveModelSerializers::Adapter::Base.inherited(my_adapter) - assert_equal ActiveModelSerializers::Adapter.lookup(:'my_namespace/my_adapter'), my_adapter - ensure - ActiveModelSerializers::Adapter.adapter_map.delete('my_namespace/my_adapter'.freeze) - MyNamespace.send(:remove_const, :MyAdapter) - Object.send(:remove_const, :MyNamespace) - end - - def test_inherited_adapter_hooks_register_subclass_of_registered_adapter - Object.const_set(:MyAdapter, Class.new) - my_adapter = MyAdapter - Object.const_set(:MySubclassedAdapter, Class.new(MyAdapter)) - my_subclassed_adapter = MySubclassedAdapter - ActiveModelSerializers::Adapter::Base.inherited(my_adapter) - ActiveModelSerializers::Adapter::Base.inherited(my_subclassed_adapter) - assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter - assert_equal ActiveModelSerializers::Adapter.lookup(:my_subclassed_adapter), my_subclassed_adapter - ensure - ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) - ActiveModelSerializers::Adapter.adapter_map.delete('my_subclassed_adapter'.freeze) - Object.send(:remove_const, :MyAdapter) - Object.send(:remove_const, :MySubclassedAdapter) - end - - private - - def with_adapter_config_setup - previous_adapter = ActiveModelSerializers.config.adapter - yield - ensure - ActiveModelSerializers.config.adapter = previous_adapter - end - end -end diff --git a/test/active_model_serializers/json_pointer_test.rb b/test/active_model_serializers/json_pointer_test.rb deleted file mode 100644 index 60619ee6..00000000 --- a/test/active_model_serializers/json_pointer_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class JsonPointerTest < ActiveSupport::TestCase - def test_attribute_pointer - attribute_name = 'title' - pointer = ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name) - assert_equal '/data/attributes/title', pointer - end - - def test_primary_data_pointer - pointer = ActiveModelSerializers::JsonPointer.new(:primary_data) - assert_equal '/data', pointer - end - - def test_unknown_data_pointer - assert_raises(TypeError) do - ActiveModelSerializers::JsonPointer.new(:unknown) - end - end - end -end diff --git a/test/active_model_serializers/logging_test.rb b/test/active_model_serializers/logging_test.rb deleted file mode 100644 index 95e61682..00000000 --- a/test/active_model_serializers/logging_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class LoggingTest < ActiveSupport::TestCase - class TestLogger < ActiveSupport::Logger - def initialize - @file = StringIO.new - super(@file) - end - - def messages - @file.rewind - @file.read - end - end - - def setup - @author = Author.new(name: 'Steve K.') - @post = Post.new(title: 'New Post', body: 'Body') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @comment.post = @post - @post.author = @author - @author.posts = [@post] - @post_serializer = PostSerializer.new(@post, custom_options: true) - - @old_logger = ActiveModelSerializers.logger - @logger = ActiveSupport::TaggedLogging.new(TestLogger.new) - logger @logger - end - - def teardown - logger @old_logger - end - - def logger(logger) - ActiveModelSerializers.logger = logger - end - - def test_uses_ams_as_tag - ActiveModelSerializers::SerializableResource.new(@post).serializable_hash - assert_match(/\[active_model_serializers\]/, @logger.messages) - end - - def test_logs_when_call_serializable_hash - ActiveModelSerializers::SerializableResource.new(@post).serializable_hash - assert_match(/Rendered/, @logger.messages) - end - - def test_logs_when_call_as_json - ActiveModelSerializers::SerializableResource.new(@post).as_json - assert_match(/Rendered/, @logger.messages) - end - - def test_logs_when_call_to_json - ActiveModelSerializers::SerializableResource.new(@post).to_json - assert_match(/Rendered/, @logger.messages) - end - - def test_logs_correct_serializer - ActiveModelSerializers::SerializableResource.new(@post).serializable_hash - assert_match(/PostSerializer/, @logger.messages) - end - - def test_logs_correct_adapter - ActiveModelSerializers::SerializableResource.new(@post).serializable_hash - assert_match(/ActiveModelSerializers::Adapter::Attributes/, @logger.messages) - end - - def test_logs_the_duration - ActiveModelSerializers::SerializableResource.new(@post).serializable_hash - assert_match(/\(\d+\.\d+ms\)/, @logger.messages) - end - end - end -end diff --git a/test/active_model_serializers/model_test.rb b/test/active_model_serializers/model_test.rb deleted file mode 100644 index 6a8a29af..00000000 --- a/test/active_model_serializers/model_test.rb +++ /dev/null @@ -1,142 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class ModelTest < ActiveSupport::TestCase - include ActiveModel::Serializer::Lint::Tests - - setup do - @resource = ActiveModelSerializers::Model.new - end - - def test_initialization_with_string_keys - klass = Class.new(ActiveModelSerializers::Model) do - attributes :key - end - value = 'value' - - model_instance = klass.new('key' => value) - - assert_equal model_instance.read_attribute_for_serialization(:key), value - end - - def test_attributes_can_be_read_for_serialization - klass = Class.new(ActiveModelSerializers::Model) do - attributes :one, :two, :three - end - original_attributes = { one: 1, two: 2, three: 3 } - original_instance = klass.new(original_attributes) - - # Initial value - instance = original_instance - expected_attributes = { one: 1, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal 1, instance.one - assert_equal 1, instance.read_attribute_for_serialization(:one) - - # FIXME: Change via accessor has no effect on attributes. - instance = original_instance.dup - instance.one = :not_one - assert_equal expected_attributes, instance.attributes - assert_equal :not_one, instance.one - assert_equal :not_one, instance.read_attribute_for_serialization(:one) - - # FIXME: Change via mutating attributes - instance = original_instance.dup - instance.attributes[:one] = :not_one - expected_attributes = { one: :not_one, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal 1, instance.one - assert_equal 1, instance.read_attribute_for_serialization(:one) - end - - def test_attributes_can_be_read_for_serialization_with_attributes_accessors_fix - klass = Class.new(ActiveModelSerializers::Model) do - derive_attributes_from_names_and_fix_accessors - attributes :one, :two, :three - end - original_attributes = { one: 1, two: 2, three: 3 } - original_instance = klass.new(original_attributes) - - # Initial value - instance = original_instance - expected_attributes = { one: 1, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal 1, instance.one - assert_equal 1, instance.read_attribute_for_serialization(:one) - - expected_attributes = { one: :not_one, two: 2, three: 3 }.with_indifferent_access - # Change via accessor - instance = original_instance.dup - instance.one = :not_one - assert_equal expected_attributes, instance.attributes - assert_equal :not_one, instance.one - assert_equal :not_one, instance.read_attribute_for_serialization(:one) - - # Attributes frozen - assert instance.attributes.frozen? - end - - def test_id_attribute_can_be_read_for_serialization - klass = Class.new(ActiveModelSerializers::Model) do - attributes :id, :one, :two, :three - end - self.class.const_set(:SomeTestModel, klass) - original_attributes = { id: :ego, one: 1, two: 2, three: 3 } - original_instance = klass.new(original_attributes) - - # Initial value - instance = original_instance.dup - expected_attributes = { id: :ego, one: 1, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal :ego, instance.id - assert_equal :ego, instance.read_attribute_for_serialization(:id) - - # FIXME: Change via accessor has no effect on attributes. - instance = original_instance.dup - instance.id = :superego - assert_equal expected_attributes, instance.attributes - assert_equal :superego, instance.id - assert_equal :superego, instance.read_attribute_for_serialization(:id) - - # FIXME: Change via mutating attributes - instance = original_instance.dup - instance.attributes[:id] = :superego - expected_attributes = { id: :superego, one: 1, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal :ego, instance.id - assert_equal :ego, instance.read_attribute_for_serialization(:id) - ensure - self.class.send(:remove_const, :SomeTestModel) - end - - def test_id_attribute_can_be_read_for_serialization_with_attributes_accessors_fix - klass = Class.new(ActiveModelSerializers::Model) do - derive_attributes_from_names_and_fix_accessors - attributes :id, :one, :two, :three - end - self.class.const_set(:SomeTestModel, klass) - original_attributes = { id: :ego, one: 1, two: 2, three: 3 } - original_instance = klass.new(original_attributes) - - # Initial value - instance = original_instance.dup - expected_attributes = { id: :ego, one: 1, two: 2, three: 3 }.with_indifferent_access - assert_equal expected_attributes, instance.attributes - assert_equal :ego, instance.id - assert_equal :ego, instance.read_attribute_for_serialization(:id) - - expected_attributes = { id: :superego, one: 1, two: 2, three: 3 }.with_indifferent_access - # Change via accessor - instance = original_instance.dup - instance.id = :superego - assert_equal expected_attributes, instance.attributes - assert_equal :superego, instance.id - assert_equal :superego, instance.read_attribute_for_serialization(:id) - - # Attributes frozen - assert instance.attributes.frozen? - ensure - self.class.send(:remove_const, :SomeTestModel) - end - end -end diff --git a/test/active_model_serializers/railtie_test_isolated.rb b/test/active_model_serializers/railtie_test_isolated.rb deleted file mode 100644 index 1044fc8b..00000000 --- a/test/active_model_serializers/railtie_test_isolated.rb +++ /dev/null @@ -1,68 +0,0 @@ -# Execute this test in isolation -require 'support/isolated_unit' - -class RailtieTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation - - class WithRailsRequiredFirst < RailtieTest - setup do - require 'rails' - require 'active_model_serializers' - make_basic_app do |app| - app.config.action_controller.perform_caching = true - end - end - - test 'mixes ActionController::Serialization into ActionController::Base' do - assert ActionController.const_defined?(:Serialization), - "ActionController::Serialization should be defined, but isn't" - assert ::ActionController::Base.included_modules.include?(::ActionController::Serialization), - "ActionController::Serialization should be included in ActionController::Base, but isn't" - end - - test 'prepares url_helpers for SerializationContext' do - assert ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for - assert_equal Rails.application.routes.default_url_options, - ActiveModelSerializers::SerializationContext.default_url_options - end - - test 'sets the ActiveModelSerializers.logger to Rails.logger' do - refute_nil Rails.logger - refute_nil ActiveModelSerializers.logger - assert_equal Rails.logger, ActiveModelSerializers.logger - end - - test 'it is configured for caching' do - assert_equal ActionController::Base.cache_store, ActiveModelSerializers.config.cache_store - assert_equal true, Rails.configuration.action_controller.perform_caching - assert_equal true, ActiveModelSerializers.config.perform_caching - end - end - - class WithoutRailsRequiredFirst < RailtieTest - setup do - require 'active_model_serializers' - make_basic_app do |app| - app.config.action_controller.perform_caching = true - end - end - - test 'does not mix ActionController::Serialization into ActionController::Base' do - refute ActionController.const_defined?(:Serialization), - 'ActionController::Serialization should not be defined, but is' - end - - test 'has its own logger at ActiveModelSerializers.logger' do - refute_nil Rails.logger - refute_nil ActiveModelSerializers.logger - refute_equal Rails.logger, ActiveModelSerializers.logger - end - - test 'it is not configured for caching' do - refute_nil ActionController::Base.cache_store - assert_nil ActiveModelSerializers.config.cache_store - assert_equal true, Rails.configuration.action_controller.perform_caching - assert_nil ActiveModelSerializers.config.perform_caching - end - end -end diff --git a/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb b/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb deleted file mode 100644 index 30542408..00000000 --- a/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +++ /dev/null @@ -1,161 +0,0 @@ -require 'support/isolated_unit' -require 'minitest/mock' -require 'action_dispatch' -require 'action_controller' - -class JsonApiRendererTest < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Isolation - - class TestController < ActionController::Base - class << self - attr_accessor :last_request_parameters - end - - def render_with_jsonapi_renderer - permitted_params = params.permit(data: [:id, :type, attributes: [:name]]) - permitted_params = permitted_params.to_h.with_indifferent_access - attributes = - if permitted_params[:data] - permitted_params[:data][:attributes].merge(id: permitted_params[:data][:id]) - else - # Rails returns empty params when no mime type can be negotiated. - # (Until https://github.com/rails/rails/pull/26632 is reviewed.) - permitted_params - end - author = Author.new(attributes) - render jsonapi: author - end - - def parse - self.class.last_request_parameters = request.request_parameters - head :ok - end - end - - def teardown - TestController.last_request_parameters = nil - end - - def assert_parses(expected, actual, headers = {}) - post '/parse', params: actual, headers: headers - assert_response :ok - assert_equal(expected, TestController.last_request_parameters) - end - - def define_author_model_and_serializer - TestController.const_set(:Author, Class.new(ActiveModelSerializers::Model) do - attributes :id, :name - end) - TestController.const_set(:AuthorSerializer, Class.new(ActiveModel::Serializer) do - type 'users' - attribute :id - attribute :name - end) - end - - class WithoutRenderer < JsonApiRendererTest - setup do - require 'rails' - require 'active_record' - require 'support/rails5_shims' - require 'active_model_serializers' - require 'fixtures/poro' - - make_basic_app - - Rails.application.routes.draw do - ActiveSupport::Deprecation.silence do - match ':action', to: TestController, via: [:get, :post] - end - end - define_author_model_and_serializer - end - - def test_jsonapi_parser_not_registered - parsers = if Rails::VERSION::MAJOR >= 5 - ActionDispatch::Request.parameter_parsers - else - ActionDispatch::ParamsParser::DEFAULT_PARSERS - end - assert_nil parsers[Mime[:jsonapi]] - end - - def test_jsonapi_renderer_not_registered - payload = '{"data": {"attributes": {"name": "Johnny Rico"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}' - headers = { 'CONTENT_TYPE' => 'application/vnd.api+json' } - post '/render_with_jsonapi_renderer', params: payload, headers: headers - assert_equal '', response.body - assert_equal 500, response.status - assert_equal ActionView::MissingTemplate, request.env['action_dispatch.exception'].class - end - - def test_jsonapi_parser - assert_parses( - {}, - '', - 'CONTENT_TYPE' => 'application/vnd.api+json' - ) - end - end - - class WithRenderer < JsonApiRendererTest - setup do - require 'rails' - require 'active_record' - require 'support/rails5_shims' - require 'active_model_serializers' - require 'fixtures/poro' - require 'active_model_serializers/register_jsonapi_renderer' - - make_basic_app - - Rails.application.routes.draw do - ActiveSupport::Deprecation.silence do - match ':action', to: TestController, via: [:get, :post] - end - end - define_author_model_and_serializer - end - - def test_jsonapi_parser_registered - if Rails::VERSION::MAJOR >= 5 - parsers = ActionDispatch::Request.parameter_parsers - assert_equal Proc, parsers[:jsonapi].class - else - parsers = ActionDispatch::ParamsParser::DEFAULT_PARSERS - assert_equal Proc, parsers[Mime[:jsonapi]].class - end - end - - def test_jsonapi_renderer_registered - expected = { - 'data' => { - 'id' => '36c9c04e-86b1-4636-a5b0-8616672d1765', - 'type' => 'users', - 'attributes' => { 'name' => 'Johnny Rico' } - } - } - - payload = '{"data": {"attributes": {"name": "Johnny Rico"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}' - headers = { 'CONTENT_TYPE' => 'application/vnd.api+json' } - post '/render_with_jsonapi_renderer', params: payload, headers: headers - assert_equal expected.to_json, response.body - end - - def test_jsonapi_parser - assert_parses( - { - 'data' => { - 'attributes' => { - 'name' => 'John Doe' - }, - 'type' => 'users', - 'id' => '36c9c04e-86b1-4636-a5b0-8616672d1765' - } - }, - '{"data": {"attributes": {"name": "John Doe"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}', - 'CONTENT_TYPE' => 'application/vnd.api+json' - ) - end - end -end diff --git a/test/active_model_serializers/serialization_context_test_isolated.rb b/test/active_model_serializers/serialization_context_test_isolated.rb deleted file mode 100644 index 5720e84a..00000000 --- a/test/active_model_serializers/serialization_context_test_isolated.rb +++ /dev/null @@ -1,71 +0,0 @@ -# Execute this test in isolation -require 'support/isolated_unit' -require 'minitest/mock' - -class SerializationContextTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation - - class WithRails < SerializationContextTest - def create_request - request = ActionDispatch::Request.new({}) - def request.original_url - 'http://example.com/articles?page=2' - end - - def request.query_parameters - { 'page' => 2 } - end - request - end - - setup do - require 'rails' - require 'active_model_serializers' - make_basic_app - @context = ActiveModelSerializers::SerializationContext.new(create_request) - end - - test 'create context with request url and query parameters' do - assert_equal @context.request_url, 'http://example.com/articles' - assert_equal @context.query_parameters, 'page' => 2 - end - - test 'url_helpers is set up for Rails url_helpers' do - assert_equal Module, ActiveModelSerializers::SerializationContext.url_helpers.class - assert ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for - end - - test 'default_url_options returns Rails.application.routes.default_url_options' do - assert_equal Rails.application.routes.default_url_options, - ActiveModelSerializers::SerializationContext.default_url_options - end - end - - class WithoutRails < SerializationContextTest - def create_request - { - request_url: 'http://example.com/articles', - query_parameters: { 'page' => 2 } - } - end - - setup do - require 'active_model_serializers/serialization_context' - @context = ActiveModelSerializers::SerializationContext.new(create_request) - end - - test 'create context with request url and query parameters' do - assert_equal @context.request_url, 'http://example.com/articles' - assert_equal @context.query_parameters, 'page' => 2 - end - - test 'url_helpers is a module when Rails is not present' do - assert_equal Module, ActiveModelSerializers::SerializationContext.url_helpers.class - refute ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for - end - - test 'default_url_options return a Hash' do - assert Hash, ActiveModelSerializers::SerializationContext.default_url_options.class - end - end -end diff --git a/test/active_model_serializers/test/schema_test.rb b/test/active_model_serializers/test/schema_test.rb deleted file mode 100644 index 0fe497d7..00000000 --- a/test/active_model_serializers/test/schema_test.rb +++ /dev/null @@ -1,131 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Test - class SchemaTest < ActionController::TestCase - include ActiveModelSerializers::Test::Schema - - class MyController < ActionController::Base - def index - render json: profile - end - - def show - index - end - - def name_as_a_integer - profile.name = 1 - index - end - - def render_using_json_api - render json: profile, adapter: :json_api - end - - def invalid_json_body - render json: '' - end - - private - - def profile - @profile ||= Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - end - end - - tests MyController - - def test_that_assert_with_a_valid_schema - get :index - assert_response_schema - end - - def test_that_raises_a_minitest_error_with_a_invalid_schema - message = "#/name: failed schema #/properties/name: For 'properties/name', \"Name 1\" is not an integer. and #/description: failed schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean." - - get :show - - error = assert_raises Minitest::Assertion do - assert_response_schema - end - assert_equal(message, error.message) - end - - def test_that_raises_error_with_a_custom_message_with_a_invalid_schema - message = 'oh boy the show is broken' - exception_message = "#/name: failed schema #/properties/name: For 'properties/name', \"Name 1\" is not an integer. and #/description: failed schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean." - expected_message = "#{message}: #{exception_message}" - - get :show - - error = assert_raises Minitest::Assertion do - assert_response_schema(nil, message) - end - assert_equal(expected_message, error.message) - end - - def test_that_assert_with_a_custom_schema - get :show - assert_response_schema('custom/show.json') - end - - def test_that_assert_with_a_hyper_schema - get :show - assert_response_schema('hyper_schema.json') - end - - def test_simple_json_pointers - get :show - assert_response_schema('simple_json_pointers.json') - end - - def test_simple_json_pointers_that_doesnt_match - get :name_as_a_integer - - assert_raises Minitest::Assertion do - assert_response_schema('simple_json_pointers.json') - end - end - - def test_json_api_schema - get :render_using_json_api - assert_response_schema('render_using_json_api.json') - end - - def test_that_assert_with_a_custom_schema_directory - original_schema_path = ActiveModelSerializers.config.schema_path - ActiveModelSerializers.config.schema_path = 'test/support/custom_schemas' - - get :index - assert_response_schema - - ActiveModelSerializers.config.schema_path = original_schema_path - end - - def test_with_a_non_existent_file - message = 'No Schema file at test/support/schemas/non-existent.json' - - get :show - - error = assert_raises ActiveModelSerializers::Test::Schema::MissingSchema do - assert_response_schema('non-existent.json') - end - assert_equal(message, error.message) - end - - def test_that_raises_with_a_invalid_json_body - # message changes from JSON gem 2.0.2 to 2.2.0 - message = /A JSON text must at least contain two octets!|unexpected token at ''/ - - get :invalid_json_body - - error = assert_raises ActiveModelSerializers::Test::Schema::InvalidSchemaError do - assert_response_schema('custom/show.json') - end - - assert_match(message, error.message) - end - end - end -end diff --git a/test/active_model_serializers/test/serializer_test.rb b/test/active_model_serializers/test/serializer_test.rb deleted file mode 100644 index 38dc60ba..00000000 --- a/test/active_model_serializers/test/serializer_test.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Test - class SerializerTest < ActionController::TestCase - include ActiveModelSerializers::Test::Serializer - - class MyController < ActionController::Base - def render_using_serializer - render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - end - - def render_some_text - render(plain: 'ok') - end - end - - tests MyController - - def test_supports_specifying_serializers_with_a_serializer_class - get :render_using_serializer - assert_serializer ProfileSerializer - end - - def test_supports_specifying_serializers_with_a_regexp - get :render_using_serializer - assert_serializer(/\AProfile.+\Z/) - end - - def test_supports_specifying_serializers_with_a_string - get :render_using_serializer - assert_serializer 'ProfileSerializer' - end - - def test_supports_specifying_serializers_with_a_symbol - get :render_using_serializer - assert_serializer :profile_serializer - end - - def test_supports_specifying_serializers_with_a_nil - get :render_some_text - assert_serializer nil - end - - def test_raises_descriptive_error_message_when_serializer_was_not_rendered - get :render_using_serializer - e = assert_raise ActiveSupport::TestCase::Assertion do - assert_serializer 'PostSerializer' - end - assert_match 'expecting <"PostSerializer"> but rendering with <["ProfileSerializer"]>', e.message - end - - def test_raises_argument_error_when_asserting_with_invalid_object - get :render_using_serializer - e = assert_raise ArgumentError do - assert_serializer Hash - end - assert_match 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil', e.message - end - end - end -end diff --git a/test/active_record_test.rb b/test/active_record_test.rb deleted file mode 100644 index 5bb941a4..00000000 --- a/test/active_record_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -class ActiveRecordTest < ActiveSupport::TestCase - include ActiveModel::Serializer::Lint::Tests - - def setup - @resource = ARModels::Post.new - end -end diff --git a/test/adapter/attributes_test.rb b/test/adapter/attributes_test.rb deleted file mode 100644 index e60019f5..00000000 --- a/test/adapter/attributes_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class AttributesTest < ActiveSupport::TestCase - class Person < ActiveModelSerializers::Model - attributes :first_name, :last_name - end - - class PersonSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - end - - def setup - ActionController::Base.cache_store.clear - end - - def test_serializable_hash - person = Person.new(first_name: 'Arthur', last_name: 'Dent') - serializer = PersonSerializer.new(person) - adapter = ActiveModelSerializers::Adapter::Attributes.new(serializer) - - assert_equal({ first_name: 'Arthur', last_name: 'Dent' }, - adapter.serializable_hash) - end - - def test_serializable_hash_with_transform_key_casing - person = Person.new(first_name: 'Arthur', last_name: 'Dent') - serializer = PersonSerializer.new(person) - adapter = ActiveModelSerializers::Adapter::Attributes.new( - serializer, - key_transform: :camel_lower - ) - - assert_equal({ firstName: 'Arthur', lastName: 'Dent' }, - adapter.serializable_hash) - end - end - end -end diff --git a/test/adapter/deprecation_test.rb b/test/adapter/deprecation_test.rb deleted file mode 100644 index ea858caa..00000000 --- a/test/adapter/deprecation_test.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class DeprecationTest < ActiveSupport::TestCase - class PostSerializer < ActiveModel::Serializer - attribute :body - end - setup do - post = Post.new(id: 1, body: 'Hello') - @serializer = PostSerializer.new(post) - end - - def test_null_adapter_serialization_deprecation - expected = {} - assert_deprecated do - assert_equal(expected, Null.new(@serializer).as_json) - end - end - - def test_json_adapter_serialization_deprecation - expected = { post: { body: 'Hello' } } - assert_deprecated do - assert_equal(expected, Json.new(@serializer).as_json) - end - end - - def test_jsonapi_adapter_serialization_deprecation - expected = { - data: { - id: '1', - type: 'posts', - attributes: { - body: 'Hello' - } - } - } - assert_deprecated do - assert_equal(expected, JsonApi.new(@serializer).as_json) - end - end - - def test_attributes_adapter_serialization_deprecation - expected = { body: 'Hello' } - assert_deprecated do - assert_equal(expected, Attributes.new(@serializer).as_json) - end - end - - def test_adapter_create_deprecation - assert_deprecated do - Adapter.create(@serializer) - end - end - - def test_adapter_adapter_map_deprecation - assert_deprecated do - Adapter.adapter_map - end - end - - def test_adapter_adapters_deprecation - assert_deprecated do - Adapter.adapters - end - end - - def test_adapter_adapter_class_deprecation - assert_deprecated do - Adapter.adapter_class(:json_api) - end - end - - def test_adapter_register_deprecation - assert_deprecated do - begin - Adapter.register(:test, Class.new) - ensure - Adapter.adapter_map.delete('test') - end - end - end - - def test_adapter_lookup_deprecation - assert_deprecated do - Adapter.lookup(:json_api) - end - end - - private - - def assert_deprecated - assert_output(nil, /deprecated/) do - yield - end - end - end - end - end -end diff --git a/test/adapter/json/belongs_to_test.rb b/test/adapter/json/belongs_to_test.rb deleted file mode 100644 index 0f096f0b..00000000 --- a/test/adapter/json/belongs_to_test.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class Json - class BelongsToTest < ActiveSupport::TestCase - def setup - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @anonymous_post.comments = [] - @comment.post = @post - @comment.author = nil - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @anonymous_post.blog = nil - - @serializer = CommentSerializer.new(@comment) - @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) - ActionController::Base.cache_store.clear - end - - def test_includes_post - assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post]) - end - - def test_include_nil_author - serializer = PostSerializer.new(@anonymous_post) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], blog: { id: 999, name: 'Custom blog' }, author: nil } }, adapter.serializable_hash) - end - - def test_include_nil_author_with_specified_serializer - serializer = PostPreviewSerializer.new(@anonymous_post) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash) - end - end - end - end -end diff --git a/test/adapter/json/collection_test.rb b/test/adapter/json/collection_test.rb deleted file mode 100644 index 8deb4050..00000000 --- a/test/adapter/json/collection_test.rb +++ /dev/null @@ -1,104 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class Json - class Collection < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @first_post.comments = [] - @second_post.comments = [] - @first_post.author = @author - @second_post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @first_post.blog = @blog - @second_post.blog = nil - - ActionController::Base.cache_store.clear - end - - def test_with_serializer_option - @blog.special_attribute = 'Special' - @blog.articles = [@first_post, @second_post] - serializer = ActiveModel::Serializer::CollectionSerializer.new([@blog], serializer: CustomBlogSerializer) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - expected = { blogs: [{ - id: 1, - special_attribute: 'Special', - articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] - }] } - assert_equal expected, adapter.serializable_hash - end - - def test_include_multiple_posts - serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - expected = { posts: [{ - title: 'Hello!!', - body: 'Hello, world!!', - id: 1, - comments: [], - author: { - id: 1, - name: 'Steve K.' - }, - blog: { - id: 999, - name: 'Custom blog' - } - }, { - title: 'New Post', - body: 'Body', - id: 2, - comments: [], - author: { - id: 1, - name: 'Steve K.' - }, - blog: { - id: 999, - name: 'Custom blog' - } - }] } - assert_equal expected, adapter.serializable_hash - end - - def test_root_is_underscored - virtual_value = VirtualValue.new(id: 1) - serializer = ActiveModel::Serializer::CollectionSerializer.new([virtual_value]) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - assert_equal 1, adapter.serializable_hash[:virtual_values].length - end - - def test_include_option - serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer, include: '') - actual = adapter.serializable_hash - expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, - { id: 2, title: 'New Post', body: 'Body' }] } - - assert_equal(expected, actual) - end - - def test_fields_with_no_associations_include_option - actual = ActiveModelSerializers::SerializableResource.new( - [@first_post, @second_post], adapter: :json, fields: [:id], include: [] - ).as_json - - expected = { posts: [{ - id: 1 - }, { - id: 2 - }] } - - assert_equal(expected, actual) - end - end - end - end -end diff --git a/test/adapter/json/has_many_test.rb b/test/adapter/json/has_many_test.rb deleted file mode 100644 index feeec93c..00000000 --- a/test/adapter/json/has_many_test.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class Json - class HasManyTestTest < ActiveSupport::TestCase - class ModelWithoutSerializer < ::Model - attributes :id, :name - end - - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @post.author = @author - @first_comment.post = @post - @second_comment.post = @post - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @tag = ModelWithoutSerializer.new(id: 1, name: '#hash_tag') - @post.tags = [@tag] - end - - def test_has_many - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - assert_equal([ - { id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], adapter.serializable_hash[:post][:comments]) - end - - def test_has_many_with_no_serializer - post_serializer_class = Class.new(ActiveModel::Serializer) do - attributes :id - has_many :tags - end - serializer = post_serializer_class.new(@post) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - assert_equal({ - id: 42, - tags: [ - { 'id' => 1, 'name' => '#hash_tag' } - ] - }.to_json, adapter.serializable_hash[:post].to_json) - end - end - end - end -end diff --git a/test/adapter/json/transform_test.rb b/test/adapter/json/transform_test.rb deleted file mode 100644 index c102b5af..00000000 --- a/test/adapter/json/transform_test.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class Json - class KeyCaseTest < ActiveSupport::TestCase - def mock_request(key_transform = nil) - context = Minitest::Mock.new - context.expect(:request_url, URI) - context.expect(:query_parameters, {}) - options = {} - options[:key_transform] = key_transform if key_transform - options[:serialization_context] = context - serializer = CustomBlogSerializer.new(@blog) - @adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options) - end - - class Post < ::Model; end - class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body, :publish_at - end - - setup do - ActionController::Base.cache_store.clear - @blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat') - end - - def test_transform_default - mock_request - assert_equal({ - blog: { id: 1, special_attribute: 'neat', articles: nil } - }, @adapter.serializable_hash) - end - - def test_transform_global_config - mock_request - result = with_config(key_transform: :camel_lower) do - @adapter.serializable_hash - end - assert_equal({ - blog: { id: 1, specialAttribute: 'neat', articles: nil } - }, result) - end - - def test_transform_serialization_ctx_overrides_global_config - mock_request(:camel) - result = with_config(key_transform: :camel_lower) do - @adapter.serializable_hash - end - assert_equal({ - Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil } - }, result) - end - - def test_transform_undefined - mock_request(:blam) - result = nil - assert_raises NoMethodError do - result = @adapter.serializable_hash - end - end - - def test_transform_dash - mock_request(:dash) - assert_equal({ - blog: { id: 1, :"special-attribute" => 'neat', articles: nil } - }, @adapter.serializable_hash) - end - - def test_transform_unaltered - mock_request(:unaltered) - assert_equal({ - blog: { id: 1, special_attribute: 'neat', articles: nil } - }, @adapter.serializable_hash) - end - - def test_transform_camel - mock_request(:camel) - assert_equal({ - Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil } - }, @adapter.serializable_hash) - end - - def test_transform_camel_lower - mock_request(:camel_lower) - assert_equal({ - blog: { id: 1, specialAttribute: 'neat', articles: nil } - }, @adapter.serializable_hash) - end - end - end - end -end diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb deleted file mode 100644 index ded83ab5..00000000 --- a/test/adapter/json_api/belongs_to_test.rb +++ /dev/null @@ -1,155 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class BelongsToTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.blog = @blog - @anonymous_post.comments = [] - @anonymous_post.blog = nil - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - - @serializer = CommentSerializer.new(@comment) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) - ActionController::Base.cache_store.clear - end - - def test_includes_post_id - expected = { data: { type: 'posts', id: '42' } } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post]) - end - - def test_includes_linked_post - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post]) - expected = [{ - id: '42', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_limiting_linked_post_fields - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] }) - expected = [{ - id: '42', - type: 'posts', - attributes: { - title: 'New Post' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_include_nil_author - serializer = PostSerializer.new(@anonymous_post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships]) - end - - def test_include_type_for_association_when_different_than_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - relationships = adapter.serializable_hash[:data][:relationships] - expected = { - writer: { - data: { - type: 'authors', - id: '1' - } - }, - articles: { - data: [ - { - type: 'posts', - id: '42' - }, - { - type: 'posts', - id: '43' - } - ] - } - } - assert_equal expected, relationships - end - - def test_include_linked_resources_with_type_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, include: [:writer, :articles]) - linked = adapter.serializable_hash[:included] - expected = [ - { - id: '1', - type: 'authors', - attributes: { - name: 'Steve K.' - }, - relationships: { - posts: { data: [] }, - roles: { data: [] }, - bio: { data: nil } - } - }, { - id: '42', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, { - id: '43', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: nil } - } - } - ] - assert_equal expected, linked - end - end - end - end -end diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb deleted file mode 100644 index e60a824e..00000000 --- a/test/adapter/json_api/collection_test.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class CollectionTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @blog = Blog.new(id: 23, name: 'AMS Blog') - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @first_post.comments = [] - @second_post.comments = [] - @first_post.blog = @blog - @second_post.blog = nil - @first_post.author = @author - @second_post.author = @author - @author.posts = [@first_post, @second_post] - - @serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) - ActionController::Base.cache_store.clear - end - - def test_include_multiple_posts - expected = [ - { - id: '1', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, - { - id: '2', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - } - ] - - assert_equal(expected, @adapter.serializable_hash[:data]) - end - - def test_limiting_fields - actual = ActiveModelSerializers::SerializableResource.new( - [@first_post, @second_post], - adapter: :json_api, - fields: { posts: %w(title comments blog author) } - ).serializable_hash - expected = [ - { - id: '1', - type: 'posts', - attributes: { - title: 'Hello!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, - { - id: '2', - type: 'posts', - attributes: { - title: 'New Post' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - } - ] - assert_equal(expected, actual[:data]) - end - end - end - end -end diff --git a/test/adapter/json_api/errors_test.rb b/test/adapter/json_api/errors_test.rb deleted file mode 100644 index cae7a5a6..00000000 --- a/test/adapter/json_api/errors_test.rb +++ /dev/null @@ -1,76 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi < Base - class ErrorsTest < Minitest::Test - include ActiveModel::Serializer::Lint::Tests - - def setup - @resource = ModelWithErrors.new - end - - def test_active_model_with_error - options = { - serializer: ActiveModel::Serializer::ErrorSerializer, - adapter: :json_api - } - - @resource.errors.add(:name, 'cannot be nil') - - serializable_resource = ActiveModelSerializers::SerializableResource.new(@resource, options) - assert_equal serializable_resource.serializer_instance.attributes, {} - assert_equal serializable_resource.serializer_instance.object, @resource - - expected_errors_object = { - errors: [ - { - source: { pointer: '/data/attributes/name' }, - detail: 'cannot be nil' - } - ] - } - assert_equal serializable_resource.as_json, expected_errors_object - end - - def test_active_model_with_multiple_errors - options = { - serializer: ActiveModel::Serializer::ErrorSerializer, - adapter: :json_api - } - - @resource.errors.add(:name, 'cannot be nil') - @resource.errors.add(:name, 'must be longer') - @resource.errors.add(:id, 'must be a uuid') - - serializable_resource = ActiveModelSerializers::SerializableResource.new(@resource, options) - assert_equal serializable_resource.serializer_instance.attributes, {} - assert_equal serializable_resource.serializer_instance.object, @resource - - expected_errors_object = { - 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' } - ] - } - assert_equal serializable_resource.as_json, expected_errors_object - end - - # see http://jsonapi.org/examples/ - def test_parameter_source_type_error - parameter = 'auther' - error_source = ActiveModelSerializers::Adapter::JsonApi::Error.error_source(:parameter, parameter) - assert_equal({ parameter: parameter }, error_source) - end - - def test_unknown_source_type_error - value = 'auther' - assert_raises(ActiveModelSerializers::Adapter::JsonApi::Error::UnknownSourceTypeError) do - ActiveModelSerializers::Adapter::JsonApi::Error.error_source(:hyper, value) - end - end - end - end - end -end diff --git a/test/adapter/json_api/fields_test.rb b/test/adapter/json_api/fields_test.rb deleted file mode 100644 index 85228318..00000000 --- a/test/adapter/json_api/fields_test.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class FieldsTest < ActiveSupport::TestCase - class Post < ::Model - attributes :title, :body - associations :author, :comments - end - class Author < ::Model - attributes :name, :birthday - end - class Comment < ::Model - attributes :body - associations :author, :post - end - - class PostSerializer < ActiveModel::Serializer - type 'posts' - attributes :title, :body - belongs_to :author - has_many :comments - end - - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - attributes :name, :birthday - end - - class CommentSerializer < ActiveModel::Serializer - type 'comments' - attributes :body - belongs_to :author - end - - def setup - @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') - @comment1 = Comment.new(id: 7, body: 'cool', author: @author) - @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) - @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', - author: @author, comments: [@comment1, @comment2]) - @comment1.post = @post - @comment2.post = @post - end - - def test_fields_attributes - fields = { posts: [:title] } - hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash - expected = { - title: 'Title 1' - } - - assert_equal(expected, hash[:data][:attributes]) - end - - def test_fields_relationships - fields = { posts: [:author] } - hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash - expected = { - author: { - data: { - type: 'authors', - id: '1' - } - } - } - - assert_equal(expected, hash[:data][:relationships]) - end - - def test_fields_included - fields = { posts: [:author], comments: [:body] } - hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash - expected = [ - { - type: 'comments', - id: '7', - attributes: { - body: 'cool' - } - }, { - type: 'comments', - id: '12', - attributes: { - body: 'awesome' - } - } - ] - - assert_equal(expected, hash[:included]) - end - end - end - end -end diff --git a/test/adapter/json_api/has_many_embed_ids_test.rb b/test/adapter/json_api/has_many_embed_ids_test.rb deleted file mode 100644 index e016de28..00000000 --- a/test/adapter/json_api/has_many_embed_ids_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class HasManyEmbedIdsTest < ActiveSupport::TestCase - def setup - @author = Author.new(name: 'Steve K.') - @author.bio = nil - @author.roles = nil - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @author.posts = [@first_post, @second_post] - @first_post.author = @author - @second_post.author = @author - @first_post.comments = [] - @second_post.comments = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @first_post.blog = @blog - @second_post.blog = nil - - @serializer = AuthorSerializer.new(@author) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) - end - - def test_includes_comment_ids - expected = { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts]) - end - - def test_no_includes_linked_comments - assert_nil @adapter.serializable_hash[:linked] - end - end - end - end -end diff --git a/test/adapter/json_api/has_many_explicit_serializer_test.rb b/test/adapter/json_api/has_many_explicit_serializer_test.rb deleted file mode 100644 index f598bc9b..00000000 --- a/test/adapter/json_api/has_many_explicit_serializer_test.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - # Test 'has_many :assocs, serializer: AssocXSerializer' - class HasManyExplicitSerializerTest < ActiveSupport::TestCase - def setup - @post = Post.new(title: 'New Post', body: 'Body') - @author = Author.new(name: 'Jane Blogger') - @author.posts = [@post] - @post.author = @author - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @first_comment.author = nil - @second_comment.post = @post - @second_comment.author = nil - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post.blog = @blog - - @serializer = PostPreviewSerializer.new(@post) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new( - @serializer, - include: [:comments, :author] - ) - end - - def test_includes_comment_ids - expected = { - data: [ - { type: 'comments', id: '1' }, - { type: 'comments', id: '2' } - ] - } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end - - def test_includes_linked_data - # If CommentPreviewSerializer is applied correctly the body text will not be present in the output - expected = [ - { - id: '1', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: @post.id.to_s } } - } - }, - { - id: '2', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: @post.id.to_s } } - } - }, - { - id: @author.id.to_s, - type: 'authors', - relationships: { - posts: { data: [{ type: 'posts', id: @post.id.to_s }] } - } - } - ] - - assert_equal(expected, @adapter.serializable_hash[:included]) - end - - def test_includes_author_id - expected = { - data: { type: 'authors', id: @author.id.to_s } - } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) - end - - def test_explicit_serializer_with_null_resource - @post.author = nil - - expected = { data: nil } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) - end - - def test_explicit_serializer_with_null_collection - @post.comments = [] - - expected = { data: [] } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end - end - end - end -end diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb deleted file mode 100644 index a9fa9ac9..00000000 --- a/test/adapter/json_api/has_many_test.rb +++ /dev/null @@ -1,173 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class HasManyTest < ActiveSupport::TestCase - class ModelWithoutSerializer < ::Model - attributes :id, :name - end - - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @author.posts = [] - @author.bio = nil - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @first_comment.author = nil - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @second_comment.author = nil - @post.comments = [@first_comment, @second_comment] - @post_without_comments.comments = [] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @post_without_comments.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post] - @post.blog = @blog - @post_without_comments.blog = nil - @tag = ModelWithoutSerializer.new(id: 1, name: '#hash_tag') - @post.tags = [@tag] - @serializer = PostSerializer.new(@post) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) - - @virtual_value = VirtualValue.new(id: 1) - end - - def test_includes_comment_ids - expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end - - test 'relationships can be whitelisted via fields' do - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, fields: { posts: [:author] }) - result = @adapter.serializable_hash - expected = { - data: { - id: '1', - type: 'posts', - relationships: { - author: { - data: { - id: '1', - type: 'authors' - } - } - } - } - } - - assert_equal expected, result - end - - def test_includes_linked_comments - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments]) - expected = [{ - id: '1', - type: 'comments', - attributes: { - body: 'ZOMG A COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - attributes: { - body: 'ZOMG ANOTHER COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_limit_fields_of_linked_comments - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] }) - expected = [{ - id: '1', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_no_include_linked_if_comments_is_empty - serializer = PostSerializer.new(@post_without_comments) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - assert_nil adapter.serializable_hash[:linked] - end - - def test_include_type_for_association_when_different_than_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - actual = adapter.serializable_hash[:data][:relationships][:articles] - - expected = { - data: [{ - type: 'posts', - id: '1' - }] - } - assert_equal expected, actual - end - - def test_has_many_with_no_serializer - post_serializer_class = Class.new(ActiveModel::Serializer) do - attributes :id - has_many :tags - end - serializer = post_serializer_class.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - assert_equal({ - data: { - id: '1', - type: 'posts', - relationships: { - tags: { data: [@tag.as_json] } - } - } - }, adapter.serializable_hash) - end - - def test_has_many_with_virtual_value - serializer = VirtualValueSerializer.new(@virtual_value) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - assert_equal({ - data: { - id: '1', - type: 'virtual-values', - relationships: { - maker: { data: { type: 'makers', id: '1' } }, - reviews: { data: [{ type: 'reviews', id: '1' }, - { type: 'reviews', id: '2' }] } - } - } - }, adapter.serializable_hash) - end - end - end - end -end diff --git a/test/adapter/json_api/has_one_test.rb b/test/adapter/json_api/has_one_test.rb deleted file mode 100644 index eb505a0d..00000000 --- a/test/adapter/json_api/has_one_test.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class HasOneTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @bio = Bio.new(id: 43, content: 'AMS Contributor') - @author.bio = @bio - @bio.author = @author - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @anonymous_post.comments = [] - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - @author.roles = [] - - @virtual_value = VirtualValue.new(id: 1) - - @serializer = AuthorSerializer.new(@author) - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio, :posts]) - end - - def test_includes_bio_id - expected = { data: { type: 'bios', id: '43' } } - - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio]) - end - - def test_includes_linked_bio - @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio]) - - expected = [ - { - id: '43', - type: 'bios', - attributes: { - content: 'AMS Contributor', - rating: nil - }, - relationships: { - author: { data: { type: 'authors', id: '1' } } - } - } - ] - - assert_equal(expected, @adapter.serializable_hash[:included]) - end - - def test_has_one_with_virtual_value - serializer = VirtualValueSerializer.new(@virtual_value) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - expected = { - data: { - id: '1', - type: 'virtual-values', - relationships: { - maker: { data: { type: 'makers', id: '1' } }, - reviews: { data: [{ type: 'reviews', id: '1' }, - { type: 'reviews', id: '2' }] } - } - } - } - - assert_equal(expected, adapter.serializable_hash) - end - end - end - end -end diff --git a/test/adapter/json_api/include_data_if_sideloaded_test.rb b/test/adapter/json_api/include_data_if_sideloaded_test.rb deleted file mode 100644 index c0da9488..00000000 --- a/test/adapter/json_api/include_data_if_sideloaded_test.rb +++ /dev/null @@ -1,183 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - module Adapter - class JsonApi - class IncludeParamTest < ActiveSupport::TestCase - IncludeParamAuthor = Class.new(::Model) do - associations :tags, :posts - end - - class CustomCommentLoader - def all - [{ foo: 'bar' }] - end - end - class Tag < ::Model - attributes :id, :name - end - - class TagSerializer < ActiveModel::Serializer - type 'tags' - attributes :id, :name - end - - class PostWithTagsSerializer < ActiveModel::Serializer - type 'posts' - attributes :id - has_many :tags - end - - class IncludeParamAuthorSerializer < ActiveModel::Serializer - class_attribute :comment_loader - - has_many :tags, serializer: TagSerializer do - link :self, '//example.com/link_author/relationships/tags' - include_data :if_sideloaded - end - - has_many :unlinked_tags, serializer: TagSerializer do - include_data :if_sideloaded - end - - has_many :posts, serializer: PostWithTagsSerializer do - include_data :if_sideloaded - end - has_many :locations do - include_data :if_sideloaded - end - has_many :comments do - include_data :if_sideloaded - IncludeParamAuthorSerializer.comment_loader.all - end - end - - def setup - IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new - @tag = Tag.new(id: 1337, name: 'mytag') - @author = IncludeParamAuthor.new( - id: 1337, - tags: [@tag] - ) - end - - def test_relationship_not_loaded_when_not_included - expected = { - links: { - self: '//example.com/link_author/relationships/tags' - } - } - - @author.define_singleton_method(:read_attribute_for_serialization) do |attr| - fail 'should not be called' if attr == :tags - super(attr) - end - - assert_relationship(:tags, expected) - end - - def test_relationship_included - expected = { - data: [ - { - id: '1337', - type: 'tags' - } - ], - links: { - self: '//example.com/link_author/relationships/tags' - } - } - - assert_relationship(:tags, expected, include: :tags) - end - - def test_sideloads_included - expected = [ - { - id: '1337', - type: 'tags', - attributes: { name: 'mytag' } - } - ] - hash = result(include: :tags) - assert_equal(expected, hash[:included]) - end - - def test_nested_relationship - expected = { - data: [ - { - id: '1337', - type: 'tags' - } - ], - links: { - self: '//example.com/link_author/relationships/tags' - } - } - - expected_no_data = { - links: { - self: '//example.com/link_author/relationships/tags' - } - } - - assert_relationship(:tags, expected, include: [:tags, { posts: :tags }]) - - @author.define_singleton_method(:read_attribute_for_serialization) do |attr| - fail 'should not be called' if attr == :tags - super(attr) - end - - assert_relationship(:tags, expected_no_data, include: { posts: :tags }) - end - - def test_include_params_with_no_block - @author.define_singleton_method(:read_attribute_for_serialization) do |attr| - fail 'should not be called' if attr == :locations - super(attr) - end - - expected = { meta: {} } - - assert_relationship(:locations, expected) - end - - def test_block_relationship - expected = { - data: [ - { 'foo' => 'bar' } - ] - } - - assert_relationship(:comments, expected, include: [:comments]) - end - - def test_node_not_included_when_no_link - expected = { meta: {} } - assert_relationship(:unlinked_tags, expected, key_transform: :unaltered) - end - - private - - def assert_relationship(relationship_name, expected, opts = {}) - actual = relationship_data(relationship_name, opts) - assert_equal(expected, actual) - end - - def result(opts) - opts = { adapter: :json_api }.merge(opts) - serializable(@author, opts).serializable_hash - end - - def relationship_data(relationship_name, opts = {}) - hash = result(opts) - hash[:data][:relationships][relationship_name] - end - end - end - end - end -end diff --git a/test/adapter/json_api/json_api_test.rb b/test/adapter/json_api/json_api_test.rb deleted file mode 100644 index cb2ce909..00000000 --- a/test/adapter/json_api/json_api_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApiTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - end - - def test_custom_keys - serializer = PostWithCustomKeysSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - - assert_equal({ - reviews: { data: [{ type: 'comments', id: '1' }, - { type: 'comments', id: '2' }] }, - writer: { data: { type: 'authors', id: '1' } }, - site: { data: { type: 'blogs', id: '1' } } - }, adapter.serializable_hash[:data][:relationships]) - end - end - end -end diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb deleted file mode 100644 index 0d9c69b6..00000000 --- a/test/adapter/json_api/linked_test.rb +++ /dev/null @@ -1,413 +0,0 @@ -require 'test_helper' - -class NestedPost < ::Model; associations :nested_posts end -class NestedPostSerializer < ActiveModel::Serializer - has_many :nested_posts -end -module ActiveModelSerializers - module Adapter - class JsonApi - class LinkedTest < ActiveSupport::TestCase - def setup - @author1 = Author.new(id: 1, name: 'Steve K.') - @author2 = Author.new(id: 2, name: 'Tenderlove') - @bio1 = Bio.new(id: 1, content: 'AMS Contributor') - @bio2 = Bio.new(id: 2, content: 'Rails Contributor') - @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 20, title: 'New Post', body: 'Body') - @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body') - @blog = Blog.new(name: 'AMS Blog') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @first_post.blog = @blog - @second_post.blog = @blog - @third_post.blog = nil - @first_post.comments = [@first_comment, @second_comment] - @second_post.comments = [] - @third_post.comments = [] - @first_post.author = @author1 - @second_post.author = @author2 - @third_post.author = @author1 - @first_comment.post = @first_post - @first_comment.author = nil - @second_comment.post = @first_post - @second_comment.author = nil - @author1.posts = [@first_post, @third_post] - @author1.bio = @bio1 - @author1.roles = [] - @author2.posts = [@second_post] - @author2.bio = @bio2 - @author2.roles = [] - @bio1.author = @author1 - @bio2.author = @author2 - end - - def test_include_multiple_posts_and_linked_array - serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) - adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [:comments, author: [:bio]] - ) - alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [:comments, author: [:bio]] - ) - - expected = { - data: [ - { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, - { - id: '20', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '2' } } - } - } - ], - included: [ - { - id: '1', - type: 'comments', - attributes: { - body: 'ZOMG A COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '10' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - attributes: { - body: 'ZOMG ANOTHER COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '10' } }, - author: { data: nil } - } - }, { - id: '1', - type: 'authors', - attributes: { - name: 'Steve K.' - }, - relationships: { - posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, - roles: { data: [] }, - bio: { data: { type: 'bios', id: '1' } } - } - }, { - id: '1', - type: 'bios', - attributes: { - content: 'AMS Contributor', - rating: nil - }, - relationships: { - author: { data: { type: 'authors', id: '1' } } - } - }, { - id: '2', - type: 'authors', - attributes: { - name: 'Tenderlove' - }, - relationships: { - posts: { data: [{ type: 'posts', id: '20' }] }, - roles: { data: [] }, - bio: { data: { type: 'bios', id: '2' } } - } - }, { - id: '2', - type: 'bios', - attributes: { - rating: nil, - content: 'Rails Contributor' - }, - relationships: { - author: { data: { type: 'authors', id: '2' } } - } - } - ] - } - assert_equal expected, adapter.serializable_hash - assert_equal expected, alt_adapter.serializable_hash - end - - def test_include_multiple_posts_and_linked - serializer = BioSerializer.new @bio1 - adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [author: [:posts]] - ) - alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [author: [:posts]] - ) - - expected = [ - { - id: '1', - type: 'authors', - attributes: { - name: 'Steve K.' - }, - relationships: { - posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, - roles: { data: [] }, - bio: { data: { type: 'bios', id: '1' } } - } - }, { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, { - id: '30', - type: 'posts', - attributes: { - title: 'Yet Another Post', - body: 'Body' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - } - ] - - assert_equal expected, adapter.serializable_hash[:included] - assert_equal expected, alt_adapter.serializable_hash[:included] - end - - def test_underscore_model_namespace_for_linked_resource_type - spammy_post = Post.new(id: 123) - spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] - serializer = SpammyPostSerializer.new(spammy_post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - relationships = adapter.serializable_hash[:data][:relationships] - expected = { - related: { - data: [{ - type: 'spam-unrelated-links', - id: '456' - }] - } - } - assert_equal expected, relationships - end - - def test_underscore_model_namespace_with_namespace_separator_for_linked_resource_type - spammy_post = Post.new(id: 123) - spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] - serializer = SpammyPostSerializer.new(spammy_post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - relationships = with_namespace_separator '--' do - adapter.serializable_hash[:data][:relationships] - end - expected = { - related: { - data: [{ - type: 'spam--unrelated-links', - id: '456' - }] - } - } - assert_equal expected, relationships - end - - def test_multiple_references_to_same_resource - serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment]) - adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [:post] - ) - - expected = [ - { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { - data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] - }, - blog: { - data: { type: 'blogs', id: '999' } - }, - author: { - data: { type: 'authors', id: '1' } - } - } - } - ] - - assert_equal expected, adapter.serializable_hash[:included] - end - - def test_nil_link_with_specified_serializer - @first_post.author = nil - serializer = PostPreviewSerializer.new(@first_post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new( - serializer, - include: [:author] - ) - - expected = { - data: { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - author: { data: nil } - } - } - } - assert_equal expected, adapter.serializable_hash - end - end - - class NoDuplicatesTest < ActiveSupport::TestCase - class Post < ::Model; associations :author end - class Author < ::Model; associations :posts, :roles, :bio end - - class PostSerializer < ActiveModel::Serializer - type 'posts' - belongs_to :author - end - - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - has_many :posts - end - - def setup - @author = Author.new(id: 1, posts: [], roles: [], bio: nil) - @post1 = Post.new(id: 1, author: @author) - @post2 = Post.new(id: 2, author: @author) - @author.posts << @post1 - @author.posts << @post2 - - @nestedpost1 = NestedPost.new(id: 1, nested_posts: []) - @nestedpost2 = NestedPost.new(id: 2, nested_posts: []) - @nestedpost1.nested_posts << @nestedpost1 - @nestedpost1.nested_posts << @nestedpost2 - @nestedpost2.nested_posts << @nestedpost1 - @nestedpost2.nested_posts << @nestedpost2 - end - - def test_no_duplicates - hash = ActiveModelSerializers::SerializableResource.new(@post1, adapter: :json_api, - include: '*.*') - .serializable_hash - expected = [ - { - type: 'authors', id: '1', - relationships: { - posts: { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } - } - }, - { - type: 'posts', id: '2', - relationships: { - author: { - data: { type: 'authors', id: '1' } - } - } - } - ] - assert_equal(expected, hash[:included]) - end - - def test_no_duplicates_collection - hash = ActiveModelSerializers::SerializableResource.new( - [@post1, @post2], - adapter: :json_api, - include: '*.*' - ).serializable_hash - expected = [ - { - type: 'authors', id: '1', - relationships: { - posts: { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } - } - } - ] - assert_equal(expected, hash[:included]) - end - - def test_no_duplicates_global - hash = ActiveModelSerializers::SerializableResource.new( - @nestedpost1, - adapter: :json_api, - include: '*' - ).serializable_hash - expected = [ - type: 'nested-posts', id: '2', - relationships: { - :"nested-posts" => { - data: [ - { type: 'nested-posts', id: '1' }, - { type: 'nested-posts', id: '2' } - ] - } - } - ] - assert_equal(expected, hash[:included]) - end - - def test_no_duplicates_collection_global - hash = ActiveModelSerializers::SerializableResource.new( - [@nestedpost1, @nestedpost2], - adapter: :json_api, - include: '*' - ).serializable_hash - assert_nil(hash[:included]) - end - end - end - end -end diff --git a/test/adapter/json_api/links_test.rb b/test/adapter/json_api/links_test.rb deleted file mode 100644 index ffbfa303..00000000 --- a/test/adapter/json_api/links_test.rb +++ /dev/null @@ -1,95 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class LinksTest < ActiveSupport::TestCase - class LinkAuthor < ::Model; associations :posts end - class LinkAuthorSerializer < ActiveModel::Serializer - link :self do - href "http://example.com/link_author/#{object.id}" - meta stuff: 'value' - end - link(:author) { link_author_url(object.id) } - link(:link_authors) { url_for(controller: 'link_authors', action: 'index', only_path: false) } - link(:posts) { link_author_posts_url(object.id) } - link :resource, 'http://example.com/resource' - link :yet_another do - "http://example.com/resource/#{object.id}" - end - link(:nil) { nil } - end - - def setup - Rails.application.routes.draw do - resources :link_authors do - resources :posts - end - end - @post = Post.new(id: 1337, comments: [], author: nil) - @author = LinkAuthor.new(id: 1337, posts: [@post]) - end - - def test_toplevel_links - hash = ActiveModelSerializers::SerializableResource.new( - @post, - adapter: :json_api, - links: { - self: { - href: 'http://example.com/posts', - meta: { - stuff: 'value' - } - } - } - ).serializable_hash - expected = { - self: { - href: 'http://example.com/posts', - meta: { - stuff: 'value' - } - } - } - assert_equal(expected, hash[:links]) - end - - def test_nil_toplevel_links - hash = ActiveModelSerializers::SerializableResource.new( - @post, - adapter: :json_api, - links: nil - ).serializable_hash - refute hash.key?(:links), 'No links key to be output' - end - - def test_nil_toplevel_links_json_adapter - hash = ActiveModelSerializers::SerializableResource.new( - @post, - adapter: :json, - links: nil - ).serializable_hash - refute hash.key?(:links), 'No links key to be output' - end - - def test_resource_links - hash = serializable(@author, adapter: :json_api).serializable_hash - expected = { - self: { - href: 'http://example.com/link_author/1337', - meta: { - stuff: 'value' - } - }, - author: 'http://example.com/link_authors/1337', - :"link-authors" => 'http://example.com/link_authors', - resource: 'http://example.com/resource', - posts: 'http://example.com/link_authors/1337/posts', - :"yet-another" => 'http://example.com/resource/1337' - } - assert_equal(expected, hash[:data][:links]) - end - end - end - end -end diff --git a/test/adapter/json_api/pagination_links_test.rb b/test/adapter/json_api/pagination_links_test.rb deleted file mode 100644 index 736ea2fe..00000000 --- a/test/adapter/json_api/pagination_links_test.rb +++ /dev/null @@ -1,193 +0,0 @@ -require 'test_helper' -require 'will_paginate/array' -require 'kaminari' -require 'kaminari/hooks' -::Kaminari::Hooks.init - -module ActiveModelSerializers - module Adapter - class JsonApi - class PaginationLinksTest < ActiveSupport::TestCase - URI = 'http://example.com'.freeze - - def setup - ActionController::Base.cache_store.clear - @array = [ - Profile.new(id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), - Profile.new(id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), - Profile.new(id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3'), - Profile.new(id: 4, name: 'Name 4', description: 'Description 4', comments: 'Comments 4'), - Profile.new(id: 5, name: 'Name 5', description: 'Description 5', comments: 'Comments 5') - ] - end - - def mock_request(query_parameters = {}, original_url = URI) - context = Minitest::Mock.new - context.expect(:request_url, original_url) - context.expect(:query_parameters, query_parameters) - context.expect(:key_transform, nil) - end - - def load_adapter(paginated_collection, mock_request = nil) - render_options = { adapter: :json_api } - render_options[:serialization_context] = mock_request if mock_request - serializable(paginated_collection, render_options) - end - - def using_kaminari(page = 2) - Kaminari.paginate_array(@array).page(page).per(2) - end - - def using_will_paginate(page = 2) - @array.paginate(page: page, per_page: 2) - end - - def data - { - data: [ - { id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } }, - { id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } }, - { id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } }, - { id: '4', type: 'profiles', attributes: { name: 'Name 4', description: 'Description 4' } }, - { id: '5', type: 'profiles', attributes: { name: 'Name 5', description: 'Description 5' } } - ] - } - end - - def links - { - links: { - self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2", - first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", - prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", - next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2", - last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2" - } - } - end - - def last_page_links - { - links: { - self: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2", - first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", - prev: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2" - } - } - end - - def expected_response_when_unpaginatable - data - end - - def expected_response_with_pagination_links - {}.tap do |hash| - hash[:data] = data.values.flatten[2..3] - hash.merge! links - end - end - - def expected_response_without_pagination_links - {}.tap do |hash| - hash[:data] = data.values.flatten[2..3] - end - end - - def expected_response_with_pagination_links_and_additional_params - new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" } - {}.tap do |hash| - hash[:data] = data.values.flatten[2..3] - hash.merge! links: new_links - end - end - - def expected_response_with_last_page_pagination_links - {}.tap do |hash| - hash[:data] = [data.values.flatten.last] - hash.merge! last_page_links - end - end - - def expected_response_with_no_data_pagination_links - {}.tap do |hash| - hash[:data] = [] - hash[:links] = {} - end - end - - def test_pagination_links_using_kaminari - adapter = load_adapter(using_kaminari, mock_request) - - assert_equal expected_response_with_pagination_links, adapter.serializable_hash - end - - def test_pagination_links_using_will_paginate - adapter = load_adapter(using_will_paginate, mock_request) - - assert_equal expected_response_with_pagination_links, adapter.serializable_hash - end - - def test_pagination_links_with_additional_params - adapter = load_adapter(using_will_paginate, mock_request(test: 'test')) - - assert_equal expected_response_with_pagination_links_and_additional_params, - adapter.serializable_hash - end - - def test_pagination_links_when_zero_results_kaminari - @array = [] - - adapter = load_adapter(using_kaminari(1), mock_request) - - assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash - end - - def test_pagination_links_when_zero_results_will_paginate - @array = [] - - adapter = load_adapter(using_will_paginate(1), mock_request) - - assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash - end - - def test_last_page_pagination_links_using_kaminari - adapter = load_adapter(using_kaminari(3), mock_request) - - assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash - end - - def test_last_page_pagination_links_using_will_paginate - adapter = load_adapter(using_will_paginate(3), mock_request) - - assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash - end - - def test_not_showing_pagination_links - adapter = load_adapter(@array, mock_request) - - assert_equal expected_response_when_unpaginatable, adapter.serializable_hash - end - - def test_raises_descriptive_error_when_serialization_context_unset - render_options = { adapter: :json_api } - adapter = serializable(using_kaminari, render_options) - exception = assert_raises do - adapter.as_json - end - exception_class = ActiveModelSerializers::Adapter::JsonApi::PaginationLinks::MissingSerializationContextError - assert_equal exception_class, exception.class - assert_match(/CollectionSerializer#paginated\?/, exception.message) - end - - def test_pagination_links_not_present_when_disabled - ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = false - adapter = load_adapter(using_kaminari, mock_request) - - assert_equal expected_response_without_pagination_links, adapter.serializable_hash - ensure - ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = true - end - end - end - end -end diff --git a/test/adapter/json_api/parse_test.rb b/test/adapter/json_api/parse_test.rb deleted file mode 100644 index bee79c8c..00000000 --- a/test/adapter/json_api/parse_test.rb +++ /dev/null @@ -1,137 +0,0 @@ -require 'test_helper' -module ActiveModelSerializers - module Adapter - class JsonApi - module Deserialization - class ParseTest < Minitest::Test - def setup - @hash = { - 'data' => { - 'type' => 'photos', - 'id' => 'zorglub', - 'attributes' => { - 'title' => 'Ember Hamster', - 'src' => 'http://example.com/images/productivity.png' - }, - 'relationships' => { - 'author' => { - 'data' => nil - }, - 'photographer' => { - 'data' => { 'type' => 'people', 'id' => '9' } - }, - 'comments' => { - 'data' => [ - { 'type' => 'comments', 'id' => '1' }, - { 'type' => 'comments', 'id' => '2' } - ] - } - } - } - } - @params = ActionController::Parameters.new(@hash) - @expected = { - id: 'zorglub', - title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - author_id: nil, - photographer_id: '9', - comment_ids: %w(1 2) - } - - @illformed_payloads = [nil, - {}, - { - 'data' => nil - }, { - 'data' => { 'attributes' => [] } - }, { - 'data' => { 'relationships' => [] } - }, { - 'data' => { - 'relationships' => { 'rel' => nil } - } - }, { - 'data' => { - 'relationships' => { 'rel' => {} } - } - }] - end - - def test_hash - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash) - assert_equal(@expected, parsed_hash) - end - - def test_actioncontroller_parameters - assert_equal(false, @params.permitted?) - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@params) - assert_equal(@expected, parsed_hash) - end - - def test_illformed_payloads_safe - @illformed_payloads.each do |p| - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(p) - assert_equal({}, parsed_hash) - end - end - - def test_illformed_payloads_unsafe - @illformed_payloads.each do |p| - assert_raises(InvalidDocument) do - ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(p) - end - end - end - - def test_filter_fields_only - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author]) - expected = { - id: 'zorglub', - title: 'Ember Hamster', - author_id: nil - } - assert_equal(expected, parsed_hash) - end - - def test_filter_fields_except - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author]) - expected = { - src: 'http://example.com/images/productivity.png', - photographer_id: '9', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end - - def test_keys - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title }) - expected = { - id: 'zorglub', - post_title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - user_id: nil, - photographer_id: '9', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end - - def test_polymorphic - parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer]) - expected = { - id: 'zorglub', - title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - author_id: nil, - photographer_id: '9', - photographer_type: 'people', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end - end - end - end - end -end diff --git a/test/adapter/json_api/relationship_test.rb b/test/adapter/json_api/relationship_test.rb deleted file mode 100644 index cfd5be85..00000000 --- a/test/adapter/json_api/relationship_test.rb +++ /dev/null @@ -1,397 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class RelationshipTest < ActiveSupport::TestCase - def test_relationship_with_data - expected = { - data: { - id: '1', - type: 'blogs' - } - } - - model_attributes = { blog: Blog.new(id: 1) } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog - end - assert_equal(expected, actual) - end - - def test_relationship_with_nil_model - expected = { data: nil } - - model_attributes = { blog: nil } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog - end - assert_equal(expected, actual) - end - - def test_relationship_with_data_array - expected = { - data: [ - { - id: '1', - type: 'posts' - }, - { - id: '2', - type: 'posts' - } - ] - } - - model_attributes = { posts: [Post.new(id: 1), Post.new(id: 2)] } - relationship_name = :posts - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :posts - end - assert_equal(expected, actual) - end - - def test_relationship_data_not_included - expected = { meta: {} } - - model_attributes = { blog: :does_not_matter } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - include_data false - end - end - assert_equal(expected, actual) - end - - def test_relationship_many_links - expected = { - links: { - self: 'a link', - related: 'another link' - } - } - - model_attributes = { blog: :does_not_matter } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - include_data false - link :self, 'a link' - link :related, 'another link' - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_link_with_meta - expected = { - links: { - self: { - href: '1', - meta: { id: 1 } - } - } - } - - model_attributes = { blog: Blog.new(id: 1) } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - include_data false - link :self do - href object.blog.id.to_s - meta(id: object.blog.id) - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_simple_meta - expected = { meta: { id: '1' } } - - model_attributes = { blog: Blog.new(id: 1) } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - include_data false - meta(id: object.blog.id.to_s) - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_meta - expected = { - meta: { - id: 1 - } - } - - model_attributes = { blog: Blog.new(id: 1) } - relationship_name = :blog - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - include_data false - meta(id: object.blog.id) - end - end - assert_equal(expected, actual) - end - - def test_relationship_simple_link - expected = { - data: { - id: '1337', - type: 'bios' - }, - links: { - self: '//example.com/link_author/relationships/bio' - } - } - - model_attributes = { bio: Bio.new(id: 1337) } - relationship_name = :bio - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :bio do - link :self, '//example.com/link_author/relationships/bio' - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_link - expected = { - data: { id: '1337', type: 'profiles' }, - links: { related: '//example.com/profiles/1337' } - } - - model_attributes = { profile: Profile.new(id: 1337) } - relationship_name = :profile - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :profile do - id = object.profile.id - link :related do - "//example.com/profiles/#{id}" if id != 123 - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_with_everything - expected = { - data: [{ id: '1337', type: 'likes' }], - links: { - related: { - href: '//example.com/likes/1337', - meta: { ids: '1337' } - } - }, - meta: { liked: true } - } - - model_attributes = { likes: [Like.new(id: 1337)] } - relationship_name = :likes - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :likes do - link :related do - ids = object.likes.map(&:id).join(',') - href "//example.com/likes/#{ids}" - meta ids: ids - end - meta liked: object.likes.any? - end - end - assert_equal(expected, actual) - end - - def test_relationship_nil_link - expected = { - data: { id: '123', type: 'profiles' } - } - - model_attributes = { profile: Profile.new(id: 123) } - relationship_name = :profile - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :profile do - id = object.profile.id - link :related do - "//example.com/profiles/#{id}" if id != 123 - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_link_href - expected = { - data: [{ id: '1337', type: 'locations' }], - links: { - related: { href: '//example.com/locations/1337' } - } - } - - model_attributes = { locations: [Location.new(id: 1337)] } - relationship_name = :locations - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :locations do - link :related do - ids = object.locations.map(&:id).join(',') - href "//example.com/locations/#{ids}" - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_link_href_and_meta - expected = { - data: [{ id: '1337', type: 'posts' }], - links: { - related: { - href: '//example.com/posts/1337', - meta: { ids: '1337' } - } - } - } - - model_attributes = { posts: [Post.new(id: 1337, comments: [], author: nil)] } - relationship_name = :posts - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :posts do - link :related do - ids = object.posts.map(&:id).join(',') - href "//example.com/posts/#{ids}" - meta ids: ids - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_block_link_meta - expected = { - data: [{ id: '1337', type: 'comments' }], - links: { - self: { - meta: { ids: [1] } - } - } - } - - model_attributes = { comments: [Comment.new(id: 1337)] } - relationship_name = :comments - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :comments do - link :self do - meta ids: [1] - end - end - end - assert_equal(expected, actual) - end - - def test_relationship_meta - expected = { - data: [{ id: 'from-serializer-method', type: 'roles' }], - meta: { count: 1 } - } - - model_attributes = { roles: [Role.new(id: 'from-record')] } - relationship_name = :roles - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_many :roles do |serializer| - meta count: object.roles.count - serializer.cached_roles - end - def cached_roles - [ - Role.new(id: 'from-serializer-method') - ] - end - end - assert_equal(expected, actual) - end - - def test_relationship_not_including_data - expected = { - links: { self: '//example.com/link_author/relationships/blog' } - } - - model_attributes = { blog: Object } - relationship_name = :blog - model = new_model(model_attributes) - model.define_singleton_method(:read_attribute_for_serialization) do |attr| - fail 'should not be called' if attr == :blog - super(attr) - end - assert_nothing_raised do - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - has_one :blog do - link :self, '//example.com/link_author/relationships/blog' - include_data false - end - end - assert_equal(expected, actual) - end - end - - def test_relationship_including_data_explicit - expected = { - data: { id: '1337', type: 'authors' }, - meta: { name: 'Dan Brown' } - } - - model_attributes = { reviewer: Author.new(id: 1337) } - relationship_name = :reviewer - model = new_model(model_attributes) - actual = build_serializer_and_serialize_relationship(model, relationship_name) do - belongs_to :reviewer do - meta name: 'Dan Brown' - include_data true - end - end - assert_equal(expected, actual) - end - - private - - def build_serializer_and_serialize_relationship(model, relationship_name, &block) - serializer_class = Class.new(ActiveModel::Serializer, &block) - hash = serializable(model, serializer: serializer_class, adapter: :json_api).serializable_hash - hash[:data][:relationships][relationship_name] - end - - def new_model(model_attributes) - Class.new(ActiveModelSerializers::Model) do - attributes(*model_attributes.keys) - - def self.name - 'TestModel' - end - end.new(model_attributes) - end - end - end - end -end diff --git a/test/adapter/json_api/resource_identifier_test.rb b/test/adapter/json_api/resource_identifier_test.rb deleted file mode 100644 index 62b7d93b..00000000 --- a/test/adapter/json_api/resource_identifier_test.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class ResourceIdentifierTest < ActiveSupport::TestCase - class WithDefinedTypeSerializer < ActiveModel::Serializer - type 'with_defined_type' - end - - class WithDefinedIdSerializer < ActiveModel::Serializer - def id - 'special_id' - end - end - - class FragmentedSerializer < ActiveModel::Serializer - cache only: :id - - def id - 'special_id' - end - end - - setup do - @model = Author.new(id: 1, name: 'Steve K.') - ActionController::Base.cache_store.clear - end - - def test_defined_type - test_type(WithDefinedTypeSerializer, 'with-defined-type') - end - - def test_singular_type - test_type_inflection(AuthorSerializer, 'author', :singular) - end - - def test_plural_type - test_type_inflection(AuthorSerializer, 'authors', :plural) - end - - def test_type_with_namespace - Object.const_set(:Admin, Module.new) - model = Class.new(::Model) - Admin.const_set(:PowerUser, model) - serializer = Class.new(ActiveModel::Serializer) - Admin.const_set(:PowerUserSerializer, serializer) - with_namespace_separator '--' do - admin_user = Admin::PowerUser.new - serializer = Admin::PowerUserSerializer.new(admin_user) - expected = { - id: admin_user.id, - type: 'admin--power-users' - } - - identifier = ResourceIdentifier.new(serializer, {}) - actual = identifier.as_json - assert_equal(expected, actual) - end - end - - def test_id_defined_on_object - test_id(AuthorSerializer, @model.id.to_s) - end - - def test_id_defined_on_serializer - test_id(WithDefinedIdSerializer, 'special_id') - end - - def test_id_defined_on_fragmented - test_id(FragmentedSerializer, 'special_id') - end - - private - - def test_type_inflection(serializer_class, expected_type, inflection) - original_inflection = ActiveModelSerializers.config.jsonapi_resource_type - ActiveModelSerializers.config.jsonapi_resource_type = inflection - test_type(serializer_class, expected_type) - ensure - ActiveModelSerializers.config.jsonapi_resource_type = original_inflection - end - - def test_type(serializer_class, expected_type) - serializer = serializer_class.new(@model) - resource_identifier = ResourceIdentifier.new(serializer, nil) - expected = { - id: @model.id.to_s, - type: expected_type - } - - assert_equal(expected, resource_identifier.as_json) - end - - def test_id(serializer_class, id) - serializer = serializer_class.new(@model) - resource_identifier = ResourceIdentifier.new(serializer, nil) - inflection = ActiveModelSerializers.config.jsonapi_resource_type - type = @model.class.model_name.send(inflection) - expected = { - id: id, - type: type - } - - assert_equal(expected, resource_identifier.as_json) - end - end - end - end -end diff --git a/test/adapter/json_api/resource_meta_test.rb b/test/adapter/json_api/resource_meta_test.rb deleted file mode 100644 index fa281f30..00000000 --- a/test/adapter/json_api/resource_meta_test.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - module Adapter - class JsonApi - class ResourceMetaTest < Minitest::Test - class MetaHashPostSerializer < ActiveModel::Serializer - attributes :id - meta stuff: 'value' - end - - class MetaBlockPostSerializer < ActiveModel::Serializer - attributes :id - meta do - { comments_count: object.comments.count } - end - end - - class MetaBlockPostBlankMetaSerializer < ActiveModel::Serializer - attributes :id - meta do - {} - end - end - - class MetaBlockPostEmptyStringSerializer < ActiveModel::Serializer - attributes :id - meta do - '' - end - end - - def setup - @post = Post.new(id: 1337, comments: [], author: nil) - end - - def test_meta_hash_object_resource - hash = ActiveModelSerializers::SerializableResource.new( - @post, - serializer: MetaHashPostSerializer, - adapter: :json_api - ).serializable_hash - expected = { - stuff: 'value' - } - assert_equal(expected, hash[:data][:meta]) - end - - def test_meta_block_object_resource - hash = ActiveModelSerializers::SerializableResource.new( - @post, - serializer: MetaBlockPostSerializer, - adapter: :json_api - ).serializable_hash - expected = { - :"comments-count" => @post.comments.count - } - assert_equal(expected, hash[:data][:meta]) - end - - def test_meta_object_resource_in_array - post2 = Post.new(id: 1339, comments: [Comment.new]) - posts = [@post, post2] - hash = ActiveModelSerializers::SerializableResource.new( - posts, - each_serializer: MetaBlockPostSerializer, - adapter: :json_api - ).serializable_hash - expected = { - data: [ - { id: '1337', type: 'posts', meta: { :"comments-count" => 0 } }, - { id: '1339', type: 'posts', meta: { :"comments-count" => 1 } } - ] - } - assert_equal(expected, hash) - end - - def test_meta_object_blank_omitted - hash = ActiveModelSerializers::SerializableResource.new( - @post, - serializer: MetaBlockPostBlankMetaSerializer, - adapter: :json_api - ).serializable_hash - refute hash[:data].key? :meta - end - - def test_meta_object_empty_string_omitted - hash = ActiveModelSerializers::SerializableResource.new( - @post, - serializer: MetaBlockPostEmptyStringSerializer, - adapter: :json_api - ).serializable_hash - refute hash[:data].key? :meta - end - end - end - end - end -end diff --git a/test/adapter/json_api/toplevel_jsonapi_test.rb b/test/adapter/json_api/toplevel_jsonapi_test.rb deleted file mode 100644 index 7b0357e5..00000000 --- a/test/adapter/json_api/toplevel_jsonapi_test.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class TopLevelJsonApiTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.blog = @blog - @anonymous_post.comments = [] - @anonymous_post.blog = nil - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - end - - def test_toplevel_jsonapi_defaults_to_false - assert_equal config.fetch(:jsonapi_include_toplevel_object), false - end - - def test_disable_toplevel_jsonapi - with_config(jsonapi_include_toplevel_object: false) do - hash = serialize(@post) - assert_nil(hash[:jsonapi]) - end - end - - def test_enable_toplevel_jsonapi - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - refute_nil(hash[:jsonapi]) - end - end - - def test_default_toplevel_jsonapi_version - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - assert_equal('1.0', hash[:jsonapi][:version]) - end - end - - def test_toplevel_jsonapi_no_meta - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - assert_nil(hash[:jsonapi][:meta]) - end - end - - def test_toplevel_jsonapi_meta - new_config = { - jsonapi_include_toplevel_object: true, - jsonapi_toplevel_meta: { - 'copyright' => 'Copyright 2015 Example Corp.' - } - } - with_config(new_config) do - hash = serialize(@post) - assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta)) - end - end - - private - - def serialize(resource, options = {}) - serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash - end - end - end - end -end diff --git a/test/adapter/json_api/transform_test.rb b/test/adapter/json_api/transform_test.rb deleted file mode 100644 index 887ec835..00000000 --- a/test/adapter/json_api/transform_test.rb +++ /dev/null @@ -1,512 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonApi - class KeyCaseTest < ActiveSupport::TestCase - class Post < ::Model - attributes :title, :body, :publish_at - associations :author, :comments - end - class Author < ::Model - attributes :first_name, :last_name - end - class Comment < ::Model - attributes :body - associations :author, :post - end - - class PostSerializer < ActiveModel::Serializer - type 'posts' - attributes :title, :body, :publish_at - belongs_to :author - has_many :comments - - link(:self) { post_url(object.id) } - link(:post_authors) { post_authors_url(object.id) } - link(:subscriber_comments) { post_comments_url(object.id) } - - meta do - { - rating: 5, - favorite_count: 10 - } - end - end - - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - attributes :first_name, :last_name - end - - class CommentSerializer < ActiveModel::Serializer - type 'comments' - attributes :body - belongs_to :author - end - - def mock_request(transform = nil) - context = Minitest::Mock.new - context.expect(:request_url, URI) - context.expect(:query_parameters, {}) - context.expect(:url_helpers, Rails.application.routes.url_helpers) - @options = {} - @options[:key_transform] = transform if transform - @options[:serialization_context] = context - end - - def setup - Rails.application.routes.draw do - resources :posts do - resources :authors - resources :comments - end - end - @publish_at = 1.day.from_now - @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') - @comment1 = Comment.new(id: 7, body: 'cool', author: @author) - @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) - @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', - author: @author, comments: [@comment1, @comment2], - publish_at: @publish_at) - @comment1.post = @post - @comment2.post = @post - end - - def test_success_document_transform_default - mock_request - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - assert_equal({ - data: { - id: '1337', - type: 'posts', - attributes: { - title: 'Title 1', - body: 'Body 1', - :"publish-at" => @publish_at - }, - relationships: { - author: { - data: { id: '1', type: 'authors' } - }, - comments: { - data: [ - { id: '7', type: 'comments' }, - { id: '12', type: 'comments' } - ] - } - }, - links: { - self: 'http://example.com/posts/1337', - :"post-authors" => 'http://example.com/posts/1337/authors', - :"subscriber-comments" => 'http://example.com/posts/1337/comments' - }, - meta: { rating: 5, :"favorite-count" => 10 } - } - }, result) - end - - def test_success_document_transform_global_config - mock_request - result = with_config(key_transform: :camel_lower) do - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - adapter.serializable_hash - end - assert_equal({ - data: { - id: '1337', - type: 'posts', - attributes: { - title: 'Title 1', - body: 'Body 1', - publishAt: @publish_at - }, - relationships: { - author: { - data: { id: '1', type: 'authors' } - }, - comments: { - data: [ - { id: '7', type: 'comments' }, - { id: '12', type: 'comments' } - ] - } - }, - links: { - self: 'http://example.com/posts/1337', - postAuthors: 'http://example.com/posts/1337/authors', - subscriberComments: 'http://example.com/posts/1337/comments' - }, - meta: { rating: 5, favoriteCount: 10 } - } - }, result) - end - - def test_success_doc_transform_serialization_ctx_overrides_global - mock_request(:camel) - result = with_config(key_transform: :camel_lower) do - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - adapter.serializable_hash - end - assert_equal({ - Data: { - Id: '1337', - Type: 'Posts', - Attributes: { - Title: 'Title 1', - Body: 'Body 1', - PublishAt: @publish_at - }, - Relationships: { - Author: { - Data: { Id: '1', Type: 'Authors' } - }, - Comments: { - Data: [ - { Id: '7', Type: 'Comments' }, - { Id: '12', Type: 'Comments' } - ] - } - }, - Links: { - Self: 'http://example.com/posts/1337', - PostAuthors: 'http://example.com/posts/1337/authors', - SubscriberComments: 'http://example.com/posts/1337/comments' - }, - Meta: { Rating: 5, FavoriteCount: 10 } - } - }, result) - end - - def test_success_document_transform_dash - mock_request(:dash) - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - assert_equal({ - data: { - id: '1337', - type: 'posts', - attributes: { - title: 'Title 1', - body: 'Body 1', - :"publish-at" => @publish_at - }, - relationships: { - author: { - data: { id: '1', type: 'authors' } - }, - comments: { - data: [ - { id: '7', type: 'comments' }, - { id: '12', type: 'comments' } - ] - } - }, - links: { - self: 'http://example.com/posts/1337', - :"post-authors" => 'http://example.com/posts/1337/authors', - :"subscriber-comments" => 'http://example.com/posts/1337/comments' - }, - meta: { rating: 5, :"favorite-count" => 10 } - } - }, result) - end - - def test_success_document_transform_unaltered - mock_request(:unaltered) - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - assert_equal({ - data: { - id: '1337', - type: 'posts', - attributes: { - title: 'Title 1', - body: 'Body 1', - publish_at: @publish_at - }, - relationships: { - author: { - data: { id: '1', type: 'authors' } - }, - comments: { - data: [ - { id: '7', type: 'comments' }, - { id: '12', type: 'comments' } - ] - } - }, - links: { - self: 'http://example.com/posts/1337', - post_authors: 'http://example.com/posts/1337/authors', - subscriber_comments: 'http://example.com/posts/1337/comments' - }, - meta: { rating: 5, favorite_count: 10 } - } - }, result) - end - - def test_success_document_transform_undefined - mock_request(:zoot) - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - exception = assert_raises NoMethodError do - adapter.serializable_hash - end - assert_match(/undefined method.*zoot/, exception.message) - end - - def test_success_document_transform_camel - mock_request(:camel) - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - assert_equal({ - Data: { - Id: '1337', - Type: 'Posts', - Attributes: { - Title: 'Title 1', - Body: 'Body 1', - PublishAt: @publish_at - }, - Relationships: { - Author: { - Data: { Id: '1', Type: 'Authors' } - }, - Comments: { - Data: [ - { Id: '7', Type: 'Comments' }, - { Id: '12', Type: 'Comments' } - ] - } - }, - Links: { - Self: 'http://example.com/posts/1337', - PostAuthors: 'http://example.com/posts/1337/authors', - SubscriberComments: 'http://example.com/posts/1337/comments' - }, - Meta: { Rating: 5, FavoriteCount: 10 } - } - }, result) - end - - def test_success_document_transform_camel_lower - mock_request(:camel_lower) - serializer = PostSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - assert_equal({ - data: { - id: '1337', - type: 'posts', - attributes: { - title: 'Title 1', - body: 'Body 1', - publishAt: @publish_at - }, - relationships: { - author: { - data: { id: '1', type: 'authors' } - }, - comments: { - data: [ - { id: '7', type: 'comments' }, - { id: '12', type: 'comments' } - ] - } - }, - links: { - self: 'http://example.com/posts/1337', - postAuthors: 'http://example.com/posts/1337/authors', - subscriberComments: 'http://example.com/posts/1337/comments' - }, - meta: { rating: 5, favoriteCount: 10 } - } - }, result) - end - - def test_error_document_transform_default - mock_request - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - expected_errors_object = { - errors: [ - { - source: { pointer: '/data/attributes/published-at' }, - detail: 'must be in the future' - }, - { - source: { pointer: '/data/attributes/title' }, - detail: 'must be longer' - } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_global_config - mock_request - result = with_config(key_transform: :camel) do - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - adapter.serializable_hash - end - expected_errors_object = { - Errors: [ - { - Source: { Pointer: '/data/attributes/PublishedAt' }, - Detail: 'must be in the future' - }, - { - Source: { Pointer: '/data/attributes/Title' }, - Detail: 'must be longer' - } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_serialization_ctx_overrides_global - mock_request(:camel) - result = with_config(key_transform: :camel_lower) do - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - adapter.serializable_hash - end - expected_errors_object = { - Errors: [ - { - Source: { Pointer: '/data/attributes/PublishedAt' }, - Detail: 'must be in the future' - }, - { - Source: { Pointer: '/data/attributes/Title' }, - Detail: 'must be longer' - } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_dash - mock_request(:dash) - - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - - expected_errors_object = { - errors: [ - { - source: { pointer: '/data/attributes/published-at' }, - detail: 'must be in the future' - }, - { - source: { pointer: '/data/attributes/title' }, - detail: 'must be longer' - } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_unaltered - mock_request(:unaltered) - - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - - expected_errors_object = { - errors: [ - { source: { pointer: '/data/attributes/published_at' }, detail: 'must be in the future' }, - { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_undefined - mock_request(:krazy) - - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - - exception = assert_raises NoMethodError do - adapter.serializable_hash - end - assert_match(/undefined method.*krazy/, exception.message) - end - - def test_error_document_transform_camel - mock_request(:camel) - - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - - expected_errors_object = { - Errors: [ - { Source: { Pointer: '/data/attributes/PublishedAt' }, Detail: 'must be in the future' }, - { Source: { Pointer: '/data/attributes/Title' }, Detail: 'must be longer' } - ] - } - assert_equal expected_errors_object, result - end - - def test_error_document_transform_camel_lower - mock_request(:camel_lower) - - resource = ModelWithErrors.new - resource.errors.add(:published_at, 'must be in the future') - resource.errors.add(:title, 'must be longer') - - serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) - adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) - result = adapter.serializable_hash - - expected_errors_object = { - errors: [ - { source: { pointer: '/data/attributes/publishedAt' }, detail: 'must be in the future' }, - { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } - ] - } - assert_equal expected_errors_object, result - end - end - end - end -end diff --git a/test/adapter/json_api/type_test.rb b/test/adapter/json_api/type_test.rb deleted file mode 100644 index 40b84cf2..00000000 --- a/test/adapter/json_api/type_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - module Adapter - class JsonApi - class TypeTest < ActiveSupport::TestCase - class StringTypeSerializer < ActiveModel::Serializer - attribute :name - type 'profile' - end - - class SymbolTypeSerializer < ActiveModel::Serializer - attribute :name - type :profile - end - - setup do - @author = Author.new(id: 1, name: 'Steve K.') - end - - def test_config_plural - with_jsonapi_resource_type :plural do - assert_type(@author, 'authors') - end - end - - def test_config_singular - with_jsonapi_resource_type :singular do - assert_type(@author, 'author') - end - end - - def test_explicit_string_type_value - assert_type(@author, 'profile', serializer: StringTypeSerializer) - end - - def test_explicit_symbol_type_value - assert_type(@author, 'profile', serializer: SymbolTypeSerializer) - end - - private - - def assert_type(resource, expected_type, opts = {}) - opts = opts.reverse_merge(adapter: :json_api) - hash = serializable(resource, opts).serializable_hash - assert_equal(expected_type, hash.fetch(:data).fetch(:type)) - end - - def with_jsonapi_resource_type(inflection) - old_inflection = ActiveModelSerializers.config.jsonapi_resource_type - ActiveModelSerializers.config.jsonapi_resource_type = inflection - yield - ensure - ActiveModelSerializers.config.jsonapi_resource_type = old_inflection - end - end - end - end - end -end diff --git a/test/adapter/json_test.rb b/test/adapter/json_test.rb deleted file mode 100644 index f7f178f8..00000000 --- a/test/adapter/json_test.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class JsonTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - - @serializer = PostSerializer.new(@post) - @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) - end - - def test_has_many - assert_equal([ - { id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], @adapter.serializable_hash[:post][:comments]) - end - - def test_custom_keys - serializer = PostWithCustomKeysSerializer.new(@post) - adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - - assert_equal({ - id: 1, - reviews: [ - { id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], - writer: { id: 1, name: 'Steve K.' }, - site: { id: 1, name: 'My Blog!!' } - }, adapter.serializable_hash[:post]) - end - end - end -end diff --git a/test/adapter/null_test.rb b/test/adapter/null_test.rb deleted file mode 100644 index 4e701db1..00000000 --- a/test/adapter/null_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - module Adapter - class NullTest < ActiveSupport::TestCase - def setup - profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - serializer = ProfileSerializer.new(profile) - - @adapter = Null.new(serializer) - end - - def test_serializable_hash - assert_equal({}, @adapter.serializable_hash) - end - - def test_it_returns_empty_json - assert_equal('{}', @adapter.to_json) - end - end - end -end diff --git a/test/adapter/polymorphic_test.rb b/test/adapter/polymorphic_test.rb deleted file mode 100644 index 87d5ff51..00000000 --- a/test/adapter/polymorphic_test.rb +++ /dev/null @@ -1,171 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - module Adapter - class PolymorphicTest < ActiveSupport::TestCase - setup do - @employee = Employee.new(id: 42, name: 'Zoop Zoopler', email: 'zoop@example.com') - @picture = @employee.pictures.new(id: 1, title: 'headshot-1.jpg') - @picture.imageable = @employee - end - - def serialization(resource, adapter = :attributes) - serializable(resource, adapter: adapter, serializer: PolymorphicBelongsToSerializer).as_json - end - - def tag_serialization(adapter = :attributes) - tag = PolyTag.new(id: 1, phrase: 'foo') - tag.object_tags << ObjectTag.new(id: 1, poly_tag_id: 1, taggable: @employee) - tag.object_tags << ObjectTag.new(id: 5, poly_tag_id: 1, taggable: @picture) - serializable(tag, adapter: adapter, serializer: PolymorphicTagSerializer, include: '*.*').as_json - end - - def test_attributes_serialization - expected = - { - id: 1, - title: 'headshot-1.jpg', - imageable: { - type: 'employee', - employee: { - id: 42, - name: 'Zoop Zoopler' - } - } - } - - assert_equal(expected, serialization(@picture)) - end - - def test_attributes_serialization_without_polymorphic_association - expected = - { - id: 2, - title: 'headshot-2.jpg', - imageable: nil - } - - simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') - assert_equal(expected, serialization(simple_picture)) - end - - def test_attributes_serialization_with_polymorphic_has_many - expected = - { - id: 1, - phrase: 'foo', - object_tags: [ - { - id: 1, - taggable: { - type: 'employee', - employee: { - id: 42 - } - } - }, - { - id: 5, - taggable: { - type: 'picture', - picture: { - id: 1 - } - } - } - ] - } - assert_equal(expected, tag_serialization) - end - - def test_json_serialization - expected = - { - picture: { - id: 1, - title: 'headshot-1.jpg', - imageable: { - type: 'employee', - employee: { - id: 42, - name: 'Zoop Zoopler' - } - } - } - } - - assert_equal(expected, serialization(@picture, :json)) - end - - def test_json_serialization_without_polymorphic_association - expected = - { - picture: { - id: 2, - title: 'headshot-2.jpg', - imageable: nil - } - } - - simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') - assert_equal(expected, serialization(simple_picture, :json)) - end - - def test_json_serialization_with_polymorphic_has_many - expected = - { - poly_tag: { - id: 1, - phrase: 'foo', - object_tags: [ - { - id: 1, - taggable: { - type: 'employee', - employee: { - id: 42 - } - } - }, - { - id: 5, - taggable: { - type: 'picture', - picture: { - id: 1 - } - } - } - ] - } - } - assert_equal(expected, tag_serialization(:json)) - end - - def test_json_api_serialization - expected = - { - data: { - id: '1', - type: 'pictures', - attributes: { - title: 'headshot-1.jpg' - }, - relationships: { - imageable: { - data: { - id: '42', - type: 'employees' - } - } - } - } - } - - assert_equal(expected, serialization(@picture, :json_api)) - end - end - end - end -end diff --git a/test/adapter_test.rb b/test/adapter_test.rb deleted file mode 100644 index c1b00d72..00000000 --- a/test/adapter_test.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class AdapterTest < ActiveSupport::TestCase - def setup - profile = Profile.new - @serializer = ProfileSerializer.new(profile) - @adapter = ActiveModelSerializers::Adapter::Base.new(@serializer) - end - - def test_serializable_hash_is_abstract_method - assert_raises(NotImplementedError) do - @adapter.serializable_hash(only: [:name]) - end - end - - def test_serialization_options_ensures_option_is_a_hash - adapter = Class.new(ActiveModelSerializers::Adapter::Base) do - def serializable_hash(options = nil) - serialization_options(options) - end - end.new(@serializer) - assert_equal({}, adapter.serializable_hash(nil)) - assert_equal({}, adapter.serializable_hash({})) - ensure - ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ } - end - - def test_serialization_options_ensures_option_is_one_of_valid_options - adapter = Class.new(ActiveModelSerializers::Adapter::Base) do - def serializable_hash(options = nil) - serialization_options(options) - end - end.new(@serializer) - filtered_options = { now: :see_me, then: :not } - valid_options = ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS.each_with_object({}) do |option, result| - result[option] = option - end - assert_equal(valid_options, adapter.serializable_hash(filtered_options.merge(valid_options))) - ensure - ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ } - end - - def test_serializer - assert_equal @serializer, @adapter.serializer - end - - def test_create_adapter - adapter = ActiveModelSerializers::Adapter.create(@serializer) - assert_equal ActiveModelSerializers::Adapter::Attributes, adapter.class - end - - def test_create_adapter_with_override - adapter = ActiveModelSerializers::Adapter.create(@serializer, adapter: :json_api) - assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter.class - end - - def test_inflected_adapter_class_for_known_adapter - ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' } - klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) - - ActiveSupport::Inflector.inflections.acronyms.clear - - assert_equal ActiveModelSerializers::Adapter::JsonApi, klass - end - end -end diff --git a/test/array_serializer_test.rb b/test/array_serializer_test.rb deleted file mode 100644 index 2ad55324..00000000 --- a/test/array_serializer_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'test_helper' -require_relative 'collection_serializer_test' - -module ActiveModel - class Serializer - class ArraySerializerTest < CollectionSerializerTest - extend Minitest::Assertions - def self.run_one_method(*) - _, stderr = capture_io do - super - end - if stderr !~ /NOTE: ActiveModel::Serializer::ArraySerializer.new is deprecated/ - fail Minitest::Assertion, stderr - end - end - - def collection_serializer - ArraySerializer - end - end - end -end diff --git a/test/benchmark/app.rb b/test/benchmark/app.rb deleted file mode 100644 index c39e9b4e..00000000 --- a/test/benchmark/app.rb +++ /dev/null @@ -1,65 +0,0 @@ -# https://github.com/rails-api/active_model_serializers/pull/872 -# approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against -require 'bundler/setup' - -require 'rails' -require 'active_model' -require 'active_support' -require 'active_support/json' -require 'action_controller' -require 'action_controller/test_case' -require 'action_controller/railtie' -abort "Rails application already defined: #{Rails.application.class}" if Rails.application - -class NullLogger < Logger - def initialize(*_args) - end - - def add(*_args, &_block) - end -end -class BenchmarkLogger < ActiveSupport::Logger - def initialize - @file = StringIO.new - super(@file) - end - - def messages - @file.rewind - @file.read - end -end -# ref: https://gist.github.com/bf4/8744473 -class BenchmarkApp < Rails::Application - # Set up production configuration - config.eager_load = true - config.cache_classes = true - # CONFIG: CACHE_ON={on,off} - config.action_controller.perform_caching = ENV['CACHE_ON'] != 'off' - config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) - - config.active_support.test_order = :random - config.secret_token = 'S' * 30 - config.secret_key_base = 'abc123' - config.consider_all_requests_local = false - - # otherwise deadlock occurred - config.middleware.delete 'Rack::Lock' - - # to disable log files - config.logger = NullLogger.new - config.active_support.deprecation = :log - config.log_level = :info -end - -require 'active_model_serializers' - -# Initialize app before any serializers are defined, for running across revisions. -# ref: https://github.com/rails-api/active_model_serializers/pull/1478 -Rails.application.initialize! -# HACK: Serializer::cache depends on the ActionController-dependent configs being set. -ActiveSupport.on_load(:action_controller) do - require_relative 'fixtures' -end - -require_relative 'controllers' diff --git a/test/benchmark/benchmarking_support.rb b/test/benchmark/benchmarking_support.rb deleted file mode 100644 index dd27f6c5..00000000 --- a/test/benchmark/benchmarking_support.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'benchmark/ips' -require 'json' - -# Add benchmarking runner from ruby-bench-suite -# https://github.com/ruby-bench/ruby-bench-suite/blob/master/rails/benchmarks/support/benchmark_rails.rb -module Benchmark - module ActiveModelSerializers - module TestMethods - def request(method, path) - response = Rack::MockRequest.new(BenchmarkApp).send(method, path) - if response.status.in?([404, 500]) - fail "omg, #{method}, #{path}, '#{response.status}', '#{response.body}'" - end - response - end - end - - # extend Benchmark with an `ams` method - def ams(label = nil, time:, disable_gc: true, warmup: 3, &block) - fail ArgumentError.new, 'block should be passed' unless block_given? - - if disable_gc - GC.disable - else - GC.enable - end - - report = Benchmark.ips(time, warmup, true) do |x| - x.report(label) { yield } - end - - entry = report.entries.first - - output = { - label: label, - version: ::ActiveModel::Serializer::VERSION.to_s, - rails_version: ::Rails.version.to_s, - iterations_per_second: entry.ips, - iterations_per_second_standard_deviation: entry.error_percentage, - total_allocated_objects_per_iteration: count_total_allocated_objects(&block) - }.to_json - - puts output - output - end - - def count_total_allocated_objects - if block_given? - key = - if RUBY_VERSION < '2.2' - :total_allocated_object - else - :total_allocated_objects - end - - before = GC.stat[key] - yield - after = GC.stat[key] - after - before - else - -1 - end - end - end - - extend Benchmark::ActiveModelSerializers -end diff --git a/test/benchmark/bm_active_record.rb b/test/benchmark/bm_active_record.rb deleted file mode 100644 index 0837e266..00000000 --- a/test/benchmark/bm_active_record.rb +++ /dev/null @@ -1,81 +0,0 @@ -require_relative './benchmarking_support' -require_relative './app' - -time = 10 -disable_gc = true - -# This is to disable any key transform effects that may impact performance -ActiveModelSerializers.config.key_transform = :unaltered - -########################################### -# Setup active record models -########################################## -require 'active_record' -require 'sqlite3' - -# For debugging SQL output -# ActiveRecord::Base.logger = Logger.new(STDERR) - -# Change the following to reflect your database settings -ActiveRecord::Base.establish_connection( - adapter: 'sqlite3', - database: ':memory:' -) - -# Don't show migration output when constructing fake db -ActiveRecord::Migration.verbose = false - -ActiveRecord::Schema.define do - create_table :authors, force: true do |t| - t.string :name - end - - create_table :posts, force: true do |t| - t.text :body - t.string :title - t.references :author - end - - create_table :profiles, force: true do |t| - t.text :project_url - t.text :bio - t.date :birthday - t.references :author - end -end - -class Author < ActiveRecord::Base - has_one :profile - has_many :posts -end - -class Post < ActiveRecord::Base - belongs_to :author -end - -class Profile < ActiveRecord::Base - belongs_to :author -end - -# Build out the data to serialize -author = Author.create(name: 'Preston Sego') -Profile.create(project_url: 'https://github.com/NullVoxPopuli', author: author) -50.times do - Post.create( - body: 'something about how password restrictions are evil, and less secure, and with the math to prove it.', - title: 'Your bank is does not know how to do security', - author: author - ) -end - -Benchmark.ams('AR: attributes', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::SerializableResource.new(author, adapter: :attributes, include: 'profile,posts').serializable_hash -end - -Benchmark.ams('AR: json', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::SerializableResource.new(author, adapter: :json, include: 'profile,posts').serializable_hash -end - -Benchmark.ams('AR: JSON API', time: time, disable_gc: disable_gc) do - ActiveModelSerializers::SerializableResource.new(author, adapter: :json_api, include: 'profile,posts').serializable_hash -end diff --git a/test/benchmark/bm_adapter.rb b/test/benchmark/bm_adapter.rb deleted file mode 100644 index c8bae66a..00000000 --- a/test/benchmark/bm_adapter.rb +++ /dev/null @@ -1,38 +0,0 @@ -require_relative './benchmarking_support' -require_relative './app' - -time = 10 -disable_gc = true -ActiveModelSerializers.config.key_transform = :unaltered -has_many_relationships = (0..60).map do |i| - HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') -end -has_one_relationship = HasOneRelationship.new( - id: 42, - first_name: 'Joao', - last_name: 'Moura' -) -primary_resource = PrimaryResource.new( - id: 1337, - title: 'New PrimaryResource', - virtual_attribute: nil, - body: 'Body', - has_many_relationships: has_many_relationships, - has_one_relationship: has_one_relationship -) -serializer = PrimaryResourceSerializer.new(primary_resource) - -Benchmark.ams('attributes', time: time, disable_gc: disable_gc) do - attributes = ActiveModelSerializers::Adapter::Attributes.new(serializer) - attributes.as_json -end - -Benchmark.ams('json_api', time: time, disable_gc: disable_gc) do - json_api = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - json_api.as_json -end - -Benchmark.ams('json', time: time, disable_gc: disable_gc) do - json = ActiveModelSerializers::Adapter::Json.new(serializer) - json.as_json -end diff --git a/test/benchmark/bm_caching.rb b/test/benchmark/bm_caching.rb deleted file mode 100644 index ae3ad798..00000000 --- a/test/benchmark/bm_caching.rb +++ /dev/null @@ -1,119 +0,0 @@ -require_relative './benchmarking_support' -require_relative './app' - -# https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/actionpack_router.rb -class ApiAssertion - include Benchmark::ActiveModelSerializers::TestMethods - class BadRevisionError < StandardError; end - - def valid? - caching = get_caching - caching[:body].delete('meta') - non_caching = get_non_caching - non_caching[:body].delete('meta') - assert_responses(caching, non_caching) - rescue BadRevisionError => e - msg = { error: e.message } - STDERR.puts msg - STDOUT.puts msg - exit 1 - end - - def get_status(on_off = 'on'.freeze) - get("/status/#{on_off}") - end - - def clear - get('/clear') - end - - def get_caching(on_off = 'on'.freeze) - get("/caching/#{on_off}") - end - - def get_fragment_caching(on_off = 'on'.freeze) - get("/fragment_caching/#{on_off}") - end - - def get_non_caching(on_off = 'on'.freeze) - get("/non_caching/#{on_off}") - end - - def debug(msg = '') - if block_given? && ENV['DEBUG'] =~ /\Atrue|on|0\z/i - STDERR.puts yield - else - STDERR.puts msg - end - end - - private - - def assert_responses(caching, non_caching) - assert_equal(caching[:code], 200, "Caching response failed: #{caching}") - assert_equal(caching[:body], expected, "Caching response format failed: \n+ #{caching[:body]}\n- #{expected}") - assert_equal(caching[:content_type], 'application/json; charset=utf-8', "Caching response content type failed: \n+ #{caching[:content_type]}\n- application/json") - assert_equal(non_caching[:code], 200, "Non caching response failed: #{non_caching}") - assert_equal(non_caching[:body], expected, "Non Caching response format failed: \n+ #{non_caching[:body]}\n- #{expected}") - assert_equal(non_caching[:content_type], 'application/json; charset=utf-8', "Non caching response content type failed: \n+ #{non_caching[:content_type]}\n- application/json") - end - - def get(url) - response = request(:get, url) - { code: response.status, body: JSON.load(response.body), content_type: response.content_type } - end - - def expected - @expected ||= - { - 'primary_resource' => { - 'id' => 1337, - 'title' => 'New PrimaryResource', - 'body' => 'Body', - 'virtual_attribute' => { - 'id' => 999, - 'name' => 'Free-Range Virtual Attribute' - }, - 'has_one_relationship' => { - 'id' => 42, - 'first_name' => 'Joao', - 'last_name' => 'Moura' - }, - 'has_many_relationships' => [ - { - 'id' => 1, - 'body' => 'ZOMG A HAS MANY RELATIONSHIP' - } - ] - } - } - end - - def assert_equal(expected, actual, message) - return true if expected == actual - if ENV['FAIL_ASSERTION'] =~ /\Atrue|on|0\z/i # rubocop:disable Style/GuardClause - fail BadRevisionError, message - else - STDERR.puts message unless ENV['SUMMARIZE'] - end - end -end -assertion = ApiAssertion.new -assertion.valid? -assertion.debug { assertion.get_status } - -time = 10 -{ - 'caching on: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'on'] }, - 'caching on: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'on'] }, - 'caching on: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'on'] }, - 'caching off: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'off'] }, - 'caching off: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'off'] }, - 'caching off: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'off'] } -}.each do |label, options| - assertion.clear - Benchmark.ams(label, time: time, disable_gc: options[:disable_gc]) do - assertion.send(*options[:send]) - end - assertion.debug { assertion.get_status(options[:send][-1]) } -end diff --git a/test/benchmark/bm_lookup_chain.rb b/test/benchmark/bm_lookup_chain.rb deleted file mode 100644 index 3b32727f..00000000 --- a/test/benchmark/bm_lookup_chain.rb +++ /dev/null @@ -1,83 +0,0 @@ -require_relative './benchmarking_support' -require_relative './app' - -time = 10 -disable_gc = true -ActiveModelSerializers.config.key_transform = :unaltered - -module AmsBench - module Api - module V1 - class PrimaryResourceSerializer < ActiveModel::Serializer - attributes :title, :body - - has_many :has_many_relationships - end - - class HasManyRelationshipSerializer < ActiveModel::Serializer - attribute :body - end - end - end - class PrimaryResourceSerializer < ActiveModel::Serializer - attributes :title, :body - - has_many :has_many_relationships - - class HasManyRelationshipSerializer < ActiveModel::Serializer - attribute :body - end - end -end - -resource = PrimaryResource.new( - id: 1, - title: 'title', - body: 'body', - has_many_relationships: [ - HasManyRelationship.new(id: 1, body: 'body1'), - HasManyRelationship.new(id: 2, body: 'body1') - ] -) - -serialization = lambda do - ActiveModelSerializers::SerializableResource.new(resource, serializer: AmsBench::PrimaryResourceSerializer).as_json - ActiveModelSerializers::SerializableResource.new(resource, namespace: AmsBench::Api::V1).as_json - ActiveModelSerializers::SerializableResource.new(resource).as_json -end - -def clear_cache - AmsBench::PrimaryResourceSerializer.serializers_cache.clear - AmsBench::Api::V1::PrimaryResourceSerializer.serializers_cache.clear - ActiveModel::Serializer.serializers_cache.clear -end - -configurable = lambda do - clear_cache - Benchmark.ams('Configurable Lookup Chain', time: time, disable_gc: disable_gc, &serialization) -end - -old = lambda do - clear_cache - module ActiveModel - class Serializer - def self.serializer_lookup_chain_for(klass, namespace = nil) - chain = [] - - resource_class_name = klass.name.demodulize - resource_namespace = klass.name.deconstantize - serializer_class_name = "#{resource_class_name}Serializer" - - chain.push("#{namespace}::#{serializer_class_name}") if namespace - chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer - chain.push("#{resource_namespace}::#{serializer_class_name}") - chain - end - end - end - - Benchmark.ams('Old Lookup Chain (v0.10)', time: time, disable_gc: disable_gc, &serialization) -end - -configurable.call -old.call diff --git a/test/benchmark/bm_transform.rb b/test/benchmark/bm_transform.rb deleted file mode 100644 index 97c655c0..00000000 --- a/test/benchmark/bm_transform.rb +++ /dev/null @@ -1,45 +0,0 @@ -require_relative './benchmarking_support' -require_relative './app' - -time = 10 -disable_gc = true -ActiveModelSerializers.config.key_transform = :unaltered -has_many_relationships = (0..50).map do |i| - HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') -end -has_one_relationship = HasOneRelationship.new( - id: 42, - first_name: 'Joao', - last_name: 'Moura' -) -primary_resource = PrimaryResource.new( - id: 1337, - title: 'New PrimaryResource', - virtual_attribute: nil, - body: 'Body', - has_many_relationships: has_many_relationships, - has_one_relationship: has_one_relationship -) -serializer = PrimaryResourceSerializer.new(primary_resource) -adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) -serialization = adapter.as_json - -Benchmark.ams('camel', time: time, disable_gc: disable_gc) do - CaseTransform.camel(serialization) -end - -Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do - CaseTransform.camel_lower(serialization) -end - -Benchmark.ams('dash', time: time, disable_gc: disable_gc) do - CaseTransform.dash(serialization) -end - -Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do - CaseTransform.unaltered(serialization) -end - -Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do - CaseTransform.underscore(serialization) -end diff --git a/test/benchmark/config.ru b/test/benchmark/config.ru deleted file mode 100644 index 908eb28c..00000000 --- a/test/benchmark/config.ru +++ /dev/null @@ -1,3 +0,0 @@ -require File.expand_path(['..', 'app'].join(File::SEPARATOR), __FILE__) - -run Rails.application diff --git a/test/benchmark/controllers.rb b/test/benchmark/controllers.rb deleted file mode 100644 index 81108445..00000000 --- a/test/benchmark/controllers.rb +++ /dev/null @@ -1,83 +0,0 @@ -class PrimaryResourceController < ActionController::Base - PRIMARY_RESOURCE = - begin - if ENV['BENCH_STRESS'] - has_many_relationships = (0..50).map do |i| - HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') - end - else - has_many_relationships = [HasManyRelationship.new(id: 1, body: 'ZOMG A HAS MANY RELATIONSHIP')] - end - has_one_relationship = HasOneRelationship.new(id: 42, first_name: 'Joao', last_name: 'Moura') - PrimaryResource.new(id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship) - end - - def render_with_caching_serializer - toggle_cache_status - render json: PRIMARY_RESOURCE, serializer: CachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching } - end - - def render_with_fragment_caching_serializer - toggle_cache_status - render json: PRIMARY_RESOURCE, serializer: FragmentCachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching } - end - - def render_with_non_caching_serializer - toggle_cache_status - render json: PRIMARY_RESOURCE, adapter: :json, meta: { caching: perform_caching } - end - - def render_cache_status - toggle_cache_status - # Uncomment to debug - # STDERR.puts cache_store.class - # STDERR.puts cache_dependencies - # ActiveSupport::Cache::Store.logger.debug [ActiveModelSerializers.config.cache_store, ActiveModelSerializers.config.perform_caching, CachingPrimaryResourceSerializer._cache, perform_caching, params].inspect - render json: { caching: perform_caching, meta: { cache_log: cache_messages, cache_status: cache_status } }.to_json - end - - def clear - ActionController::Base.cache_store.clear - # Test caching is on - # Uncomment to turn on logger; possible performance issue - # logger = BenchmarkLogger.new - # ActiveSupport::Cache::Store.logger = logger # seems to be the best way - # - # the below is used in some rails tests but isn't available/working in all versions, so far as I can tell - # https://github.com/rails/rails/pull/15943 - # ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args| - # logger.debug ActiveSupport::Notifications::Event.new(*args) - # end - render json: 'ok'.to_json - end - - private - - def cache_status - { - controller: perform_caching, - app: Rails.configuration.action_controller.perform_caching, - serializers: Rails.configuration.serializers.each_with_object({}) { |serializer, data| data[serializer.name] = serializer._cache.present? } - } - end - - def cache_messages - ActiveSupport::Cache::Store.logger.is_a?(BenchmarkLogger) && ActiveSupport::Cache::Store.logger.messages.split("\n") - end - - def toggle_cache_status - case params[:on] - when 'on'.freeze then self.perform_caching = true - when 'off'.freeze then self.perform_caching = false - else nil # no-op - end - end -end - -Rails.application.routes.draw do - get '/status(/:on)' => 'primary_resource#render_cache_status' - get '/clear' => 'primary_resource#clear' - get '/caching(/:on)' => 'primary_resource#render_with_caching_serializer' - get '/fragment_caching(/:on)' => 'primary_resource#render_with_fragment_caching_serializer' - get '/non_caching(/:on)' => 'primary_resource#render_with_non_caching_serializer' -end diff --git a/test/benchmark/fixtures.rb b/test/benchmark/fixtures.rb deleted file mode 100644 index c91e102d..00000000 --- a/test/benchmark/fixtures.rb +++ /dev/null @@ -1,219 +0,0 @@ -Rails.configuration.serializers = [] -class HasOneRelationshipSerializer < ActiveModel::Serializer - attributes :id, :first_name, :last_name - - has_many :primary_resources, embed: :ids - has_one :bio -end -Rails.configuration.serializers << HasOneRelationshipSerializer - -class VirtualAttributeSerializer < ActiveModel::Serializer - attributes :id, :name -end -Rails.configuration.serializers << VirtualAttributeSerializer - -class HasManyRelationshipSerializer < ActiveModel::Serializer - attributes :id, :body - - belongs_to :primary_resource - belongs_to :has_one_relationship -end -Rails.configuration.serializers << HasManyRelationshipSerializer - -class PrimaryResourceSerializer < ActiveModel::Serializer - attributes :id, :title, :body - - has_many :has_many_relationships, serializer: HasManyRelationshipSerializer - belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer - belongs_to :has_one_relationship, serializer: HasOneRelationshipSerializer - - link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } - - meta do - { - rating: 5, - favorite_count: 10 - } - end - - def virtual_attribute - VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') - end -end -Rails.configuration.serializers << PrimaryResourceSerializer - -class CachingHasOneRelationshipSerializer < HasOneRelationshipSerializer - cache key: 'writer', skip_digest: true -end -Rails.configuration.serializers << CachingHasOneRelationshipSerializer - -class CachingHasManyRelationshipSerializer < HasManyRelationshipSerializer - cache expires_in: 1.day, skip_digest: true -end -Rails.configuration.serializers << CachingHasManyRelationshipSerializer - -# see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532 -class CachingPrimaryResourceSerializer < ActiveModel::Serializer - cache key: 'primary_resource', expires_in: 0.1, skip_digest: true - - attributes :id, :title, :body - - belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer - belongs_to :has_one_relationship, serializer: CachingHasOneRelationshipSerializer - has_many :has_many_relationships, serializer: CachingHasManyRelationshipSerializer - - link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } - - meta do - { - rating: 5, - favorite_count: 10 - } - end - - def virtual_attribute - VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') - end -end -Rails.configuration.serializers << CachingPrimaryResourceSerializer - -class FragmentCachingHasOneRelationshipSerializer < HasOneRelationshipSerializer - cache key: 'writer', only: [:first_name, :last_name], skip_digest: true -end -Rails.configuration.serializers << FragmentCachingHasOneRelationshipSerializer - -class FragmentCachingHasManyRelationshipSerializer < HasManyRelationshipSerializer - cache expires_in: 1.day, except: [:body], skip_digest: true -end -Rails.configuration.serializers << CachingHasManyRelationshipSerializer - -# see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532 -class FragmentCachingPrimaryResourceSerializer < ActiveModel::Serializer - cache key: 'primary_resource', expires_in: 0.1, skip_digest: true - - attributes :id, :title, :body - - belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer - belongs_to :has_one_relationship, serializer: FragmentCachingHasOneRelationshipSerializer - has_many :has_many_relationships, serializer: FragmentCachingHasManyRelationshipSerializer - - link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } - - meta do - { - rating: 5, - favorite_count: 10 - } - end - - def virtual_attribute - VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') - end -end -Rails.configuration.serializers << FragmentCachingPrimaryResourceSerializer - -if ENV['ENABLE_ACTIVE_RECORD'] == 'true' - require 'active_record' - - ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') - ActiveRecord::Schema.define do - self.verbose = false - - create_table :virtual_attributes, force: true do |t| - t.string :name - t.timestamps null: false - end - create_table :has_one_relationships, force: true do |t| - t.string :first_name - t.string :last_name - t.timestamps null: false - end - create_table :primary_resources, force: true do |t| - t.string :title - t.text :body - t.references :has_one_relationship - t.references :virtual_attribute - t.timestamps null: false - end - create_table :has_many_relationships, force: true do |t| - t.text :body - t.references :has_one_relationship - t.references :primary_resource - t.timestamps null: false - end - end - - class HasManyRelationship < ActiveRecord::Base - belongs_to :has_one_relationship - belongs_to :primary_resource - end - - class HasOneRelationship < ActiveRecord::Base - has_many :primary_resources - has_many :has_many_relationships - end - - class PrimaryResource < ActiveRecord::Base - has_many :has_many_relationships - belongs_to :has_one_relationship - belongs_to :virtual_attribute - end - - class VirtualAttribute < ActiveRecord::Base - has_many :primary_resources - end -else - # ActiveModelSerializers::Model is a convenient - # serializable class to inherit from when making - # serializable non-activerecord objects. - class BenchmarkModel - include ActiveModel::Model - include ActiveModel::Serializers::JSON - - attr_reader :attributes - - def initialize(attributes = {}) - @attributes = attributes - super - end - - # Defaults to the downcased model name. - def id - attributes.fetch(:id) { self.class.name.downcase } - end - - # Defaults to the downcased model name and updated_at - def cache_key - attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}" } - end - - # Defaults to the time the serializer file was modified. - def updated_at - @updated_at ||= attributes.fetch(:updated_at) { File.mtime(__FILE__) } - end - - def read_attribute_for_serialization(key) - if key == :id || key == 'id' - attributes.fetch(key) { id } - else - attributes[key] - end - end - end - - class HasManyRelationship < BenchmarkModel - attr_accessor :id, :body - end - - class HasOneRelationship < BenchmarkModel - attr_accessor :id, :first_name, :last_name, :primary_resources - end - - class PrimaryResource < BenchmarkModel - attr_accessor :id, :title, :body, :has_many_relationships, :virtual_attribute, :has_one_relationship - end - - class VirtualAttribute < BenchmarkModel - attr_accessor :id, :name - end -end diff --git a/test/cache_test.rb b/test/cache_test.rb deleted file mode 100644 index f0958931..00000000 --- a/test/cache_test.rb +++ /dev/null @@ -1,651 +0,0 @@ -require 'test_helper' -require 'tmpdir' -require 'tempfile' - -module ActiveModelSerializers - class CacheTest < ActiveSupport::TestCase - class Article < ::Model - attributes :title - # To confirm error is raised when cache_key is not set and cache_key option not passed to cache - undef_method :cache_key - end - class ArticleSerializer < ActiveModel::Serializer - cache only: [:place], skip_digest: true - attributes :title - end - - class Author < ::Model - attributes :id, :name - associations :posts, :bio, :roles - end - # Instead of a primitive cache key (i.e. a string), this class - # returns a list of objects that require to be expanded themselves. - class AuthorWithExpandableCacheElements < Author - # For the test purposes it's important that #to_s for HasCacheKey differs - # between instances, hence not a Struct. - class HasCacheKey - attr_reader :cache_key - def initialize(cache_key) - @cache_key = cache_key - end - - def to_s - "HasCacheKey##{object_id}" - end - end - - def cache_key - [ - HasCacheKey.new(name), - HasCacheKey.new(id) - ] - end - end - class UncachedAuthor < Author - # To confirm cache_key is set using updated_at and cache_key option passed to cache - undef_method :cache_key - end - class AuthorSerializer < ActiveModel::Serializer - cache key: 'writer', skip_digest: true - attributes :id, :name - - has_many :posts - has_many :roles - has_one :bio - end - - class Blog < ::Model - attributes :name - associations :writer - end - class BlogSerializer < ActiveModel::Serializer - cache key: 'blog' - attributes :id, :name - - belongs_to :writer - end - - class Comment < ::Model - attributes :id, :body - associations :post, :author - - # Uses a custom non-time-based cache key - def cache_key - "comment/#{id}" - end - end - class CommentSerializer < ActiveModel::Serializer - cache expires_in: 1.day, skip_digest: true - attributes :id, :body - belongs_to :post - belongs_to :author - end - - class Post < ::Model - attributes :id, :title, :body - associations :author, :comments, :blog - end - class PostSerializer < ActiveModel::Serializer - cache key: 'post', expires_in: 0.1, skip_digest: true - attributes :id, :title, :body - - has_many :comments - belongs_to :blog - belongs_to :author - end - - class Role < ::Model - attributes :name, :description, :special_attribute - associations :author - end - class RoleSerializer < ActiveModel::Serializer - cache only: [:name, :slug], skip_digest: true - attributes :id, :name, :description - attribute :friendly_id, key: :slug - belongs_to :author - - def friendly_id - "#{object.name}-#{object.id}" - end - end - class InheritedRoleSerializer < RoleSerializer - cache key: 'inherited_role', only: [:name, :special_attribute] - attribute :special_attribute - end - - setup do - cache_store.clear - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post = Post.new(id: 'post', title: 'New Post', body: 'Body') - @bio = Bio.new(id: 1, content: 'AMS Contributor') - @author = Author.new(id: 'author', name: 'Joao M. D. Moura') - @blog = Blog.new(id: 999, name: 'Custom blog', writer: @author) - @role = Role.new(name: 'Great Author') - @location = Location.new(lat: '-23.550520', lng: '-46.633309') - @place = Place.new(name: 'Amazing Place') - @author.posts = [@post] - @author.roles = [@role] - @role.author = @author - @author.bio = @bio - @bio.author = @author - @post.comments = [@comment] - @post.author = @author - @comment.post = @post - @comment.author = @author - @post.blog = @blog - @location.place = @place - - @location_serializer = LocationSerializer.new(@location) - @bio_serializer = BioSerializer.new(@bio) - @role_serializer = RoleSerializer.new(@role) - @post_serializer = PostSerializer.new(@post) - @author_serializer = AuthorSerializer.new(@author) - @comment_serializer = CommentSerializer.new(@comment) - @blog_serializer = BlogSerializer.new(@blog) - end - - def test_explicit_cache_store - default_store = Class.new(ActiveModel::Serializer) do - cache - end - explicit_store = Class.new(ActiveModel::Serializer) do - cache cache_store: ActiveSupport::Cache::FileStore - end - - assert ActiveSupport::Cache::MemoryStore, ActiveModelSerializers.config.cache_store - assert ActiveSupport::Cache::MemoryStore, default_store.cache_store - assert ActiveSupport::Cache::FileStore, explicit_store.cache_store - end - - def test_inherited_cache_configuration - inherited_serializer = Class.new(PostSerializer) - - assert_equal PostSerializer._cache_key, inherited_serializer._cache_key - assert_equal PostSerializer._cache_options, inherited_serializer._cache_options - end - - def test_override_cache_configuration - inherited_serializer = Class.new(PostSerializer) do - cache key: 'new-key' - end - - assert_equal PostSerializer._cache_key, 'post' - assert_equal inherited_serializer._cache_key, 'new-key' - end - - def test_cache_definition - assert_equal(cache_store, @post_serializer.class._cache) - assert_equal(cache_store, @author_serializer.class._cache) - assert_equal(cache_store, @comment_serializer.class._cache) - end - - def test_cache_key_definition - assert_equal('post', @post_serializer.class._cache_key) - assert_equal('writer', @author_serializer.class._cache_key) - assert_nil(@comment_serializer.class._cache_key) - end - - def test_cache_key_interpolation_with_updated_at_when_cache_key_is_not_defined_on_object - uncached_author = UncachedAuthor.new(name: 'Joao M. D. Moura') - uncached_author_serializer = AuthorSerializer.new(uncached_author) - - render_object_with_cache(uncached_author) - key = "#{uncached_author_serializer.class._cache_key}/#{uncached_author_serializer.object.id}-#{uncached_author_serializer.object.updated_at.strftime('%Y%m%d%H%M%S%9N')}" - key = "#{key}/#{adapter.cache_key}" - assert_equal(uncached_author_serializer.attributes.to_json, cache_store.fetch(key).to_json) - end - - def test_cache_key_expansion - author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello') - same_author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello') - diff_author = AuthorWithExpandableCacheElements.new(id: 11, name: 'hello') - - author_serializer = AuthorSerializer.new(author) - same_author_serializer = AuthorSerializer.new(same_author) - diff_author_serializer = AuthorSerializer.new(diff_author) - adapter = AuthorSerializer.serialization_adapter_instance - - assert_equal(author_serializer.cache_key(adapter), same_author_serializer.cache_key(adapter)) - refute_equal(author_serializer.cache_key(adapter), diff_author_serializer.cache_key(adapter)) - end - - def test_default_cache_key_fallback - render_object_with_cache(@comment) - key = "#{@comment.cache_key}/#{adapter.cache_key}" - assert_equal(@comment_serializer.attributes.to_json, cache_store.fetch(key).to_json) - end - - def test_error_is_raised_if_cache_key_is_not_defined_on_object_or_passed_as_cache_option - article = Article.new(title: 'Must Read') - e = assert_raises ActiveModel::Serializer::UndefinedCacheKey do - render_object_with_cache(article) - end - assert_match(/ActiveModelSerializers::CacheTest::Article must define #cache_key, or the 'key:' option must be passed into 'ActiveModelSerializers::CacheTest::ArticleSerializer.cache'/, e.message) - end - - def test_cache_options_definition - assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options) - assert_nil(@blog_serializer.class._cache_options) - assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options) - end - - def test_fragment_cache_definition - assert_equal([:name, :slug], @role_serializer.class._cache_only) - assert_equal([:content], @bio_serializer.class._cache_except) - end - - def test_associations_separately_cache - cache_store.clear - assert_nil(cache_store.fetch(@post.cache_key)) - assert_nil(cache_store.fetch(@comment.cache_key)) - - Timecop.freeze(Time.current) do - render_object_with_cache(@post) - - key = "#{@post.cache_key}/#{adapter.cache_key}" - assert_equal(@post_serializer.attributes, cache_store.fetch(key)) - key = "#{@comment.cache_key}/#{adapter.cache_key}" - assert_equal(@comment_serializer.attributes, cache_store.fetch(key)) - end - end - - def test_associations_cache_when_updated - Timecop.freeze(Time.current) do - # Generate a new Cache of Post object and each objects related to it. - render_object_with_cache(@post) - - # Check if it cached the objects separately - key = "#{@post.cache_key}/#{adapter.cache_key}" - assert_equal(@post_serializer.attributes, cache_store.fetch(key)) - key = "#{@comment.cache_key}/#{adapter.cache_key}" - assert_equal(@comment_serializer.attributes, cache_store.fetch(key)) - - # Simulating update on comments relationship with Post - new_comment = Comment.new(id: 2567, body: 'ZOMG A NEW COMMENT') - new_comment_serializer = CommentSerializer.new(new_comment) - @post.comments = [new_comment] - - # Ask for the serialized object - render_object_with_cache(@post) - - # Check if the the new comment was cached - key = "#{new_comment.cache_key}/#{adapter.cache_key}" - assert_equal(new_comment_serializer.attributes, cache_store.fetch(key)) - key = "#{@post.cache_key}/#{adapter.cache_key}" - assert_equal(@post_serializer.attributes, cache_store.fetch(key)) - end - end - - def test_fragment_fetch_with_virtual_associations - expected_result = { - id: @location.id, - lat: @location.lat, - lng: @location.lng, - address: 'Nowhere' - } - - hash = render_object_with_cache(@location) - - assert_equal(hash, expected_result) - key = "#{@location.cache_key}/#{adapter.cache_key}" - assert_equal({ address: 'Nowhere' }, cache_store.fetch(key)) - end - - def test_fragment_cache_with_inheritance - inherited = render_object_with_cache(@role, serializer: InheritedRoleSerializer) - base = render_object_with_cache(@role) - - assert_includes(inherited.keys, :special_attribute) - refute_includes(base.keys, :special_attribute) - end - - def test_uses_adapter_in_cache_key - render_object_with_cache(@post) - key = "#{@post.cache_key}/#{adapter.class.to_s.demodulize.underscore}" - assert_equal(@post_serializer.attributes, cache_store.fetch(key)) - end - - # Based on original failing test by @kevintyll - # rubocop:disable Metrics/AbcSize - def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attributes - Object.const_set(:Alert, Class.new(ActiveModelSerializers::Model) do - attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at - end) - Object.const_set(:UncachedAlertSerializer, Class.new(ActiveModel::Serializer) do - attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at - end) - Object.const_set(:AlertSerializer, Class.new(UncachedAlertSerializer) do - cache - end) - - alert = Alert.new( - id: 1, - status: 'fail', - resource: 'resource-1', - started_at: Time.new(2016, 3, 31, 21, 36, 35, 0), - ended_at: nil, - updated_at: Time.new(2016, 3, 31, 21, 27, 35, 0), - created_at: Time.new(2016, 3, 31, 21, 37, 35, 0) - ) - - expected_fetch_attributes = { - id: 1, - status: 'fail', - resource: 'resource-1', - started_at: alert.started_at, - ended_at: nil, - updated_at: alert.updated_at, - created_at: alert.created_at - }.with_indifferent_access - expected_cached_jsonapi_attributes = { - id: '1', - type: 'alerts', - attributes: { - status: 'fail', - resource: 'resource-1', - started_at: alert.started_at, - ended_at: nil, - updated_at: alert.updated_at, - created_at: alert.created_at - } - }.with_indifferent_access - - # Assert attributes are serialized correctly - serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :attributes) - attributes_serialization = serializable_alert.as_json.with_indifferent_access - assert_equal expected_fetch_attributes, alert.attributes - assert_equal alert.attributes, attributes_serialization - attributes_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter) - assert_equal attributes_serialization, cache_store.fetch(attributes_cache_key).with_indifferent_access - - serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :json_api) - jsonapi_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter) - # Assert cache keys differ - refute_equal attributes_cache_key, jsonapi_cache_key - # Assert (cached) serializations differ - jsonapi_serialization = serializable_alert.as_json - assert_equal alert.status, jsonapi_serialization.fetch(:data).fetch(:attributes).fetch(:status) - serializable_alert = serializable(alert, serializer: UncachedAlertSerializer, adapter: :json_api) - assert_equal serializable_alert.as_json, jsonapi_serialization - - cached_serialization = cache_store.fetch(jsonapi_cache_key).with_indifferent_access - assert_equal expected_cached_jsonapi_attributes, cached_serialization - ensure - Object.send(:remove_const, :Alert) - Object.send(:remove_const, :AlertSerializer) - Object.send(:remove_const, :UncachedAlertSerializer) - end - # rubocop:enable Metrics/AbcSize - - def test_uses_file_digest_in_cache_key - render_object_with_cache(@blog) - file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) - key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}" - assert_equal(@blog_serializer.attributes, cache_store.fetch(key)) - end - - def test_cache_digest_definition - file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) - assert_equal(file_digest, @post_serializer.class._cache_digest) - end - - def test_object_cache_keys - serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment]) - include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true) - - actual = ActiveModel::Serializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_directive) - - assert_equal 3, actual.size - expected_key = "comment/1/#{serializable.adapter.cache_key}" - assert actual.any? { |key| key == expected_key }, "actual '#{actual}' should include #{expected_key}" - expected_key = %r{post/post-\d+} - assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'" - expected_key = %r{author/author-\d+} - assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'" - end - - # rubocop:disable Metrics/AbcSize - def test_fetch_attributes_from_cache - serializers = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment]) - - Timecop.freeze(Time.current) do - render_object_with_cache(@comment) - - options = {} - adapter_options = {} - adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options) - serializers.serializable_hash(adapter_options, options, adapter_instance) - cached_attributes = adapter_options.fetch(:cached_attributes).with_indifferent_access - - include_directive = ActiveModelSerializers.default_include_directive - manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive).with_indifferent_access - assert_equal manual_cached_attributes, cached_attributes - - assert_equal cached_attributes["#{@comment.cache_key}/#{adapter_instance.cache_key}"], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes - assert_equal cached_attributes["#{@comment.post.cache_key}/#{adapter_instance.cache_key}"], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes - - writer = @comment.post.blog.writer - writer_cache_key = writer.cache_key - assert_equal cached_attributes["#{writer_cache_key}/#{adapter_instance.cache_key}"], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes - end - end - # rubocop:enable Metrics/AbcSize - - def test_cache_read_multi_with_fragment_cache_enabled - post_serializer = Class.new(ActiveModel::Serializer) do - cache except: [:body] - end - - serializers = ActiveModel::Serializer::CollectionSerializer.new([@post, @post], serializer: post_serializer) - - Timecop.freeze(Time.current) do - # Warming up. - options = {} - adapter_options = {} - adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options) - serializers.serializable_hash(adapter_options, options, adapter_instance) - - # Should find something with read_multi now - adapter_options = {} - serializers.serializable_hash(adapter_options, options, adapter_instance) - cached_attributes = adapter_options.fetch(:cached_attributes) - - include_directive = ActiveModelSerializers.default_include_directive - manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive) - - refute_equal 0, cached_attributes.size - refute_equal 0, manual_cached_attributes.size - assert_equal manual_cached_attributes, cached_attributes - end - end - - def test_serializer_file_path_on_nix - path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb' - caller_line = "#{path}:1:in `'" - assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path - end - - def test_serializer_file_path_on_windows - path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb' - caller_line = "#{path}:1:in `'" - assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path - end - - def test_serializer_file_path_with_space - path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb' - caller_line = "#{path}:1:in `'" - assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path - end - - def test_serializer_file_path_with_submatch - # The submatch in the path ensures we're using a correctly greedy regexp. - path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb' - caller_line = "#{path}:1:in `'" - assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path - end - - def test_digest_caller_file - contents = "puts 'AMS rocks'!" - dir = Dir.mktmpdir('space char') - file = Tempfile.new('some_ruby.rb', dir) - file.write(contents) - path = file.path - caller_line = "#{path}:1:in `'" - file.close - assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents) - ensure - file.unlink - FileUtils.remove_entry dir - end - - def test_warn_on_serializer_not_defined_in_file - called = false - serializer = Class.new(ActiveModel::Serializer) - assert_output(nil, /_cache_digest/) do - serializer.digest_caller_file('') - called = true - end - assert called - end - - def test_cached_false_without_cache_store - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = nil - end - refute cached_serializer.class.cache_enabled? - end - - def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - end - assert cached_serializer.class.cache_enabled? - end - - def test_cached_false_with_cache_store_and_with_cache_only - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - serializer._cache_only = [:name] - end - refute cached_serializer.class.cache_enabled? - end - - def test_cached_false_with_cache_store_and_with_cache_except - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - serializer._cache_except = [:content] - end - refute cached_serializer.class.cache_enabled? - end - - def test_fragment_cached_false_without_cache_store - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = nil - serializer._cache_only = [:name] - end - refute cached_serializer.class.fragment_cache_enabled? - end - - def test_fragment_cached_true_with_cache_store_and_cache_only - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - serializer._cache_only = [:name] - end - assert cached_serializer.class.fragment_cache_enabled? - end - - def test_fragment_cached_true_with_cache_store_and_cache_except - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - serializer._cache_except = [:content] - end - assert cached_serializer.class.fragment_cache_enabled? - end - - def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only - cached_serializer = build_cached_serializer do |serializer| - serializer._cache = Object - serializer._cache_except = [:content] - serializer._cache_only = [:name] - end - refute cached_serializer.class.fragment_cache_enabled? - end - - def test_fragment_fetch_with_virtual_attributes - author = Author.new(name: 'Joao M. D. Moura') - role = Role.new(name: 'Great Author', description: nil) - role.author = [author] - role_serializer = RoleSerializer.new(role) - adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(role_serializer) - expected_result = { - id: role.id, - description: role.description, - slug: "#{role.name}-#{role.id}", - name: role.name - } - cache_store.clear - - role_hash = role_serializer.fetch_attributes_fragment(adapter_instance) - assert_equal(role_hash, expected_result) - - role.id = 'this has been updated' - role.name = 'this was cached' - - role_hash = role_serializer.fetch_attributes_fragment(adapter_instance) - assert_equal(expected_result.merge(id: role.id), role_hash) - end - - def test_fragment_fetch_with_except - adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@bio_serializer) - expected_result = { - id: @bio.id, - rating: nil, - content: @bio.content - } - cache_store.clear - - bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance) - assert_equal(expected_result, bio_hash) - - @bio.content = 'this has been updated' - @bio.rating = 'this was cached' - - bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance) - assert_equal(expected_result.merge(content: @bio.content), bio_hash) - end - - def test_fragment_fetch_with_namespaced_object - @spam = Spam::UnrelatedLink.new(id: 'spam-id-1') - @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam) - adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer) - @spam_hash = @spam_serializer.fetch_attributes_fragment(adapter_instance) - expected_result = { - id: @spam.id - } - assert_equal(@spam_hash, expected_result) - end - - private - - def cache_store - ActiveModelSerializers.config.cache_store - end - - def build_cached_serializer - serializer = Class.new(ActiveModel::Serializer) - serializer._cache_key = nil - serializer._cache_options = nil - yield serializer if block_given? - serializer.new(Object) - end - - def render_object_with_cache(obj, options = {}) - @serializable_resource = serializable(obj, options) - @serializable_resource.serializable_hash - end - - def adapter - @serializable_resource.adapter - end - end -end diff --git a/test/collection_serializer_test.rb b/test/collection_serializer_test.rb deleted file mode 100644 index cdbebb15..00000000 --- a/test/collection_serializer_test.rb +++ /dev/null @@ -1,123 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class CollectionSerializerTest < ActiveSupport::TestCase - class SingularModel < ::Model; end - class SingularModelSerializer < ActiveModel::Serializer - end - class HasManyModel < ::Model - associations :singular_models - end - class HasManyModelSerializer < ActiveModel::Serializer - has_many :singular_models - - def custom_options - instance_options - end - end - class MessagesSerializer < ActiveModel::Serializer - type 'messages' - end - - def setup - @singular_model = SingularModel.new - @has_many_model = HasManyModel.new - @resource = build_named_collection @singular_model, @has_many_model - @serializer = collection_serializer.new(@resource, some: :options) - end - - def collection_serializer - CollectionSerializer - end - - def build_named_collection(*resource) - resource.define_singleton_method(:name) { 'MeResource' } - resource - end - - def test_has_object_reader_serializer_interface - assert_equal @serializer.object, @resource - end - - def test_respond_to_each - assert_respond_to @serializer, :each - end - - def test_each_object_should_be_serialized_with_appropriate_serializer - serializers = @serializer.to_a - - assert_kind_of SingularModelSerializer, serializers.first - assert_kind_of SingularModel, serializers.first.object - - assert_kind_of HasManyModelSerializer, serializers.last - assert_kind_of HasManyModel, serializers.last.object - - assert_equal :options, serializers.last.custom_options[:some] - end - - def test_serializer_option_not_passed_to_each_serializer - serializers = collection_serializer.new([@has_many_model], serializer: HasManyModelSerializer).to_a - - refute serializers.first.custom_options.key?(:serializer) - end - - def test_root_default - @serializer = collection_serializer.new([@singular_model, @has_many_model]) - assert_nil @serializer.root - end - - def test_root - expected = 'custom_root' - @serializer = collection_serializer.new([@singular_model, @has_many_model], root: expected) - assert_equal expected, @serializer.root - end - - def test_root_with_no_serializers - expected = 'custom_root' - @serializer = collection_serializer.new([], root: expected) - assert_equal expected, @serializer.root - end - - def test_json_key_with_resource_with_serializer - singular_key = @serializer.send(:serializers).first.json_key - assert_equal singular_key.pluralize, @serializer.json_key - end - - def test_json_key_with_resource_with_name_and_no_serializers - serializer = collection_serializer.new(build_named_collection) - assert_equal 'me_resources', serializer.json_key - end - - def test_json_key_with_resource_with_nil_name_and_no_serializers - resource = [] - resource.define_singleton_method(:name) { nil } - serializer = collection_serializer.new(resource) - assert_nil serializer.json_key - end - - def test_json_key_with_resource_without_name_and_no_serializers - serializer = collection_serializer.new([]) - assert_nil serializer.json_key - end - - def test_json_key_with_empty_resources_with_serializer - resource = [] - serializer = collection_serializer.new(resource, serializer: MessagesSerializer) - assert_equal 'messages', serializer.json_key - end - - def test_json_key_with_root - expected = 'custom_root' - serializer = collection_serializer.new(@resource, root: expected) - assert_equal expected, serializer.json_key - end - - def test_json_key_with_root_and_no_serializers - expected = 'custom_root' - serializer = collection_serializer.new(build_named_collection, root: expected) - assert_equal expected, serializer.json_key - end - end - end -end diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb deleted file mode 100644 index 9dc3830d..00000000 --- a/test/fixtures/active_record.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'active_record' - -ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') -ActiveRecord::Schema.define do - self.verbose = false - create_table :posts, force: true do |t| - t.string :title - t.text :body - t.references :author - t.timestamps null: false - end - create_table :authors, force: true do |t| - t.string :name - t.timestamps null: false - end - create_table :comments, force: true do |t| - t.text :contents - t.references :author - t.references :post - t.timestamp null: false - end - create_table :employees, force: true do |t| - t.string :name - t.string :email - t.timestamp null: false - end - create_table :object_tags, force: true do |t| - t.string :poly_tag_id - t.string :taggable_type - t.string :taggable_id - t.timestamp null: false - end - create_table :poly_tags, force: true do |t| - t.string :phrase - t.timestamp null: false - end - create_table :pictures, force: true do |t| - t.string :title - t.string :imageable_type - t.string :imageable_id - t.timestamp null: false - end -end - -module ARModels - class Post < ActiveRecord::Base - has_many :comments - belongs_to :author - end - class PostSerializer < ActiveModel::Serializer - attributes :id, :title, :body - - has_many :comments - belongs_to :author - end - - class Comment < ActiveRecord::Base - belongs_to :post - belongs_to :author - end - class CommentSerializer < ActiveModel::Serializer - attributes :id, :contents - - belongs_to :author - end - - class Author < ActiveRecord::Base - has_many :posts - end - class AuthorSerializer < ActiveModel::Serializer - attributes :id, :name - - has_many :posts - end -end - -class Employee < ActiveRecord::Base - has_many :pictures, as: :imageable - has_many :object_tags, as: :taggable -end - -class PolymorphicSimpleSerializer < ActiveModel::Serializer - attributes :id -end - -class ObjectTag < ActiveRecord::Base - belongs_to :poly_tag - belongs_to :taggable, polymorphic: true -end -class PolymorphicObjectTagSerializer < ActiveModel::Serializer - attributes :id - has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true -end - -class PolyTag < ActiveRecord::Base - has_many :object_tags -end -class PolymorphicTagSerializer < ActiveModel::Serializer - attributes :id, :phrase - has_many :object_tags, serializer: PolymorphicObjectTagSerializer -end - -class Picture < ActiveRecord::Base - belongs_to :imageable, polymorphic: true - has_many :object_tags, as: :taggable -end -class PolymorphicHasManySerializer < ActiveModel::Serializer - attributes :id, :name -end -class PolymorphicBelongsToSerializer < ActiveModel::Serializer - attributes :id, :title - has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true -end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb deleted file mode 100644 index 6245ad23..00000000 --- a/test/fixtures/poro.rb +++ /dev/null @@ -1,225 +0,0 @@ -class Model < ActiveModelSerializers::Model - rand(2).zero? && derive_attributes_from_names_and_fix_accessors - - attr_writer :id - - # At this time, just for organization of intent - class_attribute :association_names - self.association_names = [] - - def self.associations(*names) - self.association_names |= names.map(&:to_sym) - # Silence redefinition of methods warnings - ActiveModelSerializers.silence_warnings do - attr_accessor(*names) - end - end - - def associations - association_names.each_with_object({}) do |association_name, result| - result[association_name] = public_send(association_name).freeze - end.with_indifferent_access.freeze - end - - def attributes - super.except(*association_names) - end -end - -# see -# https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/errors.rb -# The below allows you to do: -# -# model = ModelWithErrors.new -# model.validate! # => ["cannot be nil"] -# model.errors.full_messages # => ["name cannot be nil"] -class ModelWithErrors < Model - attributes :name -end - -class Profile < Model - attributes :name, :description - associations :comments -end -class ProfileSerializer < ActiveModel::Serializer - attributes :name, :description -end -class ProfilePreviewSerializer < ActiveModel::Serializer - attributes :name -end - -class Author < Model - attributes :name - associations :posts, :bio, :roles, :comments -end -class AuthorSerializer < ActiveModel::Serializer - cache key: 'writer', skip_digest: true - attribute :id - attribute :name - - has_many :posts - has_many :roles - has_one :bio -end -class AuthorPreviewSerializer < ActiveModel::Serializer - attributes :id - has_many :posts -end - -class Comment < Model - attributes :body, :date - associations :post, :author, :likes -end -class CommentSerializer < ActiveModel::Serializer - cache expires_in: 1.day, skip_digest: true - attributes :id, :body - belongs_to :post - belongs_to :author -end -class CommentPreviewSerializer < ActiveModel::Serializer - attributes :id - - belongs_to :post -end - -class Post < Model - attributes :title, :body - associations :author, :comments, :blog, :tags, :related -end -class PostSerializer < ActiveModel::Serializer - cache key: 'post', expires_in: 0.1, skip_digest: true - attributes :id, :title, :body - - has_many :comments - belongs_to :blog - belongs_to :author - - def blog - Blog.new(id: 999, name: 'Custom blog') - end -end -class SpammyPostSerializer < ActiveModel::Serializer - attributes :id - has_many :related -end -class PostPreviewSerializer < ActiveModel::Serializer - attributes :title, :body, :id - - has_many :comments, serializer: ::CommentPreviewSerializer - belongs_to :author, serializer: ::AuthorPreviewSerializer -end -class PostWithCustomKeysSerializer < ActiveModel::Serializer - attributes :id - has_many :comments, key: :reviews - belongs_to :author, key: :writer - has_one :blog, key: :site -end - -class Bio < Model - attributes :content, :rating - associations :author -end -class BioSerializer < ActiveModel::Serializer - cache except: [:content], skip_digest: true - attributes :id, :content, :rating - - belongs_to :author -end - -class Blog < Model - attributes :name, :type, :special_attribute - associations :writer, :articles -end -class BlogSerializer < ActiveModel::Serializer - cache key: 'blog' - attributes :id, :name - - belongs_to :writer - has_many :articles -end -class AlternateBlogSerializer < ActiveModel::Serializer - attribute :id - attribute :name, key: :title -end -class CustomBlogSerializer < ActiveModel::Serializer - attribute :id - attribute :special_attribute - has_many :articles -end - -class Role < Model - attributes :name, :description, :special_attribute - associations :author -end -class RoleSerializer < ActiveModel::Serializer - cache only: [:name, :slug], skip_digest: true - attributes :id, :name, :description - attribute :friendly_id, key: :slug - belongs_to :author - - def friendly_id - "#{object.name}-#{object.id}" - end -end - -class Location < Model - attributes :lat, :lng - associations :place -end -class LocationSerializer < ActiveModel::Serializer - cache only: [:address], skip_digest: true - attributes :id, :lat, :lng - - belongs_to :place, key: :address - - def place - 'Nowhere' - end -end - -class Place < Model - attributes :name - associations :locations -end -class PlaceSerializer < ActiveModel::Serializer - attributes :id, :name - has_many :locations -end - -class Like < Model - attributes :time - associations :likeable -end -class LikeSerializer < ActiveModel::Serializer - attributes :id, :time - belongs_to :likeable -end - -module Spam - class UnrelatedLink < Model - end - class UnrelatedLinkSerializer < ActiveModel::Serializer - cache only: [:id] - attributes :id - end -end - -class VirtualValue < Model; end -class VirtualValueSerializer < ActiveModel::Serializer - attributes :id - has_many :reviews, virtual_value: [{ type: 'reviews', id: '1' }, - { type: 'reviews', id: '2' }] - has_one :maker, virtual_value: { type: 'makers', id: '1' } - - def reviews - end - - def maker - end -end - -class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer - def json_key - 'paginated' - end -end diff --git a/test/generators/scaffold_controller_generator_test.rb b/test/generators/scaffold_controller_generator_test.rb deleted file mode 100644 index 183bb4f6..00000000 --- a/test/generators/scaffold_controller_generator_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'test_helper' -require 'generators/rails/resource_override' - -class ResourceGeneratorTest < Rails::Generators::TestCase - destination File.expand_path('../../../tmp/generators', __FILE__) - setup :prepare_destination, :copy_routes - - tests Rails::Generators::ResourceGenerator - arguments %w(account) - - def test_serializer_file_is_generated - run_generator - - assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ - end - - private - - def copy_routes - config_dir = File.join(destination_root, 'config') - FileUtils.mkdir_p(config_dir) - File.write(File.join(config_dir, 'routes.rb'), 'Rails.application.routes.draw {}') - end -end diff --git a/test/generators/serializer_generator_test.rb b/test/generators/serializer_generator_test.rb deleted file mode 100644 index eef4a41e..00000000 --- a/test/generators/serializer_generator_test.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'test_helper' -require 'generators/rails/resource_override' -require 'generators/rails/serializer_generator' - -class SerializerGeneratorTest < Rails::Generators::TestCase - destination File.expand_path('../../../tmp/generators', __FILE__) - setup :prepare_destination - - tests Rails::Generators::SerializerGenerator - arguments %w(account name:string description:text business:references) - - def test_generates_a_serializer - run_generator - assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ - end - - def test_generates_a_namespaced_serializer - run_generator ['admin/account'] - assert_file 'app/serializers/admin/account_serializer.rb', /class Admin::AccountSerializer < ActiveModel::Serializer/ - end - - def test_uses_application_serializer_if_one_exists - stub_safe_constantize(expected: 'ApplicationSerializer') do - run_generator - assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ApplicationSerializer/ - end - end - - def test_uses_given_parent - Object.const_set(:ApplicationSerializer, Class.new) - run_generator ['Account', '--parent=MySerializer'] - assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < MySerializer/ - ensure - Object.send :remove_const, :ApplicationSerializer - end - - def test_generates_attributes_and_associations - run_generator - assert_file 'app/serializers/account_serializer.rb' do |serializer| - assert_match(/^ attributes :id, :name, :description$/, serializer) - assert_match(/^ has_one :business$/, serializer) - assert_match(/^end\n*\z/, serializer) - end - end - - def test_with_no_attributes_does_not_add_extra_space - run_generator ['account'] - assert_file 'app/serializers/account_serializer.rb' do |content| - if RUBY_PLATFORM =~ /mingw/ - assert_no_match(/\r\n\r\nend/, content) - else - assert_no_match(/\n\nend/, content) - end - end - end - - private - - def stub_safe_constantize(expected:) - String.class_eval do - alias_method :old, :safe_constantize - end - String.send(:define_method, :safe_constantize) do - Class if self == expected - end - - yield - ensure - String.class_eval do - undef_method :safe_constantize - alias_method :safe_constantize, :old - undef_method :old - end - end -end diff --git a/test/grape_test.rb b/test/grape_test.rb deleted file mode 100644 index 4851e57a..00000000 --- a/test/grape_test.rb +++ /dev/null @@ -1,196 +0,0 @@ -require 'test_helper' -TestHelper.silence_warnings do - require 'grape' -end -require 'grape/active_model_serializers' -require 'kaminari' -require 'kaminari/hooks' -::Kaminari::Hooks.init - -module ActiveModelSerializers - class GrapeTest < ActiveSupport::TestCase - include Rack::Test::Methods - module Models - def self.model1 - ARModels::Post.new(id: 1, title: 'Dummy Title', body: 'Lorem Ipsum') - end - - def self.model2 - ARModels::Post.new(id: 2, title: 'Second Dummy Title', body: 'Second Lorem Ipsum') - end - - def self.all - @all ||= - begin - model1.save! - model2.save! - ARModels::Post.all - end - end - - def self.reset_all - ARModels::Post.delete_all - @all = nil - end - - def self.collection_per - 2 - end - - def self.collection - @collection ||= - begin - Kaminari.paginate_array( - [ - Profile.new(id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), - Profile.new(id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), - Profile.new(id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3'), - Profile.new(id: 4, name: 'Name 4', description: 'Description 4', comments: 'Comments 4'), - Profile.new(id: 5, name: 'Name 5', description: 'Description 5', comments: 'Comments 5') - ] - ).page(1).per(collection_per) - end - end - end - - class GrapeTest < Grape::API - format :json - TestHelper.silence_warnings do - include Grape::ActiveModelSerializers - end - - def self.resources(*) - TestHelper.silence_warnings do - super - end - end - - resources :grape do - get '/render' do - render Models.model1 - end - - get '/render_with_json_api' do - post = Models.model1 - render post, meta: { page: 1, total_pages: 2 }, adapter: :json_api - end - - get '/render_array_with_json_api' do - posts = Models.all - render posts, adapter: :json_api - end - - get '/render_collection_with_json_api' do - posts = Models.collection - render posts, adapter: :json_api - end - - get '/render_with_implicit_formatter' do - Models.model1 - end - - get '/render_array_with_implicit_formatter' do - Models.all - end - - get '/render_collection_with_implicit_formatter' do - Models.collection - end - end - end - - def app - Grape::Middleware::Globals.new(GrapeTest.new) - end - - extend Minitest::Assertions - def self.run_one_method(*) - _, stderr = capture_io do - super - end - fail Minitest::Assertion, stderr if stderr !~ /grape/ - end - - def test_formatter_returns_json - get '/grape/render' - - post = Models.model1 - serializable_resource = serializable(post) - - assert last_response.ok? - assert_equal serializable_resource.to_json, last_response.body - end - - def test_render_helper_passes_through_options_correctly - get '/grape/render_with_json_api' - - post = Models.model1 - serializable_resource = serializable(post, serializer: ARModels::PostSerializer, adapter: :json_api, meta: { page: 1, total_pages: 2 }) - - assert last_response.ok? - assert_equal serializable_resource.to_json, last_response.body - end - - def test_formatter_handles_arrays - get '/grape/render_array_with_json_api' - - posts = Models.all - serializable_resource = serializable(posts, adapter: :json_api) - - assert last_response.ok? - assert_equal serializable_resource.to_json, last_response.body - ensure - Models.reset_all - end - - def test_formatter_handles_collections - get '/grape/render_collection_with_json_api' - assert last_response.ok? - - representation = JSON.parse(last_response.body) - assert representation.include?('data') - assert representation['data'].count == Models.collection_per - assert representation.include?('links') - assert representation['links'].count > 0 - end - - def test_implicit_formatter - post = Models.model1 - serializable_resource = serializable(post, adapter: :json_api) - - with_adapter :json_api do - get '/grape/render_with_implicit_formatter' - end - - assert last_response.ok? - assert_equal serializable_resource.to_json, last_response.body - end - - def test_implicit_formatter_handles_arrays - posts = Models.all - serializable_resource = serializable(posts, adapter: :json_api) - - with_adapter :json_api do - get '/grape/render_array_with_implicit_formatter' - end - - assert last_response.ok? - assert_equal serializable_resource.to_json, last_response.body - ensure - Models.reset_all - end - - def test_implicit_formatter_handles_collections - with_adapter :json_api do - get '/grape/render_collection_with_implicit_formatter' - end - - representation = JSON.parse(last_response.body) - assert last_response.ok? - assert representation.include?('data') - assert representation['data'].count == Models.collection_per - assert representation.include?('links') - assert representation['links'].count > 0 - end - end -end diff --git a/test/lint_test.rb b/test/lint_test.rb deleted file mode 100644 index d404ccec..00000000 --- a/test/lint_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class LintTest < ActiveSupport::TestCase - include ActiveModel::Serializer::Lint::Tests - - class CompliantResource - def serializable_hash(options = nil) - end - - def read_attribute_for_serialization(name) - end - - def as_json(options = nil) - end - - def to_json(options = nil) - end - - def cache_key - end - - def id - end - - def updated_at - end - - def errors - end - - def self.human_attribute_name(_, _ = {}) - end - - def self.lookup_ancestors - end - - def self.model_name - @_model_name ||= ActiveModel::Name.new(self) - end - end - - def setup - @resource = CompliantResource.new - end - end - end -end diff --git a/test/logger_test.rb b/test/logger_test.rb deleted file mode 100644 index a15227bb..00000000 --- a/test/logger_test.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class LoggerTest < ActiveSupport::TestCase - def test_logger_is_set_to_action_controller_logger_when_initializer_runs - assert_equal $action_controller_logger, ActionController::Base.logger # rubocop:disable Style/GlobalVars - end - - def test_logger_can_be_set - original_logger = ActiveModelSerializers.logger - logger = Logger.new(STDOUT) - - ActiveModelSerializers.logger = logger - - assert_equal ActiveModelSerializers.logger, logger - ensure - ActiveModelSerializers.logger = original_logger - end - end -end diff --git a/test/poro_test.rb b/test/poro_test.rb deleted file mode 100644 index e5fba858..00000000 --- a/test/poro_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -class PoroTest < ActiveSupport::TestCase - include ActiveModel::Serializer::Lint::Tests - - def setup - @resource = Model.new - end -end diff --git a/test/serializable_resource_test.rb b/test/serializable_resource_test.rb deleted file mode 100644 index ab12bc27..00000000 --- a/test/serializable_resource_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'test_helper' - -module ActiveModelSerializers - class SerializableResourceTest < ActiveSupport::TestCase - def setup - @resource = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - @serializer = ProfileSerializer.new(@resource) - @adapter = ActiveModelSerializers::Adapter.create(@serializer) - @serializable_resource = SerializableResource.new(@resource) - end - - def test_deprecation - assert_output(nil, /deprecated/) do - deprecated_serializable_resource = ActiveModel::SerializableResource.new(@resource) - assert_equal(@serializable_resource.as_json, deprecated_serializable_resource.as_json) - end - end - - def test_serializable_resource_delegates_serializable_hash_to_the_adapter - options = nil - assert_equal @adapter.serializable_hash(options), @serializable_resource.serializable_hash(options) - end - - def test_serializable_resource_delegates_to_json_to_the_adapter - options = nil - assert_equal @adapter.to_json(options), @serializable_resource.to_json(options) - end - - def test_serializable_resource_delegates_as_json_to_the_adapter - options = nil - assert_equal @adapter.as_json(options), @serializable_resource.as_json(options) - end - - def test_use_adapter_with_adapter_option - assert SerializableResource.new(@resource, adapter: 'json').use_adapter? - end - - def test_use_adapter_with_adapter_option_as_false - refute SerializableResource.new(@resource, adapter: false).use_adapter? - end - - class SerializableResourceErrorsTest < Minitest::Test - def test_serializable_resource_with_errors - 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 - ) - expected_response_document = { - errors: [ - { source: { pointer: '/data/attributes/name' }, detail: 'must be awesome' } - ] - } - assert_equal serializable_resource.as_json(options), expected_response_document - end - - def test_serializable_resource_with_collection_containing_errors - options = nil - resources = [] - resources << resource = ModelWithErrors.new - resource.errors.add(:title, 'must be amazing') - resources << ModelWithErrors.new - serializable_resource = SerializableResource.new( - resources, serializer: ActiveModel::Serializer::ErrorsSerializer, - each_serializer: ActiveModel::Serializer::ErrorSerializer, - adapter: :json_api - ) - expected_response_document = { - errors: [ - { source: { pointer: '/data/attributes/title' }, detail: 'must be amazing' } - ] - } - assert_equal serializable_resource.as_json(options), expected_response_document - end - end - end -end diff --git a/test/serializers/association_macros_test.rb b/test/serializers/association_macros_test.rb deleted file mode 100644 index 3d5f05c5..00000000 --- a/test/serializers/association_macros_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class AssociationMacrosTest < ActiveSupport::TestCase - class AuthorSummarySerializer < ActiveModel::Serializer; end - - class AssociationsTestSerializer < Serializer - belongs_to :author, serializer: AuthorSummarySerializer - has_many :comments - has_one :category - end - - def before_setup - @reflections = AssociationsTestSerializer._reflections.values - end - - def test_has_one_defines_reflection - has_one_reflection = HasOneReflection.new(:category, {}) - - assert_includes(@reflections, has_one_reflection) - end - - def test_has_many_defines_reflection - has_many_reflection = HasManyReflection.new(:comments, {}) - - assert_includes(@reflections, has_many_reflection) - end - - def test_belongs_to_defines_reflection - belongs_to_reflection = BelongsToReflection.new(:author, serializer: AuthorSummarySerializer) - - assert_includes(@reflections, belongs_to_reflection) - end - end - end -end diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb deleted file mode 100644 index c1b164b8..00000000 --- a/test/serializers/associations_test.rb +++ /dev/null @@ -1,424 +0,0 @@ -require 'test_helper' -module ActiveModel - class Serializer - class AssociationsTest < ActiveSupport::TestCase - class ModelWithoutSerializer < ::Model - attributes :id, :name - end - - def setup - @author = Author.new(name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(name: 'AMS Blog') - @post = Post.new(title: 'New Post', body: 'Body') - @tag = ModelWithoutSerializer.new(id: 'tagid', name: '#hashtagged') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.tags = [@tag] - @post.blog = @blog - @comment.post = @post - @comment.author = nil - @post.author = @author - @author.posts = [@post] - - @post_serializer = PostSerializer.new(@post, custom_options: true) - @author_serializer = AuthorSerializer.new(@author) - @comment_serializer = CommentSerializer.new(@comment) - end - - def test_has_many_and_has_one - @author_serializer.associations.each do |association| - key = association.key - serializer = association.lazy_association.serializer - - case key - when :posts - assert_equal true, association.include_data? - assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) - when :bio - assert_equal true, association.include_data? - assert_nil serializer - when :roles - assert_equal true, association.include_data? - assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) - else - flunk "Unknown association: #{key}" - end - end - end - - def test_has_many_with_no_serializer - post_serializer_class = Class.new(ActiveModel::Serializer) do - attributes :id - has_many :tags - end - post_serializer_class.new(@post).associations.each do |association| - key = association.key - serializer = association.lazy_association.serializer - - assert_equal :tags, key - assert_nil serializer - assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, association.virtual_value.to_json - end - end - - def test_serializer_options_are_passed_into_associations_serializers - association = @post_serializer - .associations - .detect { |assoc| assoc.key == :comments } - - comment_serializer = association.lazy_association.serializer.first - class << comment_serializer - def custom_options - instance_options - end - end - assert comment_serializer.custom_options.fetch(:custom_options) - end - - def test_belongs_to - @comment_serializer.associations.each do |association| - key = association.key - serializer = association.lazy_association.serializer - - case key - when :post - assert_kind_of(PostSerializer, serializer) - when :author - assert_nil serializer - else - flunk "Unknown association: #{key}" - end - - assert_equal true, association.include_data? - end - end - - def test_belongs_to_with_custom_method - assert( - @post_serializer.associations.any? do |association| - association.key == :blog - end - ) - end - - def test_associations_inheritance - inherited_klass = Class.new(PostSerializer) - - assert_equal(PostSerializer._reflections, inherited_klass._reflections) - end - - def test_associations_inheritance_with_new_association - inherited_klass = Class.new(PostSerializer) do - has_many :top_comments, serializer: CommentSerializer - end - - assert( - PostSerializer._reflections.values.all? do |reflection| - inherited_klass._reflections.values.include?(reflection) - end - ) - - assert( - inherited_klass._reflections.values.any? do |reflection| - reflection.name == :top_comments - end - ) - end - - def test_associations_custom_keys - serializer = PostWithCustomKeysSerializer.new(@post) - - expected_association_keys = serializer.associations.map(&:key) - - assert expected_association_keys.include? :reviews - assert expected_association_keys.include? :writer - assert expected_association_keys.include? :site - end - - class BelongsToBlogModel < ::Model - attributes :id, :title - associations :blog - end - class BelongsToBlogModelSerializer < ActiveModel::Serializer - type :posts - belongs_to :blog - end - - def test_belongs_to_doesnt_load_record - attributes = { id: 1, title: 'Belongs to Blog', blog: Blog.new(id: 5) } - post = BelongsToBlogModel.new(attributes) - class << post - def blog - fail 'should use blog_id' - end - - def blog_id - 5 - end - end - - actual = serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json - expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } } - - assert_equal expected, actual - end - - class InlineAssociationTestPostSerializer < ActiveModel::Serializer - has_many :comments - has_many :comments, key: :last_comments do - object.comments.last(1) - end - end - - def test_virtual_attribute_block - comment1 = ::ARModels::Comment.create!(contents: 'first comment') - comment2 = ::ARModels::Comment.create!(contents: 'last comment') - post = ::ARModels::Post.create!( - title: 'inline association test', - body: 'etc', - comments: [comment1, comment2] - ) - actual = serializable(post, adapter: :attributes, serializer: InlineAssociationTestPostSerializer).as_json - expected = { - comments: [ - { id: 1, contents: 'first comment' }, - { id: 2, contents: 'last comment' } - ], - last_comments: [ - { id: 2, contents: 'last comment' } - ] - } - - assert_equal expected, actual - ensure - ::ARModels::Post.delete_all - ::ARModels::Comment.delete_all - end - - class NamespacedResourcesTest < ActiveSupport::TestCase - class ResourceNamespace - class Post < ::Model - associations :comments, :author, :description - end - class Comment < ::Model; end - class Author < ::Model; end - class Description < ::Model; end - class PostSerializer < ActiveModel::Serializer - has_many :comments - belongs_to :author - has_one :description - end - class CommentSerializer < ActiveModel::Serializer; end - class AuthorSerializer < ActiveModel::Serializer; end - class DescriptionSerializer < ActiveModel::Serializer; end - end - - def setup - @comment = ResourceNamespace::Comment.new - @author = ResourceNamespace::Author.new - @description = ResourceNamespace::Description.new - @post = ResourceNamespace::Post.new(comments: [@comment], - author: @author, - description: @description) - @post_serializer = ResourceNamespace::PostSerializer.new(@post) - end - - def test_associations_namespaced_resources - @post_serializer.associations.each do |association| - case association.key - when :comments - assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first) - when :author - assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer) - when :description - assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer) - else - flunk "Unknown association: #{key}" - end - end - end - end - - class NestedSerializersTest < ActiveSupport::TestCase - class Post < ::Model - associations :comments, :author, :description - end - class Comment < ::Model; end - class Author < ::Model; end - class Description < ::Model; end - class PostSerializer < ActiveModel::Serializer - has_many :comments - class CommentSerializer < ActiveModel::Serializer; end - belongs_to :author - class AuthorSerializer < ActiveModel::Serializer; end - has_one :description - class DescriptionSerializer < ActiveModel::Serializer; end - end - - def setup - @comment = Comment.new - @author = Author.new - @description = Description.new - @post = Post.new(comments: [@comment], - author: @author, - description: @description) - @post_serializer = PostSerializer.new(@post) - end - - def test_associations_namespaced_resources - @post_serializer.associations.each do |association| - case association.key - when :comments - assert_instance_of(PostSerializer::CommentSerializer, association.lazy_association.serializer.first) - when :author - assert_instance_of(PostSerializer::AuthorSerializer, association.lazy_association.serializer) - when :description - assert_instance_of(PostSerializer::DescriptionSerializer, association.lazy_association.serializer) - else - flunk "Unknown association: #{key}" - end - end - end - - # rubocop:disable Metrics/AbcSize - def test_conditional_associations - model = Class.new(::Model) do - attributes :true, :false - associations :something - end.new(true: true, false: false) - - scenarios = [ - { options: { if: :true }, included: true }, - { options: { if: :false }, included: false }, - { options: { unless: :false }, included: true }, - { options: { unless: :true }, included: false }, - { options: { if: 'object.true' }, included: true }, - { options: { if: 'object.false' }, included: false }, - { options: { unless: 'object.false' }, included: true }, - { options: { unless: 'object.true' }, included: false }, - { options: { if: -> { object.true } }, included: true }, - { options: { if: -> { object.false } }, included: false }, - { options: { unless: -> { object.false } }, included: true }, - { options: { unless: -> { object.true } }, included: false }, - { options: { if: -> (s) { s.object.true } }, included: true }, - { options: { if: -> (s) { s.object.false } }, included: false }, - { options: { unless: -> (s) { s.object.false } }, included: true }, - { options: { unless: -> (s) { s.object.true } }, included: false } - ] - - scenarios.each do |s| - serializer = Class.new(ActiveModel::Serializer) do - belongs_to :something, s[:options] - - def true - true - end - - def false - false - end - end - - hash = serializable(model, serializer: serializer).serializable_hash - assert_equal(s[:included], hash.key?(:something), "Error with #{s[:options]}") - end - end - - def test_illegal_conditional_associations - exception = assert_raises(TypeError) do - Class.new(ActiveModel::Serializer) do - belongs_to :x, if: nil - end - end - - assert_match(/:if should be a Symbol, String or Proc/, exception.message) - end - end - - class InheritedSerializerTest < ActiveSupport::TestCase - class PostSerializer < ActiveModel::Serializer - belongs_to :author - has_many :comments - belongs_to :blog - end - - class InheritedPostSerializer < PostSerializer - belongs_to :author, polymorphic: true - has_many :comments, key: :reviews - end - - class AuthorSerializer < ActiveModel::Serializer - has_many :posts - has_many :roles - has_one :bio - end - - class InheritedAuthorSerializer < AuthorSerializer - has_many :roles, polymorphic: true - has_one :bio, polymorphic: true - end - - def setup - @author = Author.new(name: 'Steve K.') - @post = Post.new(title: 'New Post', body: 'Body') - @post_serializer = PostSerializer.new(@post) - @author_serializer = AuthorSerializer.new(@author) - @inherited_post_serializer = InheritedPostSerializer.new(@post) - @inherited_author_serializer = InheritedAuthorSerializer.new(@author) - @author_associations = @author_serializer.associations.to_a.sort_by(&:name) - @inherited_author_associations = @inherited_author_serializer.associations.to_a.sort_by(&:name) - @post_associations = @post_serializer.associations.to_a - @inherited_post_associations = @inherited_post_serializer.associations.to_a - end - - test 'an author serializer must have [posts,roles,bio] associations' do - expected = [:posts, :roles, :bio].sort - result = @author_serializer.associations.map(&:name).sort - assert_equal(result, expected) - end - - test 'a post serializer must have [author,comments,blog] associations' do - expected = [:author, :comments, :blog].sort - result = @post_serializer.associations.map(&:name).sort - assert_equal(result, expected) - end - - test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do - expected = [:roles, :bio].sort - result = (@inherited_author_associations.map(&:reflection) - @author_associations.map(&:reflection)).map(&:name) - assert_equal(result, expected) - assert_equal [true, false, true], @inherited_author_associations.map(&:polymorphic?) - assert_equal [false, false, false], @author_associations.map(&:polymorphic?) - end - - test 'a serializer inheriting from another serializer can redefine belongs_to associations' do - assert_equal [:author, :comments, :blog], @post_associations.map(&:name) - assert_equal [:author, :comments, :blog, :comments], @inherited_post_associations.map(&:name) - - refute @post_associations.detect { |assoc| assoc.name == :author }.polymorphic? - assert @inherited_post_associations.detect { |assoc| assoc.name == :author }.polymorphic? - - refute @post_associations.detect { |assoc| assoc.name == :comments }.key? - original_comment_assoc, new_comments_assoc = @inherited_post_associations.select { |assoc| assoc.name == :comments } - refute original_comment_assoc.key? - assert_equal :reviews, new_comments_assoc.key - - original_blog = @post_associations.detect { |assoc| assoc.name == :blog } - inherited_blog = @inherited_post_associations.detect { |assoc| assoc.name == :blog } - original_parent_serializer = original_blog.lazy_association.association_options.delete(:parent_serializer) - inherited_parent_serializer = inherited_blog.lazy_association.association_options.delete(:parent_serializer) - assert_equal PostSerializer, original_parent_serializer.class - assert_equal InheritedPostSerializer, inherited_parent_serializer.class - end - - test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do - expected = [:author, :comments, :blog, :reviews].sort - result = @inherited_post_serializer.associations.map(&:key).sort - assert_equal(result, expected) - end - end - end - end -end diff --git a/test/serializers/attribute_test.rb b/test/serializers/attribute_test.rb deleted file mode 100644 index 608898c3..00000000 --- a/test/serializers/attribute_test.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class AttributeTest < ActiveSupport::TestCase - def setup - @blog = Blog.new(id: 1, name: 'AMS Hints', type: 'stuff') - @blog_serializer = AlternateBlogSerializer.new(@blog) - end - - def test_attributes_definition - assert_equal([:id, :title], - @blog_serializer.class._attributes) - end - - def test_json_serializable_hash - adapter = ActiveModelSerializers::Adapter::Json.new(@blog_serializer) - assert_equal({ blog: { id: 1, title: 'AMS Hints' } }, adapter.serializable_hash) - end - - def test_attribute_inheritance_with_key - inherited_klass = Class.new(AlternateBlogSerializer) - blog_serializer = inherited_klass.new(@blog) - adapter = ActiveModelSerializers::Adapter::Attributes.new(blog_serializer) - assert_equal({ id: 1, title: 'AMS Hints' }, adapter.serializable_hash) - end - - def test_multiple_calls_with_the_same_attribute - serializer_class = Class.new(ActiveModel::Serializer) do - attribute :title - attribute :title - end - - assert_equal([:title], serializer_class._attributes) - end - - def test_id_attribute_override - serializer = Class.new(ActiveModel::Serializer) do - attribute :name, key: :id - end - - adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog)) - assert_equal({ blog: { id: 'AMS Hints' } }, adapter.serializable_hash) - end - - def test_object_attribute_override - serializer = Class.new(ActiveModel::Serializer) do - attribute :name, key: :object - end - - adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog)) - assert_equal({ blog: { object: 'AMS Hints' } }, adapter.serializable_hash) - end - - def test_type_attribute - attribute_serializer = Class.new(ActiveModel::Serializer) do - attribute :id, key: :type - end - attributes_serializer = Class.new(ActiveModel::Serializer) do - attributes :type - end - - adapter = ActiveModelSerializers::Adapter::Json.new(attribute_serializer.new(@blog)) - assert_equal({ blog: { type: 1 } }, adapter.serializable_hash) - - adapter = ActiveModelSerializers::Adapter::Json.new(attributes_serializer.new(@blog)) - assert_equal({ blog: { type: 'stuff' } }, adapter.serializable_hash) - end - - def test_id_attribute_override_before - serializer = Class.new(ActiveModel::Serializer) do - def id - 'custom' - end - - attribute :id - end - - hash = ActiveModelSerializers::SerializableResource.new(@blog, adapter: :json, serializer: serializer).serializable_hash - - assert_equal('custom', hash[:blog][:id]) - end - - class PostWithVirtualAttribute < ::Model; attributes :first_name, :last_name end - class PostWithVirtualAttributeSerializer < ActiveModel::Serializer - attribute :name do - "#{object.first_name} #{object.last_name}" - end - end - - def test_virtual_attribute_block - post = PostWithVirtualAttribute.new(first_name: 'Lucas', last_name: 'Hosseini') - hash = serializable(post).serializable_hash - expected = { name: 'Lucas Hosseini' } - - assert_equal(expected, hash) - end - - # rubocop:disable Metrics/AbcSize - def test_conditional_associations - model = Class.new(::Model) do - attributes :true, :false, :attribute - end.new(true: true, false: false) - - scenarios = [ - { options: { if: :true }, included: true }, - { options: { if: :false }, included: false }, - { options: { unless: :false }, included: true }, - { options: { unless: :true }, included: false }, - { options: { if: 'object.true' }, included: true }, - { options: { if: 'object.false' }, included: false }, - { options: { unless: 'object.false' }, included: true }, - { options: { unless: 'object.true' }, included: false }, - { options: { if: -> { object.true } }, included: true }, - { options: { if: -> { object.false } }, included: false }, - { options: { unless: -> { object.false } }, included: true }, - { options: { unless: -> { object.true } }, included: false }, - { options: { if: -> (s) { s.object.true } }, included: true }, - { options: { if: -> (s) { s.object.false } }, included: false }, - { options: { unless: -> (s) { s.object.false } }, included: true }, - { options: { unless: -> (s) { s.object.true } }, included: false } - ] - - scenarios.each do |s| - serializer = Class.new(ActiveModel::Serializer) do - attribute :attribute, s[:options] - - def true - true - end - - def false - false - end - end - - hash = serializable(model, serializer: serializer).serializable_hash - assert_equal(s[:included], hash.key?(:attribute), "Error with #{s[:options]}") - end - end - - def test_illegal_conditional_attributes - exception = assert_raises(TypeError) do - Class.new(ActiveModel::Serializer) do - attribute :x, if: nil - end - end - - assert_match(/:if should be a Symbol, String or Proc/, exception.message) - end - end - end -end diff --git a/test/serializers/attributes_test.rb b/test/serializers/attributes_test.rb deleted file mode 100644 index fb792b26..00000000 --- a/test/serializers/attributes_test.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class AttributesTest < ActiveSupport::TestCase - def setup - @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') - @profile_serializer = ProfileSerializer.new(@profile) - @comment = Comment.new(id: 1, body: 'ZOMG!!', date: '2015') - @serializer_klass = Class.new(CommentSerializer) - @serializer_klass_with_new_attributes = Class.new(CommentSerializer) do - attributes :date, :likes - end - end - - def test_attributes_definition - assert_equal([:name, :description], - @profile_serializer.class._attributes) - end - - def test_attributes_inheritance_definition - assert_equal([:id, :body], @serializer_klass._attributes) - end - - def test_attributes_inheritance - serializer = @serializer_klass.new(@comment) - assert_equal({ id: 1, body: 'ZOMG!!' }, - serializer.attributes) - end - - def test_attribute_inheritance_with_new_attribute_definition - assert_equal([:id, :body, :date, :likes], @serializer_klass_with_new_attributes._attributes) - assert_equal([:id, :body], CommentSerializer._attributes) - end - - def test_attribute_inheritance_with_new_attribute - serializer = @serializer_klass_with_new_attributes.new(@comment) - assert_equal({ id: 1, body: 'ZOMG!!', date: '2015', likes: nil }, - serializer.attributes) - end - - def test_multiple_calls_with_the_same_attribute - serializer_class = Class.new(ActiveModel::Serializer) do - attributes :id, :title - attributes :id, :title, :title, :body - end - - assert_equal([:id, :title, :body], serializer_class._attributes) - end - end - end -end diff --git a/test/serializers/caching_configuration_test_isolated.rb b/test/serializers/caching_configuration_test_isolated.rb deleted file mode 100644 index b5698dd1..00000000 --- a/test/serializers/caching_configuration_test_isolated.rb +++ /dev/null @@ -1,170 +0,0 @@ -# Execute this test in isolation -require 'support/isolated_unit' - -class CachingConfigurationTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation - - setup do - require 'rails' - # AMS needs to be required before Rails.application is initialized for - # Railtie's to fire in Rails.application.initialize! - # (and make_basic_app initializes the app) - require 'active_model_serializers' - # Create serializers before Rails.application.initialize! - # To ensure we're testing that the cache settings depend on - # the Railtie firing, not on the ActionController being loaded. - create_serializers - end - - def create_serializers - @cached_serializer = Class.new(ActiveModel::Serializer) do - cache skip_digest: true - attributes :id, :name, :title - end - @fragment_cached_serializer = Class.new(ActiveModel::Serializer) do - cache only: :id - attributes :id, :name, :title - end - @non_cached_serializer = Class.new(ActiveModel::Serializer) do - attributes :id, :name, :title - end - end - - class PerformCachingTrue < CachingConfigurationTest - setup do - # Let's make that Rails app and initialize it! - make_basic_app do |app| - app.config.action_controller.perform_caching = true - app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) - end - controller_cache_store # Force ActiveSupport.on_load(:action_controller) to run - end - - test 'it sets perform_caching to true on AMS.config and serializers' do - assert Rails.configuration.action_controller.perform_caching - assert ActiveModelSerializers.config.perform_caching - assert ActiveModel::Serializer.perform_caching? - assert @cached_serializer.perform_caching? - assert @non_cached_serializer.perform_caching? - assert @fragment_cached_serializer.perform_caching? - end - - test 'it sets the AMS.config.cache_store to the controller cache_store' do - assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore - assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class - end - - test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do - assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class - assert_equal controller_cache_store, @cached_serializer.cache_store.class - assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class - end - - test 'the cached serializer has cache_enabled?' do - assert @cached_serializer.cache_enabled? - end - - test 'the cached serializer does not have fragment_cache_enabled?' do - refute @cached_serializer.fragment_cache_enabled? - end - - test 'the non-cached serializer cache_store is nil' do - assert_nil @non_cached_serializer._cache - assert_nil @non_cached_serializer.cache_store - assert_nil @non_cached_serializer._cache - end - - test 'the non-cached serializer does not have cache_enabled?' do - refute @non_cached_serializer.cache_enabled? - end - - test 'the non-cached serializer does not have fragment_cache_enabled?' do - refute @non_cached_serializer.fragment_cache_enabled? - end - - test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do - assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class - assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class - assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class - end - - test 'the fragment cached serializer does not have cache_enabled?' do - refute @fragment_cached_serializer.cache_enabled? - end - - test 'the fragment cached serializer has fragment_cache_enabled?' do - assert @fragment_cached_serializer.fragment_cache_enabled? - end - end - - class PerformCachingFalse < CachingConfigurationTest - setup do - # Let's make that Rails app and initialize it! - make_basic_app do |app| - app.config.action_controller.perform_caching = false - app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) - end - controller_cache_store # Force ActiveSupport.on_load(:action_controller) to run - end - - test 'it sets perform_caching to false on AMS.config and serializers' do - refute Rails.configuration.action_controller.perform_caching - refute ActiveModelSerializers.config.perform_caching - refute ActiveModel::Serializer.perform_caching? - refute @cached_serializer.perform_caching? - refute @non_cached_serializer.perform_caching? - refute @fragment_cached_serializer.perform_caching? - end - - test 'it sets the AMS.config.cache_store to the controller cache_store' do - assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore - assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class - end - - test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do - assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class - assert_equal controller_cache_store, @cached_serializer.cache_store.class - assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class - end - - test 'the cached serializer does not have cache_enabled?' do - refute @cached_serializer.cache_enabled? - end - - test 'the cached serializer does not have fragment_cache_enabled?' do - refute @cached_serializer.fragment_cache_enabled? - end - - test 'the non-cached serializer cache_store is nil' do - assert_nil @non_cached_serializer._cache - assert_nil @non_cached_serializer.cache_store - assert_nil @non_cached_serializer._cache - end - - test 'the non-cached serializer does not have cache_enabled?' do - refute @non_cached_serializer.cache_enabled? - end - - test 'the non-cached serializer does not have fragment_cache_enabled?' do - refute @non_cached_serializer.fragment_cache_enabled? - end - - test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do - assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class - assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class - assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class - end - - test 'the fragment cached serializer does not have cache_enabled?' do - refute @fragment_cached_serializer.cache_enabled? - end - - test 'the fragment cached serializer does not have fragment_cache_enabled?' do - refute @fragment_cached_serializer.fragment_cache_enabled? - end - end - - def controller_cache_store - ActionController::Base.cache_store.class - end -end diff --git a/test/serializers/configuration_test.rb b/test/serializers/configuration_test.rb deleted file mode 100644 index 2c5f922f..00000000 --- a/test/serializers/configuration_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class ConfigurationTest < ActiveSupport::TestCase - def test_collection_serializer - assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.collection_serializer - end - - def test_array_serializer - assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.array_serializer - end - - def test_setting_array_serializer_sets_collection_serializer - config = ActiveModelSerializers.config - old_config = config.dup - begin - assert_equal ActiveModel::Serializer::CollectionSerializer, config.collection_serializer - config.array_serializer = :foo - assert_equal config.array_serializer, :foo - assert_equal config.collection_serializer, :foo - ensure - ActiveModelSerializers.config.replace(old_config) - end - end - - def test_default_adapter - assert_equal :attributes, ActiveModelSerializers.config.adapter - end - end - end -end diff --git a/test/serializers/fieldset_test.rb b/test/serializers/fieldset_test.rb deleted file mode 100644 index 5b99d57a..00000000 --- a/test/serializers/fieldset_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class FieldsetTest < ActiveSupport::TestCase - def test_fieldset_with_hash - fieldset = ActiveModel::Serializer::Fieldset.new('post' => %w(id title), 'comment' => ['body']) - expected = { post: [:id, :title], comment: [:body] } - - assert_equal(expected, fieldset.fields) - end - end - end -end diff --git a/test/serializers/meta_test.rb b/test/serializers/meta_test.rb deleted file mode 100644 index 64209321..00000000 --- a/test/serializers/meta_test.rb +++ /dev/null @@ -1,202 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class MetaTest < ActiveSupport::TestCase - def setup - @blog = Blog.new(id: 1, - name: 'AMS Hints', - writer: Author.new(id: 2, name: 'Steve'), - articles: [Post.new(id: 3, title: 'AMS')]) - end - - def test_meta_is_present_with_root - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json, - serializer: AlternateBlogSerializer, - meta: { total: 10 } - ).as_json - expected = { - blog: { - id: 1, - title: 'AMS Hints' - }, - 'meta' => { - total: 10 - } - } - assert_equal(expected, actual) - end - - def test_meta_is_not_included_when_blank - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json, - serializer: AlternateBlogSerializer, - meta: {} - ).as_json - expected = { - blog: { - id: 1, - title: 'AMS Hints' - } - } - assert_equal(expected, actual) - end - - def test_meta_is_not_included_when_empty_string - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json, - serializer: AlternateBlogSerializer, - meta: '' - ).as_json - expected = { - blog: { - id: 1, - title: 'AMS Hints' - } - } - assert_equal(expected, actual) - end - - def test_meta_is_not_included_when_root_is_missing - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :attributes, - serializer: AlternateBlogSerializer, - meta: { total: 10 } - ).as_json - expected = { - id: 1, - title: 'AMS Hints' - } - assert_equal(expected, actual) - end - - def test_meta_key_is_used - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json, - serializer: AlternateBlogSerializer, - meta: { total: 10 }, - meta_key: 'haha_meta' - ).as_json - expected = { - blog: { - id: 1, - title: 'AMS Hints' - }, - 'haha_meta' => { - total: 10 - } - } - assert_equal(expected, actual) - end - - def test_meta_key_is_not_used_with_json_api - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json_api, - serializer: AlternateBlogSerializer, - meta: { total: 10 }, - meta_key: 'haha_meta' - ).as_json - expected = { - data: { - id: '1', - type: 'blogs', - attributes: { title: 'AMS Hints' } - }, - meta: { total: 10 } - } - assert_equal(expected, actual) - end - - def test_meta_key_is_not_present_when_empty_hash_with_json_api - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json_api, - serializer: AlternateBlogSerializer, - meta: {} - ).as_json - expected = { - data: { - id: '1', - type: 'blogs', - attributes: { title: 'AMS Hints' } - } - } - assert_equal(expected, actual) - end - - def test_meta_key_is_not_present_when_empty_string_with_json_api - actual = ActiveModelSerializers::SerializableResource.new( - @blog, - adapter: :json_api, - serializer: AlternateBlogSerializer, - meta: '' - ).as_json - expected = { - data: { - id: '1', - type: 'blogs', - attributes: { title: 'AMS Hints' } - } - } - assert_equal(expected, actual) - end - - def test_meta_is_not_present_on_arrays_without_root - actual = ActiveModelSerializers::SerializableResource.new( - [@blog], - adapter: :attributes, - meta: { total: 10 } - ).as_json - expected = [{ - id: 1, - name: 'AMS Hints', - writer: { - id: 2, - name: 'Steve' - }, - articles: [{ - id: 3, - title: 'AMS', - body: nil - }] - }] - assert_equal(expected, actual) - end - - def test_meta_is_present_on_arrays_with_root - actual = ActiveModelSerializers::SerializableResource.new( - [@blog], - adapter: :json, - meta: { total: 10 }, - meta_key: 'haha_meta' - ).as_json - expected = { - blogs: [{ - id: 1, - name: 'AMS Hints', - writer: { - id: 2, - name: 'Steve' - }, - articles: [{ - id: 3, - title: 'AMS', - body: nil - }] - }], - 'haha_meta' => { - total: 10 - } - } - assert_equal(expected, actual) - end - end - end -end diff --git a/test/serializers/options_test.rb b/test/serializers/options_test.rb deleted file mode 100644 index 009388e3..00000000 --- a/test/serializers/options_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class OptionsTest < ActiveSupport::TestCase - class ModelWithOptions < ActiveModelSerializers::Model - attributes :name, :description - end - class ModelWithOptionsSerializer < ActiveModel::Serializer - attributes :name, :description - - def arguments_passed_in? - instance_options[:my_options] == :accessible - end - end - - setup do - @model_with_options = ModelWithOptions.new(name: 'Name 1', description: 'Description 1') - end - - def test_options_are_accessible - model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options, my_options: :accessible) - assert model_with_options_serializer.arguments_passed_in? - end - - def test_no_option_is_passed_in - model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options) - refute model_with_options_serializer.arguments_passed_in? - end - end - end -end diff --git a/test/serializers/read_attribute_for_serialization_test.rb b/test/serializers/read_attribute_for_serialization_test.rb deleted file mode 100644 index 02911c0e..00000000 --- a/test/serializers/read_attribute_for_serialization_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class ReadAttributeForSerializationTest < ActiveSupport::TestCase - # https://github.com/rails-api/active_model_serializers/issues/1653 - class Parent < ActiveModelSerializers::Model - attributes :id - end - class Child < Parent - attributes :name - end - class ParentSerializer < ActiveModel::Serializer - attributes :$id - - define_method(:$id) do - object.id - end - end - class ChildSerializer < ParentSerializer - attributes :name - end - - def test_child_serializer_calls_dynamic_method_in_parent_serializer - parent = ParentSerializer.new(Parent.new(id: 5)) - child = ChildSerializer.new(Child.new(id: 6, name: 'Child')) - assert_equal 5, parent.read_attribute_for_serialization(:$id) - assert_equal 6, child.read_attribute_for_serialization(:$id) - end - - # https://github.com/rails-api/active_model_serializers/issues/1658 - class ErrorResponse < ActiveModelSerializers::Model - attributes :error - end - class ApplicationSerializer < ActiveModel::Serializer - attributes :status - - def status - object.try(:errors).blank? && object.try(:error).blank? - end - end - class ErrorResponseSerializer < ApplicationSerializer - attributes :error - end - class ErrorResponseWithSuperSerializer < ApplicationSerializer - attributes :error - - def success - super - end - end - - def test_child_serializer_with_error_attribute - error = ErrorResponse.new(error: 'i have an error') - serializer = ErrorResponseSerializer.new(error) - serializer_with_super = ErrorResponseWithSuperSerializer.new(error) - assert_equal false, serializer.read_attribute_for_serialization(:status) - assert_equal false, serializer_with_super.read_attribute_for_serialization(:status) - end - - def test_child_serializer_with_errors - error = ErrorResponse.new - error.errors.add(:invalid, 'i am not valid') - serializer = ErrorResponseSerializer.new(error) - serializer_with_super = ErrorResponseWithSuperSerializer.new(error) - assert_equal false, serializer.read_attribute_for_serialization(:status) - assert_equal false, serializer_with_super.read_attribute_for_serialization(:status) - end - - def test_child_serializer_no_error_attribute_or_errors - error = ErrorResponse.new - serializer = ErrorResponseSerializer.new(error) - serializer_with_super = ErrorResponseWithSuperSerializer.new(error) - assert_equal true, serializer.read_attribute_for_serialization(:status) - assert_equal true, serializer_with_super.read_attribute_for_serialization(:status) - end - end - end -end diff --git a/test/serializers/reflection_test.rb b/test/serializers/reflection_test.rb deleted file mode 100644 index 11cb154b..00000000 --- a/test/serializers/reflection_test.rb +++ /dev/null @@ -1,427 +0,0 @@ -require 'test_helper' -module ActiveModel - class Serializer - class ReflectionTest < ActiveSupport::TestCase - class Blog < ActiveModelSerializers::Model - attributes :id - end - class BlogSerializer < ActiveModel::Serializer - type 'blog' - attributes :id - end - - setup do - @expected_meta = { id: 1 } - @expected_links = { self: 'no_uri_validation' } - @empty_links = {} - model_attributes = { blog: Blog.new(@expected_meta) } - @model = Class.new(ActiveModelSerializers::Model) do - attributes(*model_attributes.keys) - - def self.name - 'TestModel' - end - end.new(model_attributes) - @instance_options = {} - end - - def evaluate_association_value(association) - association.lazy_association.eval_reflection_block - end - - # TODO: Remaining tests - # test_reflection_value_block_with_scope - # test_reflection_value_uses_serializer_instance_method - # test_reflection_excluded_eh_blank_is_false - # test_reflection_excluded_eh_if - # test_reflection_excluded_eh_unless - # test_evaluate_condition_symbol_serializer_method - # test_evaluate_condition_string_serializer_method - # test_evaluate_condition_proc - # test_evaluate_condition_proc_yields_serializer - # test_evaluate_condition_other - # test_options_key - # test_options_polymorphic - # test_options_serializer - # test_options_virtual_value - # test_options_namespace - - def test_reflection_value - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_nil reflection.block - assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) - assert_equal true, reflection.options.fetch(:include_data_setting) - - include_slice = :does_not_matter - assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) - end - - def test_reflection_value_block - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - object.blog - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_respond_to reflection.block, :call - assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) - assert_equal true, reflection.options.fetch(:include_data_setting) - - include_slice = :does_not_matter - assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) - end - - def test_reflection_value_block_with_explicit_include_data_true - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - include_data true - object.blog - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_respond_to reflection.block, :call - assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) - assert_equal true, reflection.options.fetch(:include_data_setting) - - include_slice = :does_not_matter - assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) - end - - def test_reflection_value_block_with_include_data_false_mutates_the_reflection_include_data - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - include_data false - object.blog - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_respond_to reflection.block, :call - assert_equal true, reflection.options.fetch(:include_data_setting) - include_slice = :does_not_matter - assert_nil reflection.send(:value, serializer_instance, include_slice) - assert_equal false, reflection.options.fetch(:include_data_setting) - end - - def test_reflection_value_block_with_include_data_if_sideloaded_included_mutates_the_reflection_include_data - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - include_data :if_sideloaded - object.blog - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_respond_to reflection.block, :call - assert_equal true, reflection.options.fetch(:include_data_setting) - include_slice = {} - assert_nil reflection.send(:value, serializer_instance, include_slice) - assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting) - end - - def test_reflection_value_block_with_include_data_if_sideloaded_excluded_mutates_the_reflection_include_data - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - include_data :if_sideloaded - object.blog - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - - # Assert - assert_respond_to reflection.block, :call - assert_equal true, reflection.options.fetch(:include_data_setting) - include_slice = { blog: :does_not_matter } - assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) - assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting) - end - - def test_reflection_block_with_link_mutates_the_reflection_links - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - link :self, 'no_uri_validation' - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_equal @empty_links, reflection.options.fetch(:links) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - - # Assert association links empty when not yet evaluated - assert_equal @empty_links, reflection.options.fetch(:links) - assert_equal @empty_links, association.links - - evaluate_association_value(association) - - assert_equal @expected_links, association.links - assert_equal @expected_links, reflection.options.fetch(:links) - end - - def test_reflection_block_with_link_block_mutates_the_reflection_links - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - link :self do - 'no_uri_validation' - end - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_equal @empty_links, reflection.options.fetch(:links) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - - # Assert association links empty when not yet evaluated - assert_equal @empty_links, association.links - - evaluate_association_value(association) - - # Assert before instance_eval link - link = association.links.fetch(:self) - assert_respond_to link, :call - assert_respond_to reflection.options.fetch(:links).fetch(:self), :call - - # Assert after instance_eval link - assert_equal @expected_links.fetch(:self), reflection.instance_eval(&link) - assert_respond_to reflection.options.fetch(:links).fetch(:self), :call - end - - def test_reflection_block_with_meta_mutates_the_reflection_meta - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - meta(id: object.blog.id) - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_nil reflection.options.fetch(:meta) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - - evaluate_association_value(association) - - assert_equal @expected_meta, association.meta - assert_equal @expected_meta, reflection.options.fetch(:meta) - end - - def test_reflection_block_with_meta_block_mutates_the_reflection_meta - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - meta do - { id: object.blog.id } - end - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_nil reflection.options.fetch(:meta) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - # Assert before instance_eval meta - - evaluate_association_value(association) - - assert_respond_to association.meta, :call - assert_respond_to reflection.options.fetch(:meta), :call - - # Assert after instance_eval meta - assert_equal @expected_meta, reflection.instance_eval(&association.meta) - assert_respond_to reflection.options.fetch(:meta), :call - assert_respond_to association.meta, :call - end - - # rubocop:disable Metrics/AbcSize - def test_reflection_block_with_meta_in_link_block_mutates_the_reflection_meta - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - link :self do - meta(id: object.blog.id) - 'no_uri_validation' - end - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_nil reflection.options.fetch(:meta) - assert_equal @empty_links, reflection.options.fetch(:links) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - # Assert before instance_eval link meta - assert_nil association.meta - assert_nil reflection.options.fetch(:meta) - - evaluate_association_value(association) - - link = association.links.fetch(:self) - assert_respond_to link, :call - assert_respond_to reflection.options.fetch(:links).fetch(:self), :call - assert_nil reflection.options.fetch(:meta) - - # Assert after instance_eval link - assert_equal 'no_uri_validation', reflection.instance_eval(&link) - assert_equal @expected_meta, reflection.options.fetch(:meta) - assert_equal @expected_meta, association.meta - end - # rubocop:enable Metrics/AbcSize - - # rubocop:disable Metrics/AbcSize - def test_reflection_block_with_meta_block_in_link_block_mutates_the_reflection_meta - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - link :self do - meta do - { id: object.blog.id } - end - 'no_uri_validation' - end - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_nil reflection.options.fetch(:meta) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - assert_nil association.meta - assert_nil reflection.options.fetch(:meta) - - # Assert before instance_eval link - - evaluate_association_value(association) - - link = association.links.fetch(:self) - assert_nil reflection.options.fetch(:meta) - assert_respond_to link, :call - assert_respond_to association.links.fetch(:self), :call - - # Assert after instance_eval link - assert_equal 'no_uri_validation', reflection.instance_eval(&link) - assert_respond_to association.links.fetch(:self), :call - # Assert before instance_eval link meta - assert_respond_to reflection.options.fetch(:meta), :call - assert_respond_to association.meta, :call - - # Assert after instance_eval link meta - assert_equal @expected_meta, reflection.instance_eval(&reflection.options.fetch(:meta)) - assert_respond_to association.meta, :call - end - # rubocop:enable Metrics/AbcSize - - def test_no_href_in_vanilla_reflection - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - link :self do - href 'no_uri_validation' - end - end - end - serializer_instance = serializer_class.new(@model, @instance_options) - - # Get Reflection - reflection = serializer_class._reflections.fetch(:blog) - assert_equal @empty_links, reflection.options.fetch(:links) - - # Build Association - association = reflection.build_association(serializer_instance, @instance_options) - # Assert before instance_eval link - - evaluate_association_value(association) - - link = association.links.fetch(:self) - assert_respond_to link, :call - - # Assert after instance_eval link - exception = assert_raise(NoMethodError) do - reflection.instance_eval(&link) - end - assert_match(/undefined method `href'/, exception.message) - end - - # rubocop:disable Metrics/AbcSize - def test_mutating_reflection_block_is_not_thread_safe - serializer_class = Class.new(ActiveModel::Serializer) do - has_one :blog do - meta(id: object.blog.id) - end - end - model1_meta = @expected_meta - # Evaluate reflection meta for model with id 1 - serializer_instance = serializer_class.new(@model, @instance_options) - reflection = serializer_class._reflections.fetch(:blog) - assert_nil reflection.options.fetch(:meta) - association = reflection.build_association(serializer_instance, @instance_options) - - evaluate_association_value(association) - - assert_equal model1_meta, association.meta - assert_equal model1_meta, reflection.options.fetch(:meta) - - model2_meta = @expected_meta.merge(id: 2) - # Evaluate reflection meta for model with id 2 - @model.blog.id = 2 - assert_equal 2, @model.blog.id # sanity check - serializer_instance = serializer_class.new(@model, @instance_options) - reflection = serializer_class._reflections.fetch(:blog) - - # WARN: Thread-safety issue - # Before the reflection is evaluated, it has the value from the previous evaluation - assert_equal model1_meta, reflection.options.fetch(:meta) - - association = reflection.build_association(serializer_instance, @instance_options) - - evaluate_association_value(association) - - assert_equal model2_meta, association.meta - assert_equal model2_meta, reflection.options.fetch(:meta) - end - # rubocop:enable Metrics/AbcSize - end - end -end diff --git a/test/serializers/root_test.rb b/test/serializers/root_test.rb deleted file mode 100644 index 5bd4cdc3..00000000 --- a/test/serializers/root_test.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class RootTest < ActiveSupport::TestCase - def setup - @virtual_value = VirtualValue.new(id: 1) - end - - def test_overwrite_root - serializer = VirtualValueSerializer.new(@virtual_value, root: 'smth') - assert_equal('smth', serializer.json_key) - end - - def test_underscore_in_root - serializer = VirtualValueSerializer.new(@virtual_value) - assert_equal('virtual_value', serializer.json_key) - end - end - end -end diff --git a/test/serializers/serialization_test.rb b/test/serializers/serialization_test.rb deleted file mode 100644 index 3c1884e6..00000000 --- a/test/serializers/serialization_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -module ActiveModel - class Serializer - class SerializationTest < ActiveSupport::TestCase - class Blog < ActiveModelSerializers::Model - attributes :id, :name, :authors - end - class Author < ActiveModelSerializers::Model - attributes :id, :name - end - class BlogSerializer < ActiveModel::Serializer - attributes :id - attribute :name, key: :title - - has_many :authors - end - class AuthorSerializer < ActiveModel::Serializer - attributes :id, :name - end - - setup do - @authors = [Author.new(id: 1, name: 'Blog Author')] - @blog = Blog.new(id: 2, name: 'The Blog', authors: @authors) - @serializer_instance = BlogSerializer.new(@blog) - @serializable = ActiveModelSerializers::SerializableResource.new(@blog, serializer: BlogSerializer, adapter: :attributes) - @expected_hash = { id: 2, title: 'The Blog', authors: [{ id: 1, name: 'Blog Author' }] } - @expected_json = '{"id":2,"title":"The Blog","authors":[{"id":1,"name":"Blog Author"}]}' - end - - test '#serializable_hash is the same as generated by the attributes adapter' do - assert_equal @serializable.serializable_hash, @serializer_instance.serializable_hash - assert_equal @expected_hash, @serializer_instance.serializable_hash - end - - test '#as_json is the same as generated by the attributes adapter' do - assert_equal @serializable.as_json, @serializer_instance.as_json - assert_equal @expected_hash, @serializer_instance.as_json - end - - test '#to_json is the same as generated by the attributes adapter' do - assert_equal @serializable.to_json, @serializer_instance.to_json - assert_equal @expected_json, @serializer_instance.to_json - end - - test '#to_h is an alias for #serializable_hash' do - assert_equal @serializable.serializable_hash, @serializer_instance.to_h - assert_equal @expected_hash, @serializer_instance.to_h - end - - test '#to_hash is an alias for #serializable_hash' do - assert_equal @serializable.serializable_hash, @serializer_instance.to_hash - assert_equal @expected_hash, @serializer_instance.to_hash - end - end - end -end diff --git a/test/serializers/serializer_for_test.rb b/test/serializers/serializer_for_test.rb deleted file mode 100644 index 9f691708..00000000 --- a/test/serializers/serializer_for_test.rb +++ /dev/null @@ -1,136 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class SerializerForTest < ActiveSupport::TestCase - class CollectionSerializerTest < ActiveSupport::TestCase - def setup - @array = [1, 2, 3] - @previous_collection_serializer = ActiveModelSerializers.config.collection_serializer - end - - def teardown - ActiveModelSerializers.config.collection_serializer = @previous_collection_serializer - end - - def test_serializer_for_array - serializer = ActiveModel::Serializer.serializer_for(@array) - assert_equal ActiveModelSerializers.config.collection_serializer, serializer - end - - def test_overwritten_serializer_for_array - new_collection_serializer = Class.new - ActiveModelSerializers.config.collection_serializer = new_collection_serializer - serializer = ActiveModel::Serializer.serializer_for(@array) - assert_equal new_collection_serializer, serializer - end - end - - class SerializerTest < ActiveSupport::TestCase - module ResourceNamespace - class Post < ::Model; end - class Comment < ::Model; end - - class PostSerializer < ActiveModel::Serializer - class CommentSerializer < ActiveModel::Serializer - end - end - end - - class MyProfile < Profile - end - - class CustomProfile - def serializer_class - ProfileSerializer - end - end - - class Tweet < ::Model; end - TweetSerializer = Class.new - - def setup - @profile = Profile.new - @my_profile = MyProfile.new - @custom_profile = CustomProfile.new - @model = ::Model.new - @tweet = Tweet.new - end - - def test_serializer_for_non_ams_serializer - serializer = ActiveModel::Serializer.serializer_for(@tweet) - assert_nil serializer - end - - def test_serializer_for_existing_serializer - serializer = ActiveModel::Serializer.serializer_for(@profile) - assert_equal ProfileSerializer, serializer - end - - def test_serializer_for_existing_serializer_with_lookup_disabled - serializer = with_serializer_lookup_disabled do - ActiveModel::Serializer.serializer_for(@profile) - end - assert_nil serializer - end - - def test_serializer_for_not_existing_serializer - serializer = ActiveModel::Serializer.serializer_for(@model) - assert_nil serializer - end - - def test_serializer_inherited_serializer - serializer = ActiveModel::Serializer.serializer_for(@my_profile) - assert_equal ProfileSerializer, serializer - end - - def test_serializer_inherited_serializer_with_lookup_disabled - serializer = with_serializer_lookup_disabled do - ActiveModel::Serializer.serializer_for(@my_profile) - end - assert_nil serializer - end - - def test_serializer_custom_serializer - serializer = ActiveModel::Serializer.serializer_for(@custom_profile) - assert_equal ProfileSerializer, serializer - end - - def test_serializer_custom_serializer_with_lookup_disabled - serializer = with_serializer_lookup_disabled do - ActiveModel::Serializer.serializer_for(@custom_profile) - end - assert_equal ProfileSerializer, serializer - end - - def test_serializer_for_namespaced_resource - post = ResourceNamespace::Post.new - serializer = ActiveModel::Serializer.serializer_for(post) - assert_equal ResourceNamespace::PostSerializer, serializer - end - - def test_serializer_for_namespaced_resource_with_lookup_disabled - post = ResourceNamespace::Post.new - serializer = with_serializer_lookup_disabled do - ActiveModel::Serializer.serializer_for(post) - end - assert_nil serializer - end - - def test_serializer_for_nested_resource - comment = ResourceNamespace::Comment.new - serializer = ResourceNamespace::PostSerializer.serializer_for(comment) - assert_equal ResourceNamespace::PostSerializer::CommentSerializer, serializer - end - - def test_serializer_for_nested_resource_with_lookup_disabled - comment = ResourceNamespace::Comment.new - serializer = with_serializer_lookup_disabled do - ResourceNamespace::PostSerializer.serializer_for(comment) - end - assert_nil serializer - end - end - end - end -end diff --git a/test/serializers/serializer_for_with_namespace_test.rb b/test/serializers/serializer_for_with_namespace_test.rb deleted file mode 100644 index 5c6e3e5e..00000000 --- a/test/serializers/serializer_for_with_namespace_test.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'test_helper' - -module ActiveModel - class Serializer - class SerializerForWithNamespaceTest < ActiveSupport::TestCase - class Book < ::Model - attributes :title, :author_name - associations :publisher, :pages - end - class Page < ::Model; attributes :number, :text end - class Publisher < ::Model; attributes :name end - - module Api - module V3 - class BookSerializer < ActiveModel::Serializer - attributes :title, :author_name - - has_many :pages - belongs_to :publisher - end - - class PageSerializer < ActiveModel::Serializer - attributes :number, :text - end - - class PublisherSerializer < ActiveModel::Serializer - attributes :name - end - end - end - - class BookSerializer < ActiveModel::Serializer - attributes :title, :author_name - end - test 'resource without a namespace' do - book = Book.new(title: 'A Post', author_name: 'hello') - - # TODO: this should be able to pull up this serializer without explicitly specifying the serializer - # currently, with no options, it still uses the Api::V3 serializer - result = ActiveModelSerializers::SerializableResource.new(book, serializer: BookSerializer).serializable_hash - - expected = { title: 'A Post', author_name: 'hello' } - assert_equal expected, result - end - - test 'resource with namespace' do - book = Book.new(title: 'A Post', author_name: 'hi') - - result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash - - expected = { title: 'A Post', author_name: 'hi', pages: nil, publisher: nil } - assert_equal expected, result - end - - test 'has_many with nested serializer under the namespace' do - page = Page.new(number: 1, text: 'hello') - book = Book.new(title: 'A Post', author_name: 'hi', pages: [page]) - - result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash - - expected = { - title: 'A Post', author_name: 'hi', - publisher: nil, - pages: [{ - number: 1, text: 'hello' - }] - } - assert_equal expected, result - end - - test 'belongs_to with nested serializer under the namespace' do - publisher = Publisher.new(name: 'Disney') - book = Book.new(title: 'A Post', author_name: 'hi', publisher: publisher) - - result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash - - expected = { - title: 'A Post', author_name: 'hi', - pages: nil, - publisher: { - name: 'Disney' - } - } - assert_equal expected, result - end - end - end -end diff --git a/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json b/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json deleted file mode 100644 index 9474c509..00000000 --- a/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "properties": { - "name" : { "type" : "string" }, - "description" : { "type" : "string" } - } -} diff --git a/test/support/isolated_unit.rb b/test/support/isolated_unit.rb deleted file mode 100644 index 26948d4a..00000000 --- a/test/support/isolated_unit.rb +++ /dev/null @@ -1,82 +0,0 @@ -# https://github.com/rails/rails/blob/v5.0.0.beta1/railties/test/isolation/abstract_unit.rb - -# Usage Example: -# -# require 'support/isolated_unit' -# -# class RailtieTest < ActiveSupport::TestCase -# include ActiveSupport::Testing::Isolation -# -# class WithRailsDefinedOnLoad < RailtieTest -# setup do -# require 'rails' -# require 'active_model_serializers' -# make_basic_app -# end -# -# # some tests -# end -# -# class WithoutRailsDefinedOnLoad < RailtieTest -# setup do -# require 'active_model_serializers' -# make_basic_app -# end -# -# # some tests -# end -# end -# -# Note: -# It is important to keep this file as light as possible -# the goal for tests that require this is to test booting up -# rails from an empty state, so anything added here could -# hide potential failures -# -# It is also good to know what is the bare minimum to get -# Rails booted up. -require 'bundler/setup' unless defined?(Bundler) -require 'active_support' -require 'active_support/core_ext/string/access' - -# These files do not require any others and are needed -# to run the tests -require 'active_support/testing/autorun' -require 'active_support/testing/isolation' - -module TestHelpers - module Generation - module_function - - # Make a very basic app, without creating the whole directory structure. - # Is faster and simpler than generating a Rails app in a temp directory - def make_basic_app - require 'rails' - require 'action_controller/railtie' - - @app = Class.new(Rails::Application) do - config.eager_load = false - config.session_store :cookie_store, key: '_myapp_session' - config.active_support.deprecation = :log - config.active_support.test_order = :parallel - ActiveSupport::TestCase.respond_to?(:test_order=) && ActiveSupport::TestCase.test_order = :parallel - config.root = File.dirname(__FILE__) - config.log_level = :info - # Set a fake logger to avoid creating the log directory automatically - fake_logger = Logger.new(nil) - config.logger = fake_logger - Rails.application.routes.default_url_options = { host: 'example.com' } - end - @app.respond_to?(:secrets) && @app.secrets.secret_key_base = '3b7cd727ee24e8444053437c36cc66c4' - - yield @app if block_given? - @app.initialize! - end - end -end - -module ActiveSupport - class TestCase - include TestHelpers::Generation - end -end diff --git a/test/support/rails5_shims.rb b/test/support/rails5_shims.rb deleted file mode 100644 index 03a036da..00000000 --- a/test/support/rails5_shims.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Rails5Shims - module ControllerTests - # https://github.com/rails/rails/blob/b217354/actionpack/lib/action_controller/test_case.rb - REQUEST_KWARGS = [:params, :headers, :session, :flash, :method, :body, :xhr].freeze - - def get(path, *args) - fold_kwargs!(args) - super - end - - def post(path, *args) - fold_kwargs!(args) - super - end - - def patch(path, *args) - fold_kwargs!(args) - super - end - - def put(path, *args) - fold_kwargs!(args) - super - end - - # Fold kwargs from test request into args - # Band-aid for DEPRECATION WARNING - def fold_kwargs!(args) - hash = args && args[0] - return unless hash.respond_to?(:key) - Rails5Shims::ControllerTests::REQUEST_KWARGS.each do |kwarg| - next unless hash.key?(kwarg) - value = hash.delete(kwarg) - if value.is_a? String - args.insert(0, value) - else - hash.merge! value - end - end - end - - # Uncomment for debugging where the kwargs warnings come from - # def non_kwarg_request_warning - # super.tap do - # STDOUT.puts caller[2..3] - # end - # end - end -end -if Rails::VERSION::MAJOR < 5 - ActionController::TestCase.send :include, Rails5Shims::ControllerTests - ActionDispatch::IntegrationTest.send :include, Rails5Shims::ControllerTests -end diff --git a/test/support/rails_app.rb b/test/support/rails_app.rb deleted file mode 100644 index 43324b78..00000000 --- a/test/support/rails_app.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'support/isolated_unit' -module ActiveModelSerializers - RailsApplication = TestHelpers::Generation.make_basic_app do |app| - app.configure do - config.secret_key_base = 'abc123' - config.active_support.test_order = :random - config.action_controller.perform_caching = true - config.action_controller.cache_store = :memory_store - - config.filter_parameters += [:password] - end - - app.routes.default_url_options = { host: 'example.com' } - end -end - -Routes = ActionDispatch::Routing::RouteSet.new -Routes.draw do - get ':controller(/:action(/:id))' - get ':controller(/:action)' -end -ActionController::Base.send :include, Routes.url_helpers -ActionController::TestCase.class_eval do - def setup - @routes = Routes - end -end - -# ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)] -# ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__) -# -# Load fixtures from the engine -# if ActiveSupport::TestCase.respond_to?(:fixture_path=) -# ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) -# ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path -# ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" -# ActiveSupport::TestCase.fixtures :all -# end diff --git a/test/support/schemas/active_model_serializers/test/schema_test/my/index.json b/test/support/schemas/active_model_serializers/test/schema_test/my/index.json deleted file mode 100644 index 9474c509..00000000 --- a/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "properties": { - "name" : { "type" : "string" }, - "description" : { "type" : "string" } - } -} diff --git a/test/support/schemas/active_model_serializers/test/schema_test/my/show.json b/test/support/schemas/active_model_serializers/test/schema_test/my/show.json deleted file mode 100644 index 71313665..00000000 --- a/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "properties": { - "name" : { "type" : "integer" }, - "description" : { "type" : "boolean" } - } -} diff --git a/test/support/schemas/custom/show.json b/test/support/schemas/custom/show.json deleted file mode 100644 index 29a47e15..00000000 --- a/test/support/schemas/custom/show.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "file://custom/show.json#", - "properties": { - "name" : { "type" : "string" }, - "description" : { "type" : "string" } - } -} diff --git a/test/support/schemas/hyper_schema.json b/test/support/schemas/hyper_schema.json deleted file mode 100644 index ae1f8f33..00000000 --- a/test/support/schemas/hyper_schema.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/hyper-schema", - "title": "Profile", - "description": "Profile schema", - "stability": "prototype", - "strictProperties": true, - "type": [ - "object" - ], - "definitions": { - "name": { - "description": "unique name of profile", - "readOnly": true, - "type": [ - "string" - ] - }, - "description": { - "description": "description of profile", - "readOnly": true, - "type": [ - "string" - ] - }, - "identity": { - "anyOf": [ - { - "$ref": "/schemata/profile#/definitions/name" - } - ] - } - }, - "links": [ - { - "description": "Create a new profile.", - "href": "/profiles", - "method": "POST", - "rel": "create", - "schema": { - "properties": { - }, - "type": [ - "object" - ] - }, - "title": "Create" - }, - { - "description": "Delete an existing profile.", - "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", - "method": "DELETE", - "rel": "destroy", - "title": "Delete" - }, - { - "description": "Info for existing profile.", - "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", - "method": "GET", - "rel": "self", - "title": "Info" - }, - { - "description": "List existing profiles.", - "href": "/profiles", - "method": "GET", - "rel": "instances", - "title": "List" - }, - { - "description": "Update an existing profile.", - "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", - "method": "PATCH", - "rel": "update", - "schema": { - "properties": { - }, - "type": [ - "object" - ] - }, - "title": "Update" - } - ], - "properties": { - "name": { - "$ref": "/schemata/profile#/definitions/name" - }, - "description": { - "$ref": "/schemata/profile#/definitions/description" - } - }, - "id": "/schemata/profile" -} diff --git a/test/support/schemas/render_using_json_api.json b/test/support/schemas/render_using_json_api.json deleted file mode 100644 index 1a8f8fe1..00000000 --- a/test/support/schemas/render_using_json_api.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "", - "type": "object", - "properties": { - "data": { - "id": "/data", - "type": "object", - "properties": { - "id": { - "id": "/data/id", - "type": "string" - }, - "type": { - "id": "/data/type", - "type": "string" - }, - "attributes": { - "id": "/data/attributes", - "type": "object", - "properties": { - "name": { - "id": "/data/attributes/name", - "type": "string" - }, - "description": { - "id": "/data/attributes/description", - "type": "string" - } - } - } - }, - "required": [ - "id", - "type", - "attributes" - ] - } - }, - "required": [ - "data" - ] -} diff --git a/test/support/schemas/simple_json_pointers.json b/test/support/schemas/simple_json_pointers.json deleted file mode 100644 index d1a6f1eb..00000000 --- a/test/support/schemas/simple_json_pointers.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "properties": { - "name": { - "$ref": "file://custom/show.json#/properties/name" - }, - "description": { - "$ref": "file://custom/show.json#/properties/description" - } - } -} diff --git a/test/support/serialization_testing.rb b/test/support/serialization_testing.rb deleted file mode 100644 index 524a3297..00000000 --- a/test/support/serialization_testing.rb +++ /dev/null @@ -1,71 +0,0 @@ -module SerializationTesting - def config - ActiveModelSerializers.config - end - - private - - def generate_cached_serializer(obj) - ActiveModelSerializers::SerializableResource.new(obj).to_json - end - - def with_namespace_separator(separator) - original_separator = ActiveModelSerializers.config.jsonapi_namespace_separator - ActiveModelSerializers.config.jsonapi_namespace_separator = separator - yield - ensure - ActiveModelSerializers.config.jsonapi_namespace_separator = original_separator - end - - def with_prepended_lookup(lookup_proc) - original_lookup = ActiveModelSerializers.config.serializer_lookup_cahin - ActiveModelSerializers.config.serializer_lookup_chain.unshift lookup_proc - yield - ensure - ActiveModelSerializers.config.serializer_lookup_cahin = original_lookup - end - - # Aliased as :with_configured_adapter to clarify that - # this method tests the configured adapter. - # When not testing configuration, it may be preferable - # to pass in the +adapter+ option to ActiveModelSerializers::SerializableResource. - # e.g ActiveModelSerializers::SerializableResource.new(resource, adapter: :json_api) - def with_adapter(adapter) - old_adapter = ActiveModelSerializers.config.adapter - ActiveModelSerializers.config.adapter = adapter - yield - ensure - ActiveModelSerializers.config.adapter = old_adapter - end - alias with_configured_adapter with_adapter - - def with_config(hash) - old_config = config.dup - ActiveModelSerializers.config.update(hash) - yield - ensure - ActiveModelSerializers.config.replace(old_config) - end - - def with_serializer_lookup_disabled - original_serializer_lookup = ActiveModelSerializers.config.serializer_lookup_enabled - ActiveModelSerializers.config.serializer_lookup_enabled = false - yield - ensure - ActiveModelSerializers.config.serializer_lookup_enabled = original_serializer_lookup - end - - def serializable(resource, options = {}) - ActiveModelSerializers::SerializableResource.new(resource, options) - end -end - -module Minitest - class Test - def before_setup - ActionController::Base.cache_store.clear - end - - include SerializationTesting - end -end diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 294fa33c..00000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,70 +0,0 @@ -# Configure Rails Environment -ENV['RAILS_ENV'] = 'test' -require 'bundler/setup' - -begin - require 'simplecov' - AppCoverage.start -rescue LoadError - STDERR.puts 'Running without SimpleCov' -end - -require 'pry' -require 'timecop' -require 'rails' -require 'action_controller' -require 'action_controller/test_case' -require 'action_controller/railtie' -require 'active_model_serializers' -# For now, we only restrict the options to serializable_hash/as_json/to_json -# in tests, to ensure developers don't add any unsupported options. -# There's no known benefit, at this time, to having the filtering run in -# production when the excluded options would simply not be used. -# -# However, for documentation purposes, the constant -# ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS is defined -# in the Serializer. -ActiveModelSerializers::Adapter::Base.class_eval do - alias_method :original_serialization_options, :serialization_options - - def serialization_options(options) - original_serialization_options(options) - .slice(*ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS) - end -end -require 'fileutils' -FileUtils.mkdir_p(File.expand_path('../../tmp/cache', __FILE__)) - -gem 'minitest' -require 'minitest' -require 'minitest/autorun' -Minitest.backtrace_filter = Minitest::BacktraceFilter.new - -module TestHelper - module_function - - def silence_warnings - original_verbose = $VERBOSE - $VERBOSE = nil - yield - ensure - $VERBOSE = original_verbose - end -end - -require 'support/rails_app' - -# require "rails/test_help" - -require 'support/serialization_testing' - -require 'support/rails5_shims' - -require 'fixtures/active_record' - -require 'fixtures/poro' - -ActiveSupport.on_load(:action_controller) do - $action_controller_logger = ActiveModelSerializers.logger - ActiveModelSerializers.logger = Logger.new(IO::NULL) -end