193 lines
5.2 KiB
Ruby
193 lines
5.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# rubocop:disable Lint/DuplicateRescueException
|
|
begin
|
|
require 'active_support/inflector'
|
|
rescue LoadError
|
|
require 'dry/inflector'
|
|
rescue LoadError
|
|
warn(
|
|
'No inflector found. Install `dry-inflector` or `active_support/inflector`'
|
|
)
|
|
end
|
|
# rubocop:enable Lint/DuplicateRescueException
|
|
|
|
module JSONAPI
|
|
module Serializer
|
|
# Our serializer DSL
|
|
module DSL
|
|
attr_writer :record_type, :record_id
|
|
attr_accessor(
|
|
:transform_method,
|
|
:attributes_to_serialize,
|
|
:relationships_to_serialize,
|
|
:data_links,
|
|
:meta_to_serialize,
|
|
:cache_store_instance,
|
|
:cache_store_options
|
|
)
|
|
|
|
def inherited(subclass)
|
|
super
|
|
|
|
subclass.attributes_to_serialize = attributes_to_serialize.dup
|
|
subclass.relationships_to_serialize = relationships_to_serialize.dup
|
|
subclass.transform_method = transform_method
|
|
subclass.data_links = data_links.dup
|
|
subclass.cache_store_instance = cache_store_instance
|
|
subclass.cache_store_options = cache_store_options
|
|
subclass.meta_to_serialize = meta_to_serialize
|
|
subclass.record_id = record_id
|
|
|
|
::JSONAPI::Serializer.register_serializer(subclass)
|
|
end
|
|
|
|
def set_key_transform(transform_name)
|
|
@transform_method = TRANSFORMS_MAPPING[transform_name.to_sym]
|
|
end
|
|
|
|
def set_type(type_name)
|
|
@record_type = type_name
|
|
end
|
|
|
|
def record_type
|
|
@record_type ||= run_key_transform(
|
|
name.chomp('Serializer'),
|
|
[:demodulize, :underscore]
|
|
)
|
|
|
|
run_key_transform(@record_type)
|
|
end
|
|
|
|
def set_id(id_name = nil, &block)
|
|
@record_id = block || id_name
|
|
end
|
|
|
|
def record_id
|
|
@record_id || :id
|
|
end
|
|
|
|
def attributes(*attributes_list, **options, &block)
|
|
@attributes_to_serialize ||= {}
|
|
|
|
attributes_list.each do |attr_name|
|
|
@attributes_to_serialize[attr_name] = {
|
|
key: attr_name,
|
|
method: block || attr_name,
|
|
options: options.freeze
|
|
}.freeze
|
|
end
|
|
end
|
|
alias attribute attributes
|
|
|
|
def belongs_to(relationship_name, options = {}, &block)
|
|
@relationships_to_serialize ||= {}
|
|
|
|
@relationships_to_serialize[relationship_name] = {
|
|
name: relationship_name,
|
|
relationship_type: __callee__,
|
|
options: options,
|
|
object_block: block
|
|
}
|
|
|
|
# Run the resolver...
|
|
::JSONAPI::Serializer
|
|
.serializers.map(&:resolve_relationship_serializers!)
|
|
end
|
|
alias has_one belongs_to
|
|
alias has_many belongs_to
|
|
|
|
def meta(meta_name = nil, &block)
|
|
@meta_to_serialize = meta_name || block
|
|
end
|
|
|
|
def link(link_name, link_method_name = nil, **options, &block)
|
|
@data_links ||= {}
|
|
|
|
@data_links[link_name] = {
|
|
key: link_name,
|
|
method: link_method_name || block,
|
|
options: options.freeze
|
|
}.freeze
|
|
end
|
|
|
|
def cache_options(cache_options)
|
|
@cache_store_instance = cache_options[:store]
|
|
@cache_store_options = cache_options.except(:store).freeze
|
|
end
|
|
|
|
def resolve_relationship_serializers!
|
|
return if @relationships_to_serialize.nil?
|
|
|
|
@relationships_to_serialize.each do |_rel_name, relationship|
|
|
next if relationship.frozen?
|
|
|
|
resolve_relationship_serializer(relationship)
|
|
end
|
|
rescue NotFoundError
|
|
# Ignore it, probably not all serializers are defined...
|
|
end
|
|
|
|
def run_key_transform(key, transform_methods = nil)
|
|
return key.to_sym if inflector.nil?
|
|
|
|
tmethods = @transform_method || transform_methods
|
|
|
|
return key.to_sym if tmethods.nil?
|
|
|
|
Array(tmethods).each do |tmethod|
|
|
key = inflector.send(tmethod, key.to_s)
|
|
end
|
|
|
|
key.to_sym
|
|
end
|
|
|
|
private
|
|
|
|
# Maps tranformations to inflector methods
|
|
TRANSFORMS_MAPPING = {
|
|
camel: :camelize,
|
|
camel_lower: [:camelize, :lower],
|
|
dash: :dasherize,
|
|
underscore: :underscore
|
|
}.freeze
|
|
|
|
# Resolves serializer options of relationship into proper classe(s)
|
|
#
|
|
# We skip classes and procs since those suggest per record serializer
|
|
# classes. On no options available, the name of the relationship is used
|
|
# to reflect on the serializer class.
|
|
#
|
|
# @params relationship [Hash] the relationship data
|
|
# @return nil
|
|
def resolve_relationship_serializer(relationship)
|
|
opts = relationship[:options]
|
|
|
|
# The two are exclusive...
|
|
return opts.delete(:serializer) unless opts[:serializers].nil?
|
|
|
|
sname = opts[:serializer] || relationship[:name].to_s
|
|
|
|
return if sname.is_a?(Class) || sname.is_a?(Proc)
|
|
|
|
singularize = relationship[:relationship_type] == :has_many
|
|
|
|
sname = inflector.singularize(sname) if inflector && singularize
|
|
|
|
opts[:serializer] = ::JSONAPI::Serializer.for_type(sname)
|
|
|
|
opts.freeze && relationship.freeze
|
|
end
|
|
|
|
# Helper method to pick an available inflector implementation
|
|
#
|
|
# @return [Object]
|
|
def inflector
|
|
ActiveSupport::Inflector
|
|
rescue NameError
|
|
Dry::Inflector.new
|
|
end
|
|
end
|
|
end
|
|
end
|