Skip to content

Instantly share code, notes, and snippets.

@sineed
Last active June 10, 2020 15:42
Show Gist options
  • Save sineed/9c9a5d837d558e79309f90919a9b7aad to your computer and use it in GitHub Desktop.
Save sineed/9c9a5d837d558e79309f90919a9b7aad to your computer and use it in GitHub Desktop.
Upgrade AMS from 0.9.0 to 0.10.0
module AMS
module V09
class Serializer < ActiveModel::Serializer
def serializable_hash(adapter_options = nil,
options = {},
adapter_instance = self.class.serialization_adapter_instance)
object.nil? ? nil : super
end
end
class ArraySerializer < ActiveModel::Serializer::CollectionSerializer
end
class ResourceIdentifier < ActiveModelSerializers::Adapter::JsonApi::ResourceIdentifier
def initialize(serializer, options)
@id = id_for(serializer)
@type = type_for(serializer)
end
private
def id_for(serializer)
serializer.read_attribute_for_serialization(:id)
end
end
class Base < ActiveModelSerializers::Adapter::Base
def initialize(serializer, options = {})
super
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
end
private
attr_reader :fieldset
def attributes_for(serializer, fields)
serializer.attributes(fields).except(:id)
end
def resource_object_for(serializer)
resource_object = serializer.fetch(self) do
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields)
resource_object.merge!(attributes) if attributes.any?
resource_object
end
resource_object
end
end
# represent resource without associations
class Attributes < Base
def serializable_hash(options = nil)
return nil if serializer.nil? || serializer.object.nil?
is_collection = serializer.respond_to?(:each)
serializers = is_collection ? serializer : [serializer]
data = resource_objects_for(serializers)
result = is_collection ? data : data[0]
self.class.transform_key_casing!(result, instance_options)
end
private
def resource_objects_for(serializers)
serializers.map { |serializer| resource_object_for(serializer) }
end
end
class AttributesWithIncluded < Base
def serializable_hash(options = nil)
return {} if serializer.object.nil?
is_collection = serializer.respond_to?(:each)
serializers = is_collection ? serializer : [serializer]
primary_data, included = resource_objects_for(serializers)
hash = {}
hash[root] = is_collection ? primary_data : primary_data[0]
hash[:included] = included if included.any?
hash[meta_key] = meta unless meta.blank?
self.class.transform_key_casing!(hash, instance_options)
end
private
def resource_objects_for(serializers)
@primary = []
@included = {}
@resource_identifiers = Set.new
serializers.each { |serializer| process_resource(serializer) }
serializers.each { |serializer| process_relationships(serializer) }
[@primary, @included]
end
def process_resource(serializer)
return false unless resource_already_processed?(serializer)
@primary << resource_object_for(serializer)
true
end
def process_relationship_resource(serializer, json_key)
return false unless resource_already_processed?(serializer)
@included[json_key] << resource_object_for(serializer)
true
end
def process_relationships(serializer)
serializer.associations.each do |association|
process_relationship(association.serializer, association.key)
end
end
def process_relationship(serializer, key)
json_key = key || serializer.json_key.to_s.pluralize
@included[json_key] ||= []
if serializer.respond_to?(:each)
serializer.each { |s| process_relationship(s, key) }
return
end
return unless serializer && serializer.object
return unless process_relationship_resource(serializer, json_key)
process_relationships(serializer)
end
def resource_already_processed?(serializer)
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
@resource_identifiers.add?(resource_identifier)
end
def meta
instance_options.fetch(:meta, nil)
end
def meta_key
instance_options.fetch(:meta_key, 'meta'.freeze)
end
end
end
end
ActiveModelSerializers::Adapter.register :v09_attributes_with_included, AMS::V09::AttributesWithIncluded
ActiveModelSerializers::Adapter.register :v09_attributes, AMS::V09::Attributes
ActiveModelSerializers.config.adapter = :v09_attributes_with_included

Serializer utilization

Explicit serialization, also change context to scope

-      JSON.parse(
-        ActiveModel::ArraySerializer.new(
-          @categories,
-          each_serializer: CategorySerializer,
-          context: {locale: @locale}
-        ).to_json
-      )
+      ActiveModelSerializers::SerializableResource.new(
+        @categories,
+        each_serializer: CategorySerializer,
+        scope: {locale: @locale}
+      ).serializable_hash

Explicitly set root option

render  json: @accounts,
-                each_serializer: UserSerializer
+                each_serializer: UserSerializer, root: :accounts

Use v09_attributes when I don't need root and included associations

-        LocationSerializer.new(@location, root: false).serializable_hash
+        ActiveModelSerializers::SerializableResource.new(
+          @location,
+          serializer: LocationSerializer,
+          adapter: :v09_attributes
+        ).serializable_hash

Serializer definition

Subclass from V09 serializers

-  class BaseSerializer < ActiveModel::Serializer
-    attributes :id, :type
-
-    def type
-      object.class.model_name.plural
-    end
+  class BaseSerializer < AMS::V09::Serializer

Use type method and provide null id method

-    class EmailSerializer < ActiveModel::Serializer
+    class EmailSerializer < AMS::V09::Serializer
+      type :emails
+      def id
+        nil
+      end

Replace root to key for association, also explicitly define attribute for association

class OrderSerializer < BaseSerializer
-    has_one :consumer, serializer: UserSerializer, root: :users
+    has_one :consumer, serializer: UserSerializer, key: :users
+    attribute :consumer_id,
+    def consumer_id
+      object.consumer_id
+    end
# This is configuration fo 0.9.0 that has been removed
ActiveModel::Serializer.setup do |config|
config.embed = :ids
config.embed_in_root = true
config.embed_in_root_key = :included
end
@oyeanuj
Copy link

oyeanuj commented Dec 21, 2016

@sineed Thank you for posting the gist! I am in the same boat, and found your gist on the issue you opened in AMS.

If i may, can I ask you a couple of basic questions around your code snippets here?

  1. I was having a hard time getting a hang of the the reasons for having the V09 module - what features were you trying to monkey-patch?

  2. How did you solve the problem of not being able to custom define includes, only, except associations that you could do in AMS 0.9? (rails-api/active_model_serializers#1845 talks about it but it never got merged).

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment