From 400c0adc9738767cdf6fabb596e4e4d85dfa2246 Mon Sep 17 00:00:00 2001 From: eno Date: Sat, 20 Apr 2019 13:34:49 +0200 Subject: [PATCH] Adds lazy JSON parsing This class implements lazy JSON parsing. When enabled via Simple::SQL::JSON.lazy! it should improve performance if data is fetched from the database in a JSON format, and is consumed via a JSON encoder. This seems a bit far-fetched, but that use case is actually quite common when implementing a JSON API. --- lib/simple/sql/helpers.rb | 1 + lib/simple/sql/helpers/decoder.rb | 4 ++-- lib/simple/sql/json.rb | 37 +++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 lib/simple/sql/json.rb diff --git a/lib/simple/sql/helpers.rb b/lib/simple/sql/helpers.rb index 0f236c6..b6eddf3 100644 --- a/lib/simple/sql/helpers.rb +++ b/lib/simple/sql/helpers.rb @@ -1,6 +1,7 @@ module ::Simple::SQL::Helpers end +require_relative "./json.rb" require_relative "helpers/decoder.rb" require_relative "helpers/encoder.rb" require_relative "helpers/row_converter.rb" diff --git a/lib/simple/sql/helpers/decoder.rb b/lib/simple/sql/helpers/decoder.rb index 3ae7c55..c27bc5b 100644 --- a/lib/simple/sql/helpers/decoder.rb +++ b/lib/simple/sql/helpers/decoder.rb @@ -21,8 +21,8 @@ def decode_value(type, s) when :"timestamp without time zone" then ::Time.parse(s) when :"timestamp with time zone" then ::Time.parse(s) when :hstore then HStore.parse(s) - when :json then ::JSON.parse(s) - when :jsonb then ::JSON.parse(s) + when :json then ::Simple::SQL::JSON.parse(s) + when :jsonb then ::Simple::SQL::JSON.parse(s) when :boolean then s == "t" else # unknown value, we just return the string here. diff --git a/lib/simple/sql/json.rb b/lib/simple/sql/json.rb new file mode 100644 index 0000000..2474d42 --- /dev/null +++ b/lib/simple/sql/json.rb @@ -0,0 +1,37 @@ +# rubocop:disable Style/DoubleNegation + +# This class implements lazy JSON parsing. When enabled it should improve +# performance if data is fetched from the database in a JSON format, and is +# consumed via a JSON encoder. +# +# This seems a bit far-fetched, but that use case is actually quite common +# when implementing a JSON API. +class Simple::SQL::JSON < BasicObject + def self.parse(str) + new(str) + end + + def initialize(json_string) + @json_string = json_string + end + + def method_missing(sym, *args, &block) + parsed.send(sym, *args, &block) || super + end + + def respond_to_missing?(name, include_private = false) + parsed.send(:respond_to_missing?, name, include_private) || super + end + + def parsed + @parsed ||= ::JSON.parse(@json_string) + end + + def ==(other) + super || (parsed == other) + end + + def to_json(*_args) + @json_string + end +end