class ActiveRecordExtended::QueryMethods::Json::JsonChain

Constants

ARRAY_OPTIONS
DEFAULT_ALIAS
TO_JSONB_OPTIONS

Public Class Methods

new(scope) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 22
def initialize(scope)
  @scope = scope
end

Public Instance Methods

json_build_literal!(*args) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 41
def json_build_literal!(*args)
  options = json_object_options(args, only: [:values, :col_alias])
  build_json_literal(Arel::Nodes::JsonBuildObject, **options)
end
json_build_object!(*args) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 31
def json_build_object!(*args)
  options = json_object_options(args, except: [:values, :cast_with, :order_by])
  build_json_object(Arel::Nodes::JsonBuildObject, **options)
end
jsonb_build_literal!(*args) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 46
def jsonb_build_literal!(*args)
  options = json_object_options(args, only: [:values, :col_alias])
  build_json_literal(Arel::Nodes::JsonbBuildObject, **options)
end
jsonb_build_object!(*args) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 36
def jsonb_build_object!(*args)
  options = json_object_options(args, except: [:values, :cast_with, :order_by])
  build_json_object(Arel::Nodes::JsonbBuildObject, **options)
end
row_to_json!(**args, &block) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 26
def row_to_json!(**args, &block)
  options = json_object_options(args, except: [:values, :value])
  build_row_to_json(**options, &block)
end

Private Instance Methods

build_json_literal(arel_klass, values:, col_alias: DEFAULT_ALIAS) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 53
def build_json_literal(arel_klass, values:, col_alias: DEFAULT_ALIAS)
  json_values    = flatten_to_sql(values.to_a) { |value| literal_key(value) }
  col_alias      = double_quote(col_alias)
  json_build_obj = arel_klass.new(json_values)
  @scope.select(nested_alias_escape(json_build_obj, col_alias))
end
build_json_object(arel_klass, from:, key: key_generator, value: nil, col_alias: DEFAULT_ALIAS) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 60
def build_json_object(arel_klass, from:, key: key_generator, value: nil, col_alias: DEFAULT_ALIAS)
  tbl_alias         = double_quote(key)
  col_alias         = double_quote(col_alias)
  col_key           = literal_key(key)
  col_value         = to_arel_sql(value.presence || tbl_alias)
  json_build_object = arel_klass.new(to_sql_array(col_key, col_value))

  unless /".+"/.match?(col_value)
    warn("`#{col_value}`: the `value` argument should contain a double quoted key reference for safety")
  end

  @scope.select(nested_alias_escape(json_build_object, col_alias)).from(nested_alias_escape(from, tbl_alias))
end
build_row_to_json(from:, **options, &block) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 74
def build_row_to_json(from:, **options, &block)
  key         = options[:key]
  row_to_json = ::Arel::Nodes::RowToJson.new(double_quote(key))
  row_to_json = ::Arel::Nodes::ToJsonb.new(row_to_json) if options.dig(:cast_with, :to_jsonb)

  dummy_table = from_clause_constructor(from, key).select(row_to_json)
  dummy_table = dummy_table.instance_eval(&block) if block
  return dummy_table if options[:col_alias].blank?

  query = wrap_row_to_json(dummy_table, options)
  @scope.select(query)
end
casting_options(cast_with) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 132
def casting_options(cast_with)
  return {} if cast_with.blank?

  skip_convert = [Symbol, TrueClass, FalseClass]
  Array(cast_with).compact.each_with_object({}) do |arg, options|
    arg                  = arg.to_sym unless skip_convert.include?(arg.class)
    options[:to_jsonb]  |= TO_JSONB_OPTIONS.include?(arg)
    options[:array]     |= ARRAY_OPTIONS.include?(arg)
    options[:array_agg] |= arg == :array_agg
    options[:distinct]  |= arg == :distinct
  end
end
json_object_options(args, except: [], only: []) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 101
def json_object_options(args, except: [], only: []) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
  options   = {}
  lean_opts = lambda do |key, &block|
    if only.present?
      options[key] ||= block.call if only.include?(key)
    elsif !except.include?(key)
      options[key] ||= block.call
    end
  end

  flatten_safely(args) do |arg|
    next if arg.nil?

    if arg.is_a?(Hash)
      lean_opts.call(:key)       { arg.fetch(:key, key_generator) }
      lean_opts.call(:value)     { arg[:value].presence }
      lean_opts.call(:col_alias) { arg[:as] }
      lean_opts.call(:order_by)  { order_by_expression(arg[:order_by]) }
      lean_opts.call(:from)      { arg[:from].tap { |from_clause| pipe_cte_with!(from_clause) } }
      lean_opts.call(:cast_with) { casting_options(arg[:cast_with]) }
    end

    unless except.include?(:values)
      options[:values] ||= []
      options[:values] << (arg.respond_to?(:to_a) ? arg.to_a : arg)
    end
  end

  options.tap(&:compact!)
end
wrap_row_to_json(dummy_table, options) click to toggle source
# File lib/active_record_extended/query_methods/json.rb, line 87
def wrap_row_to_json(dummy_table, options)
  cast_opts = options[:cast_with]
  col_alias = options[:col_alias]
  order_by  = options[:order_by]

  if cast_opts[:array_agg] || cast_opts[:distinct]
    wrap_with_agg_array(dummy_table, col_alias, order_by: order_by, distinct: cast_opts[:distinct])
  elsif cast_opts[:array]
    wrap_with_array(dummy_table, col_alias, order_by: order_by)
  else
    nested_alias_escape(dummy_table, col_alias)
  end
end