From b169ed387b22d9823d2205852c656ddf21a87720 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 3 Feb 2016 11:12:40 -0600 Subject: [PATCH] Make serializers serializable, step 1. --- CHANGELOG.md | 2 + lib/active_model/serializer.rb | 51 ++++++++++++++++++++++++ test/serializers/serialization_test.rb | 55 ++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 test/serializers/serialization_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a2cb9d4..39f40107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Breaking changes: Features: +- [#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. diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 6b3d4090..fe51fb8f 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -126,6 +126,57 @@ module ActiveModel true end + # @return [Hash] containing the attributes and first level + # associations, similar to how ActiveModel::Serializers::JSON is used + # in ActiveRecord::Base. + # + # TODO: Move to here the Attributes adapter logic for + # +serializable_hash_for_single_resource(options)+ + # and include ActiveModel::Serializers::JSON. + # So that the below is true: + # @param options [nil, Hash] The same valid options passed to `serializable_hash` + # (:only, :except, :methods, and :include). + # + # See + # https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serializers/json.rb#L17-L101 + # https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serialization.rb#L85-L123 + # https://github.com/rails/rails/blob/v5.0.0.beta2/activerecord/lib/active_record/serialization.rb#L11-L17 + # https://github.com/rails/rails/blob/v5.0.0.beta2/activesupport/lib/active_support/core_ext/object/json.rb#L147-L162 + # + # @example + # # The :only and :except options can be used to limit the attributes included, and work + # # similar to the attributes method. + # serializer.as_json(only: [:id, :name]) + # serializer.as_json(except: [:id, :created_at, :age]) + # + # # To include the result of some method calls on the model use :methods: + # serializer.as_json(methods: :permalink) + # + # # To include associations use :include: + # serializer.as_json(include: :posts) + # # Second level and higher order associations work as well: + # serializer.as_json(include: { posts: { include: { comments: { only: :body } }, only: :title } }) + def serializable_hash(adapter_opts = nil) + adapter_opts ||= {} + adapter_opts = { include: '*', adapter: :attributes }.merge!(adapter_opts) + adapter = ActiveModelSerializers::Adapter.create(self, adapter_opts) + adapter.serializable_hash(adapter_opts) + end + alias to_hash serializable_hash + alias to_h serializable_hash + + # @see #serializable_hash + # TODO: When moving attributes adapter logic here, @see #serializable_hash + # So that the below is true: + # @param options [nil, Hash] The same valid options passed to `as_json` + # (:root, :only, :except, :methods, and :include). + # The default for `root` is nil. + # The default value for include_root is false. You can change it to true if the given + # JSON string includes a single root node. + def as_json(adapter_opts = nil) + serializable_hash(adapter_opts) + end + # Used by adapter as resource root. def json_key root || object.class.model_name.to_s.underscore diff --git a/test/serializers/serialization_test.rb b/test/serializers/serialization_test.rb new file mode 100644 index 00000000..e8785aad --- /dev/null +++ b/test/serializers/serialization_test.rb @@ -0,0 +1,55 @@ +module ActiveModel + class Serializer + class SerializationTest < ActiveSupport::TestCase + class Blog < ActiveModelSerializers::Model + attr_accessor :id, :name, :authors + end + class Author < ActiveModelSerializers::Model + attr_accessor :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 = ActiveModel::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