From 975793f72442553733ab653245fd57b5846631c8 Mon Sep 17 00:00:00 2001 From: Joe Woodward Date: Sat, 1 Sep 2018 18:24:06 +0700 Subject: [PATCH] Simplify jsonapi_class configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds • JSONAPI::Rails::SerializableClassMapping class Overriding Hash’s lookup can be confusing without creating an descendent class. - Old behavior inferrer.class == Hash Doesn’t make it obvious that there’s custom behavior - New behavior inferrer.class == JSONAPI::Rails::SerializableClassMapping Now it’s obvious where to look for the unusual behavior This setup also allows us to define the default mappings and the lookup behavior in separate configuration options • configuration options for 1. jsonapi_class_mapper 2. jsonapi_class_mappings 3. jsonapi_errors_class_mapper (fallback to jsonapi_class_mapper if nil) 4. jsonapi_errors_class_mappings Removes • configration options for 1. jsonapi_class 2. jsonapi_errors_class --- .../initializer/templates/initializer.rb | 36 ++++++++++++------- lib/jsonapi/rails/configuration.rb | 27 +++++++------- lib/jsonapi/rails/controller/hooks.rb | 12 +++++-- .../rails/serializable_class_mapping.rb | 13 +++++++ 4 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 lib/jsonapi/rails/serializable_class_mapping.rb diff --git a/lib/generators/jsonapi/initializer/templates/initializer.rb b/lib/generators/jsonapi/initializer/templates/initializer.rb index 36a9774..13d444e 100644 --- a/lib/generators/jsonapi/initializer/templates/initializer.rb +++ b/lib/generators/jsonapi/initializer/templates/initializer.rb @@ -1,19 +1,31 @@ JSONAPI::Rails.configure do |config| - # # Set a default serializable class mapping. - # config.jsonapi_class = Hash.new { |h, k| - # names = k.to_s.split('::') + # # Set a default serializable class mapper + # # Can be a Proc or any object that responds to #call(class_name) + # # e.g. MyCustomMapper.call('User::Article') => User::SerializableArticle + # config.jsonapi_class_mapper = -> (class_name) do + # names = class_name.to_s.split('::') # klass = names.pop - # h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize + # [*names, "Serializable#{klass}"].join('::').safe_constantize + # end + # + # # Set any default serializable class mappings + # config.jsonapi_class_mappings = { + # :Car => SerializableVehicle, + # :Boat => SerializableVehicle # } # - # # Set a default serializable class mapping for errors. - # config.jsonapi_errors_class = Hash.new { |h, k| - # names = k.to_s.split('::') - # klass = names.pop - # h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize - # }.tap { |h| - # h[:'ActiveModel::Errors'] = JSONAPI::Rails::SerializableActiveModelErrors - # h[:Hash] = JSONAPI::Rails::SerializableErrorHash + # # Set a default serializable class mapper for errors. + # # Can be a Proc or any object that responds to #call(class_name) + # # e.g. MyCustomMapper.call('PORO::Error') => PORO::SerializableError + # # If no jsonapi_errors_class_mapper is configured jsonapi_class_mapper will + # # be used + # config.jsonapi_errors_class_mapper = config.jsonapi_class_mapper.dup + # + # # Set any default serializable class errors mappings + # config.jsonapi_errors_class_mappings = { + # :'MyCustomModule::ErrorObject' => MyCustomModule::SerializableErrorObject, + # :'ActiveModel::Errors' => JSONAPI::Rails::SerializableActiveModelErrors, + # :Hash => JSONAPI::Rails::SerializableErrorHash # } # # # Set a default JSON API object. diff --git a/lib/jsonapi/rails/configuration.rb b/lib/jsonapi/rails/configuration.rb index 7a7f76f..6e5b8bb 100644 --- a/lib/jsonapi/rails/configuration.rb +++ b/lib/jsonapi/rails/configuration.rb @@ -7,22 +7,21 @@ class Configuration < ActiveSupport::InheritableOptions; end # @private module Configurable - DEFAULT_JSONAPI_CLASS = Hash.new do |h, k| - names = k.to_s.split('::') + DEFAULT_JSONAPI_CLASS_MAPPER = -> (class_name) do + names = class_name.to_s.split('::') klass = names.pop - h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize - end.freeze + [*names, "Serializable#{klass}"].join('::').safe_constantize + end - DEFAULT_JSONAPI_ERRORS_CLASS = DEFAULT_JSONAPI_CLASS.dup.merge!( - 'ActiveModel::Errors'.to_sym => - JSONAPI::Rails::SerializableActiveModelErrors, - 'Hash'.to_sym => JSONAPI::Rails::SerializableErrorHash - ).freeze + DEFAULT_JSONAPI_CLASS_MAPPINGS = {}.freeze - DEFAULT_JSONAPI_OBJECT = { - version: '1.0' + DEFAULT_JSONAPI_ERROR_CLASS_MAPPINGS = { + :'ActiveModel::Errors' => JSONAPI::Rails::SerializableActiveModelErrors, + :Hash => JSONAPI::Rails::SerializableErrorHash }.freeze + DEFAULT_JSONAPI_OBJECT = { version: '1.0' }.freeze + DEFAULT_JSONAPI_CACHE = ->() { nil } DEFAULT_JSONAPI_EXPOSE = lambda { @@ -42,8 +41,10 @@ module Configurable DEFAULT_LOGGER = Logger.new(STDERR) DEFAULT_CONFIG = { - jsonapi_class: DEFAULT_JSONAPI_CLASS, - jsonapi_errors_class: DEFAULT_JSONAPI_ERRORS_CLASS, + jsonapi_class_mapper: DEFAULT_JSONAPI_CLASS_MAPPER, + jsonapi_class_mappings: DEFAULT_JSONAPI_CLASS_MAPPINGS, + jsonapi_errors_class_mapper: nil, + jsonapi_errors_class_mappings: DEFAULT_JSONAPI_ERROR_CLASS_MAPPINGS, jsonapi_cache: DEFAULT_JSONAPI_CACHE, jsonapi_expose: DEFAULT_JSONAPI_EXPOSE, jsonapi_fields: DEFAULT_JSONAPI_FIELDS, diff --git a/lib/jsonapi/rails/controller/hooks.rb b/lib/jsonapi/rails/controller/hooks.rb index d18a6c6..32243d5 100644 --- a/lib/jsonapi/rails/controller/hooks.rb +++ b/lib/jsonapi/rails/controller/hooks.rb @@ -1,4 +1,5 @@ require 'jsonapi/rails/configuration' +require 'jsonapi/rails/serializable_class_mapping' module JSONAPI module Rails @@ -11,14 +12,21 @@ module Hooks # Overridden by the `class` renderer option. # @return [Hash{Symbol=>Class}] def jsonapi_class - JSONAPI::Rails.config[:jsonapi_class].dup + JSONAPI::Rails::SerializableClassMapping.new( + JSONAPI::Rails.config[:jsonapi_class_mapper], + JSONAPI::Rails.config[:jsonapi_class_mappings].dup + ) end # Hook for serializable class mapping (for errors). # Overridden by the `class` renderer option. # @return [Hash{Symbol=>Class}] def jsonapi_errors_class - JSONAPI::Rails.config[:jsonapi_errors_class].dup + mapper = JSONAPI::Rails.config[:jsonapi_errors_class_mapper] || + JSONAPI::Rails.config[:jsonapi_class_mapper] + JSONAPI::Rails::SerializableClassMapping.new( + mapper, JSONAPI::Rails.config[:jsonapi_errors_class_mappings].dup + ) end # Hook for the jsonapi object. diff --git a/lib/jsonapi/rails/serializable_class_mapping.rb b/lib/jsonapi/rails/serializable_class_mapping.rb new file mode 100644 index 0000000..70b2941 --- /dev/null +++ b/lib/jsonapi/rails/serializable_class_mapping.rb @@ -0,0 +1,13 @@ +module JSONAPI + module Rails + # @private + class SerializableClassMapping < Hash + def initialize(mapper, default_mappings = {}) + super() do |hash, class_name_sym| + hash[class_name_sym] = + mapper.call(class_name_sym.to_s) + end.merge!(default_mappings) + end + end + end +end