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..714c69ed 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) @@ -101,203 +36,8 @@ been released yet. Please see below for the documentation relevant to you. ## High-level behavior -Choose an adapter from [adapters](lib/active_model_serializers/adapter): - -``` ruby -ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes` -``` - -Given a [serializable model](lib/active_model/serializer/lint.rb): - -```ruby -# either -class SomeResource < ActiveRecord::Base - # columns: title, body -end -# or -class SomeResource < ActiveModelSerializers::Model - attributes :title, :body -end -``` - -And initialized as: - -```ruby -resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration') -``` - -Given a serializer for the serializable model: - -```ruby -class SomeSerializer < ActiveModel::Serializer - attribute :title, key: :name - attributes :body -end -``` - -The model can be serialized as: - -```ruby -options = {} -serialization = ActiveModelSerializers::SerializableResource.new(resource, options) -serialization.to_json -serialization.as_json -``` - -SerializableResource delegates to the adapter, which it builds as: - -```ruby -adapter_options = {} -adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options) -adapter.to_json -adapter.as_json -adapter.serializable_hash -``` - -The adapter formats the serializer's attributes and associations (a.k.a. includes): - -```ruby -serializer_options = {} -serializer = SomeSerializer.new(resource, serializer_options) -serializer.attributes -serializer.associations -``` - ## Architecture -This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, -please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or -[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md). - -The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile). - -### ActiveModel::Serializer - -An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) -and exposes an `attributes` method, among a few others. -It allows you to specify which attributes and associations should be represented in the serializatation of the resource. -It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself. -It may be useful to think of it as a -[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters). - -#### ActiveModel::CollectionSerializer - -The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers -and, if there is no serializer, primitives. - -### ActiveModelSerializers::Adapter::Base - -The **`ActiveModelSerializeres::Adapter::Base`** describes the structure of the JSON document generated from a -serializer. For example, the `Attributes` example represents each serializer as its -unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON -API](http://jsonapi.org/) document. - -### ActiveModelSerializers::SerializableResource - -The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter -to an object that responds to `to_json`, and `as_json`. It is used in the controller to -encapsulate the serialization resource when rendered. However, it can also be used on its own -to serialize a resource outside of a controller, as well. - -### Primitive handling - -Definitions: A primitive is usually a String or Array. There is no serializer -defined for them; they will be serialized when the resource is converted to JSON (`as_json` or -`to_json`). (The below also applies for any object with no serializer.) - -- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all. - -Internally, if no serializer can be found in the controller, the resource is not decorated by -ActiveModelSerializers. - -- However, when a primitive value is an attribute or in a collection, it is not modified. - -When serializing a collection and the collection serializer (CollectionSerializer) cannot -identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128). -For example, when caught by `Reflection#build_association`, and the association value is set directly: - -```ruby -reflection_options[:virtual_value] = association_value.try(:as_json) || association_value -``` - -(which is called by the adapter as `serializer.associations(*)`.) - -### How options are parsed - -High-level overview: - -- For a **collection** - - `:serializer` specifies the collection serializer and - - `:each_serializer` specifies the serializer for each resource in the collection. -- For a **single resource**, the `:serializer` option is the resource serializer. -- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by - [`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5). - The remaining options are serializer options. - -Details: - -1. **ActionController::Serialization** - 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)` - 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`). - The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5). -1. **ActiveModelSerializers::SerializableResource** - 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.) - - Where `serializer?` is `use_adapter? && !!(serializer)` - - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil); - False when explicit adapter is falsy (nil or false)' - - Where `serializer`: - 1. from explicit `:serializer` option, else - 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)` - 1. A side-effect of checking `serializer` is: - - The `:serializer` option is removed from the serializer_opts hash - - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option - 1. The serializer and adapter are created as - 1. `serializer_instance = serializer.new(resource, serializer_opts)` - 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)` -1. **ActiveModel::Serializer::CollectionSerializer#new** - 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts - is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16). -1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for - resource as defined by the serializer. - -(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)` -methods on the resource serialization by the Rails JSON renderer. They are, therefore, important -to know about, but not part of ActiveModelSerializers.) - -### What does a 'serializable resource' look like? - -- An `ActiveRecord::Base` object. -- Any Ruby object that passes the - [Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests) - [code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb). - -ActiveModelSerializers provides a -[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb), -which is a simple serializable PORO (Plain-Old Ruby Object). - -`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code. - -```ruby -class MyModel < ActiveModelSerializers::Model - attributes :id, :name, :level -end -``` - -The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an -ActiveRecord::Base object or not. - -Outside of the controller the rules are **exactly** the same as for records. For example: - -```ruby -render json: MyModel.new(level: 'awesome'), adapter: :json -``` - -would be serialized the same as - -```ruby -ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json -``` - ## Semantic Versioning This project adheres to [semver](http://semver.org/) 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