diff --git a/lib/fast_jsonapi/multi_to_json.rb b/lib/fast_jsonapi/multi_to_json.rb new file mode 100644 index 0000000..b43f1a0 --- /dev/null +++ b/lib/fast_jsonapi/multi_to_json.rb @@ -0,0 +1,90 @@ +# Usage: +# class Movie +# def to_json(payload) +# FastJsonapi::MultiToJson.to_json(payload) +# end +# end +module FastJsonapi + module MultiToJson + # Result object pattern is from https://johnnunemaker.com/resilience-in-ruby/ + # e.g. https://github.com/github/github-ds/blob/fbda5389711edfb4c10b6c6bad19311dfcb1bac1/lib/github/result.rb + class Result + def initialize(*rescued_exceptions) + rescued_exceptions = [StandardError] if rescued_exceptions.empty? + @value = yield + @error = nil + rescue *rescued_exceptions => e + @error = e + end + + def ok? + @error.nil? + end + + def value! + if ok? + @value + else + raise @error + end + end + + def rescue + return self if ok? + Result.new { yield(@error) } + end + end + + def self.logger(device=nil) + return @logger = Logger.new(device) if device + @logger ||= Logger.new(IO::NULL) + end + + # Encoder-compatible with default MultiJSON adapters and defaults + def self.to_json_method + encode_method = String.new(%(def _fast_to_json(object)\n )) + encode_method << Result.new(LoadError) { + require 'oj' + %(::Oj.dump(object, mode: :compat, time_format: :ruby, use_to_json: true)) + }.rescue { + require 'yajl' + %(::Yajl::Encoder.encode(object)) + }.rescue { + require 'jrjackson' unless defined?(::JrJackson) + %(::JrJackson::Json.dump(object)) + }.rescue { + require 'json' + %(JSON.fast_generate(object, create_additions: false, quirks_mode: true)) + }.rescue { + require 'gson' + %(::Gson::Encoder.new({}).encode(object)) + }.rescue { + require 'active_support/json/encoding' + %(::ActiveSupport::JSON.encode(object)) + }.rescue { + warn "No JSON encoder found. Falling back to `object.to_json`" + %(object.to_json) + }.value! + encode_method << "\nend" + end + + def self.to_json(object) + _fast_to_json(object) + rescue NameError + define_to_json(FastJsonapi::MultiToJson) + _fast_to_json(object) + end + + def self.define_to_json(receiver) + cl = caller_locations[0] + method_body = to_json_method + logger.debug { "Defining #{receiver}._fast_to_json as #{method_body.inspect}" } + receiver.instance_eval method_body, cl.absolute_path, cl.lineno + end + + def self.reset_to_json! + undef :_fast_to_json if method_defined?(:_fast_to_json) + logger.debug { "Undefining #{receiver}._fast_to_json" } + end + end +end diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index d8d7d6d..7daea60 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -1,4 +1,5 @@ require 'active_support/concern' +require 'fast_jsonapi/multi_to_json' module FastJsonapi module SerializationCore @@ -67,7 +68,7 @@ module FastJsonapi # Override #to_json for alternative implementation def to_json(payload) - JSON.fast_generate(payload) if payload.present? + FastJsonapi::MultiToJson.to_json(payload) if payload.present? end # includes handler