Created
February 18, 2019 18:13
-
-
Save 0legovich/998bda87171f7a7ec96e51474b78a4ff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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