Skip to content

Instantly share code, notes, and snippets.

@0legovich
Created February 18, 2019 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0legovich/998bda87171f7a7ec96e51474b78a4ff to your computer and use it in GitHub Desktop.
Save 0legovich/998bda87171f7a7ec96e51474b78a4ff to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
# parse react json schema (https://github.com/mozilla-services/react-jsonschema-form)
# return Array with elements: Symbol, Special Hash
# Symbol - field name
# Special Hash: Hash, where
# * key - nested fields name,
# * value -
# Hash: key - nested object id, value - Array with elements: Symbol or Special Hash
# or
# Array with elements: Symbol or Special Hash
class JsonSchemaVisibleFieldsService
STRING_STUFF = %w[_attributes _form_data _stuff].freeze
def initialize(schema, object)
object_underscore = object.class.base_class.to_s.underscore.to_sym
@schema = schema[:properties][object_underscore]
@object = object
@result = []
end
def self.call(schema, object)
new(schema.deep_symbolize_keys, object).process
end
def process
properties_fields = fields_from_properties(@schema[:properties], @object) if @schema[:properties]
@result += properties_fields
dependency_fields = fields_from_dependencies(@schema[:dependencies], @object) if @schema[:dependencies]
@result += dependency_fields if dependency_fields
@result.uniq
end
private
def fields_from_properties(properties_data, object)
out = []
fields_and_attributes = properties_data.keys
fields = properties_schema_fields(properties_data, object)
schema_attributes = properties_schema_attributes(fields_and_attributes, object)
out += fields
schema_attributes.each do |attributes|
association_name = attribute_association_name(attributes)
association_objects = object.public_send(association_name)
next if association_objects.blank?
out << association_fields(association_objects, properties_data, attributes)
end
out
end
def fields_from_dependencies(dependencies_data, object)
out = []
dependencies_data.each_key do |key|
value_by_object = object.read_attribute(key)
dependency_array = dependencies_data[key][:oneOf]
schema = if dependency_array
valid_dependency_node(dependency_array, key, value_by_object)
elsif value_by_object
dependencies_data[key]
end
next if schema.nil?
properties_fields = fields_from_properties(schema[:properties], object) if schema[:properties]
dependencies_fields = fields_from_dependencies(schema[:dependencies], object) if schema[:dependencies]
out += properties_fields
out += dependencies_fields if dependencies_fields
end
out
end
def valid_dependency_node(dependency_array, key, value_by_object)
out = dependency_array.select do |schema|
enum_hash = schema[:properties].fetch(key, {})
next if enum_hash.blank?
if enum_hash.keys == [:enum]
enum_hash.fetch(:enum, []).include?(value_by_object)
else
enum_hash.fetch(:not, {}).fetch(:enum, []).exclude?(value_by_object)
end
end
raise if out.count > 1
out.first
end
def association_fields(association_objects, properties_data, attributes)
association_out = { attributes => {} }
schema = properties_data[attributes]
if association_objects.is_a? ActiveRecord::Associations::CollectionProxy
return collection_association_fields(association_objects, attributes, schema)
end
properties_fields = fields_from_properties(schema[:properties], association_objects) if schema[:properties]
dependencies_fields = fields_from_dependencies(schema[:dependencies], association_objects) if schema[:dependencies]
association_out[attributes] = properties_fields.to_a + dependencies_fields.to_a
association_out
end
def collection_association_fields(association_objects, attributes, object_schema)
association_out = { attributes => {} }
association_objects.each do |assoc_object|
schema = object_schema[:additionalItems] || object_schema.fetch(:items, {})
properties_fields = fields_from_properties(schema[:properties], assoc_object) if schema[:properties]
dependencies_fields = fields_from_dependencies(schema[:dependencies], assoc_object) if schema[:dependencies]
association_out[attributes][assoc_object.id] = (properties_fields.to_a + dependencies_fields.to_a).uniq
end
association_out
end
def properties_schema_fields(properties_data, object)
fields_and_attributes = properties_data.keys
fields = fields_and_attributes.select { |i| not_stuff?(i) }
stuff_fields = properties_data.select { |k, _v| stuff?(k) && !is_association?(object, k) }
stuff_fields = stuff_fields.values.flat_map { |schema| schema[:properties].keys }
fields + stuff_fields
end
def properties_schema_attributes(fields_and_attributes, object)
fields_and_attributes.select { |i| stuff?(i) && is_association?(object, i) }
end
def is_association?(object, field_name)
object.respond_to?(attribute_association_name(field_name))
end
def attribute_association_name(attributes)
association_name = attributes.dup.to_s
STRING_STUFF.each { |string| association_name.slice! string }
association_name
end
def not_stuff?(field_name)
STRING_STUFF.map { |string| /#{Regexp.quote(string)}/.match?(field_name) }.uniq == [false]
end
def stuff?(field_name)
STRING_STUFF.map { |string| /#{Regexp.quote(string)}/.match?(field_name) }.include?(true)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment