app/api/core/io/base/json_formatting_behaviour.rb
# frozen_string_literal: true
module Core::Io::Base::JsonFormattingBehaviour
def self.extended(base)
base.class_eval do
extend ::Core::Io::Base::JsonFormattingBehaviour::Input
extend ::Core::Io::Base::JsonFormattingBehaviour::Output
class_attribute :attribute_to_json_field, instance_writer: false
self.attribute_to_json_field = {}
delegate :json_field_for, to: 'self.class'
end
end
# NOTE: This one is OK!
def as_json(options = nil)
options ||= {}
object = options.delete(:object)
object_json(object, options)
end
#--
# Very root level does absolutely nothing useful!
#++
def object_json(*args); end
def json_field_for(attribute) # rubocop:todo Metrics/AbcSize
return attribute_to_json_field[attribute.to_s] if attribute_to_json_field.key?(attribute.to_s)
# We have to assume that this could be an association that is being exposed, in which case we'll
# need to determine the I/O class that deals with it and hand off the error handling to it.
association, *association_parts = attribute.to_s.split('.')
return attribute.to_s if association_parts.empty?
reflection = model_for_input.reflections[association]
return attribute.to_s if reflection.nil?
# TODO: 'association' here should really be garnered from the appropriate endpoint
association_json_field =
::Core::Io::Registry.instance.lookup_for_class(reflection.klass).json_field_for(association_parts.join('.'))
"#{association}.#{association_json_field}"
end
def set_json_root(name)
@json_root = name.to_sym
end
def json_root
@json_root or raise StandardError, "JSON root is not set for #{name}"
end
def api_root
json_root.to_s.pluralize
end
def define_attribute_and_json_mapping(mapping)
parse_mapping_rules(mapping) do |attribute_to_json, json_to_attribute|
attribute_to_json_field.merge!(attribute_to_json.to_h)
generate_object_to_json_mapping(attribute_to_json)
generate_json_to_object_mapping(json_to_attribute)
end
end
VALID_LINE_REGEXP = /^\s*((?:[a-z_][\w_]*\.)*[a-z_][\w_]*[?!]?)\s*(<=|<=>|=>)\s*((?:[a-z_][\w_]*\.)*[a-z_][\w_]*)\s*$/
def parse_mapping_rules(mapping) # rubocop:todo Metrics/AbcSize
attribute_to_json, json_to_attribute = [], []
StringIO
.new(mapping)
.each_line do |line|
next if line.blank? || line =~ (/^\s*#/)
match = VALID_LINE_REGEXP.match(line) or raise StandardError, "Invalid line: #{line.inspect}"
attribute_to_json.push([match[1], match[3]]) if (match[2] =~ /<?=>/)
json_to_attribute.push([match[3], /<=>?/.match?(match[2]) ? match[1] : nil])
end
yield(attribute_to_json, json_to_attribute)
end
private :parse_mapping_rules
end