|
| 1 | +# Usage: |
| 2 | +# class Movie |
| 3 | +# def to_json(payload) |
| 4 | +# FastJsonapi::MultiToJson.to_json(payload) |
| 5 | +# end |
| 6 | +# end |
| 7 | +module FastJsonapi |
| 8 | + module MultiToJson |
| 9 | + # Result object pattern is from https://johnnunemaker.com/resilience-in-ruby/ |
| 10 | + # e.g. https://github.com/github/github-ds/blob/fbda5389711edfb4c10b6c6bad19311dfcb1bac1/lib/github/result.rb |
| 11 | + class Result |
| 12 | + def initialize(*rescued_exceptions) |
| 13 | + rescued_exceptions = [StandardError] if rescued_exceptions.empty? |
| 14 | + @value = yield |
| 15 | + @error = nil |
| 16 | + rescue *rescued_exceptions => e |
| 17 | + @error = e |
| 18 | + end |
| 19 | + |
| 20 | + def ok? |
| 21 | + @error.nil? |
| 22 | + end |
| 23 | + |
| 24 | + def value! |
| 25 | + if ok? |
| 26 | + @value |
| 27 | + else |
| 28 | + raise @error |
| 29 | + end |
| 30 | + end |
| 31 | + |
| 32 | + def rescue |
| 33 | + return self if ok? |
| 34 | + Result.new { yield(@error) } |
| 35 | + end |
| 36 | + end |
| 37 | + |
| 38 | + def self.logger(device=nil) |
| 39 | + return @logger = Logger.new(device) if device |
| 40 | + @logger ||= Logger.new(IO::NULL) |
| 41 | + end |
| 42 | + |
| 43 | + # Encoder-compatible with default MultiJSON adapters and defaults |
| 44 | + def self.to_json_method |
| 45 | + encode_method = String.new(%(def _fast_to_json(object)\n )) |
| 46 | + encode_method << Result.new(LoadError) { |
| 47 | + require 'oj' |
| 48 | + %(::Oj.dump(object, mode: :compat, time_format: :ruby, use_to_json: true)) |
| 49 | + }.rescue { |
| 50 | + require 'yajl' |
| 51 | + %(::Yajl::Encoder.encode(object)) |
| 52 | + }.rescue { |
| 53 | + require 'jrjackson' unless defined?(::JrJackson) |
| 54 | + %(::JrJackson::Json.dump(object)) |
| 55 | + }.rescue { |
| 56 | + require 'json' |
| 57 | + %(JSON.fast_generate(object, create_additions: false, quirks_mode: true)) |
| 58 | + }.rescue { |
| 59 | + require 'gson' |
| 60 | + %(::Gson::Encoder.new({}).encode(object)) |
| 61 | + }.rescue { |
| 62 | + require 'active_support/json/encoding' |
| 63 | + %(::ActiveSupport::JSON.encode(object)) |
| 64 | + }.rescue { |
| 65 | + warn "No JSON encoder found. Falling back to `object.to_json`" |
| 66 | + %(object.to_json) |
| 67 | + }.value! |
| 68 | + encode_method << "\nend" |
| 69 | + end |
| 70 | + |
| 71 | + def self.to_json(object) |
| 72 | + _fast_to_json(object) |
| 73 | + rescue NameError |
| 74 | + define_to_json(FastJsonapi::MultiToJson) |
| 75 | + _fast_to_json(object) |
| 76 | + end |
| 77 | + |
| 78 | + def self.define_to_json(receiver) |
| 79 | + cl = caller_locations[0] |
| 80 | + method_body = to_json_method |
| 81 | + logger.debug { "Defining #{receiver}._fast_to_json as #{method_body.inspect}" } |
| 82 | + receiver.instance_eval method_body, cl.absolute_path, cl.lineno |
| 83 | + end |
| 84 | + |
| 85 | + def self.reset_to_json! |
| 86 | + undef :_fast_to_json if method_defined?(:_fast_to_json) |
| 87 | + logger.debug { "Undefining #{receiver}._fast_to_json" } |
| 88 | + end |
| 89 | + end |
| 90 | +end |
0 commit comments