Skip to content

Commit

Permalink
memoize Schema methods that do significant calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan committed Jun 13, 2018
1 parent 75fe96a commit df023b9
Showing 1 changed file with 75 additions and 63 deletions.
138 changes: 75 additions & 63 deletions lib/scorpio/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module Scorpio
class Schema
include Memoize

def initialize(schema_object)
if schema_object.is_a?(Scorpio::Schema)
raise(TypeError, "will not instantiate Schema from another Schema: #{schema_object.pretty_inspect.chomp}")
Expand Down Expand Up @@ -101,86 +103,96 @@ def match_to_instance(instance)
return self
end

def subschema_for_property(property_name)
if schema_object['properties'].respond_to?(:to_hash) && schema_object['properties'][property_name].respond_to?(:to_hash)
self.class.new(schema_object['properties'][property_name])
else
if schema_object['patternProperties'].respond_to?(:to_hash)
_, pattern_schema_object = schema_object['patternProperties'].detect do |pattern, _|
property_name.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
end
end
if pattern_schema_object
self.class.new(pattern_schema_object)
def subschema_for_property(property_name_)
memoize(:subschema_for_property, property_name_) do |property_name|
if schema_object['properties'].respond_to?(:to_hash) && schema_object['properties'][property_name].respond_to?(:to_hash)
self.class.new(schema_object['properties'][property_name])
else
if schema_object['additionalProperties'].respond_to?(:to_hash)
self.class.new(schema_object['additionalProperties'])
if schema_object['patternProperties'].respond_to?(:to_hash)
_, pattern_schema_object = schema_object['patternProperties'].detect do |pattern, _|
property_name.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
end
end
if pattern_schema_object
self.class.new(pattern_schema_object)
else
nil
if schema_object['additionalProperties'].respond_to?(:to_hash)
self.class.new(schema_object['additionalProperties'])
else
nil
end
end
end
end
end

def subschema_for_index(index)
if schema_object['items'].respond_to?(:to_ary)
if index < schema_object['items'].size
self.class.new(schema_object['items'][index])
elsif schema_object['additionalItems'].respond_to?(:to_hash)
self.class.new(schema_object['additionalItems'])
def subschema_for_index(index_)
memoize(:subschema_for_index, index_) do |index|
if schema_object['items'].respond_to?(:to_ary)
if index < schema_object['items'].size
self.class.new(schema_object['items'][index])
elsif schema_object['additionalItems'].respond_to?(:to_hash)
self.class.new(schema_object['additionalItems'])
end
elsif schema_object['items'].respond_to?(:to_hash)
self.class.new(schema_object['items'])
else
nil
end
elsif schema_object['items'].respond_to?(:to_hash)
self.class.new(schema_object['items'])
else
nil
end
end

def describes_array?
schema_node['type'] == 'array' ||
schema_node['items'] ||
schema_node['additionalItems'] ||
schema_node['default'].respond_to?(:to_ary) || # TODO make sure this is right
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_ary) }) ||
schema_node['maxItems'] ||
schema_node['minItems'] ||
schema_node.key?('uniqueItems') ||
schema_node['oneOf'].respond_to?(:to_ary) &&
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
schema_node['allOf'].respond_to?(:to_ary) &&
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
schema_node['anyOf'].respond_to?(:to_ary) &&
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_array? }
memoize(:describes_array?) do
schema_node['type'] == 'array' ||
schema_node['items'] ||
schema_node['additionalItems'] ||
schema_node['default'].respond_to?(:to_ary) || # TODO make sure this is right
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_ary) }) ||
schema_node['maxItems'] ||
schema_node['minItems'] ||
schema_node.key?('uniqueItems') ||
schema_node['oneOf'].respond_to?(:to_ary) &&
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
schema_node['allOf'].respond_to?(:to_ary) &&
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
schema_node['anyOf'].respond_to?(:to_ary) &&
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_array? }
end
end
def describes_hash?
schema_node['type'] == 'object' ||
schema_node['required'].respond_to?(:to_ary) ||
schema_node['properties'].respond_to?(:to_hash) ||
schema_node['additionalProperties'] ||
schema_node['patternProperties'] ||
schema_node['default'].respond_to?(:to_hash) ||
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_hash) }) ||
schema_node['oneOf'].respond_to?(:to_ary) &&
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
schema_node['allOf'].respond_to?(:to_ary) &&
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
schema_node['anyOf'].respond_to?(:to_ary) &&
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? }
memoize(:describes_hash?) do
schema_node['type'] == 'object' ||
schema_node['required'].respond_to?(:to_ary) ||
schema_node['properties'].respond_to?(:to_hash) ||
schema_node['additionalProperties'] ||
schema_node['patternProperties'] ||
schema_node['default'].respond_to?(:to_hash) ||
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_hash) }) ||
schema_node['oneOf'].respond_to?(:to_ary) &&
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
schema_node['allOf'].respond_to?(:to_ary) &&
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
schema_node['anyOf'].respond_to?(:to_ary) &&
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? }
end
end

def described_hash_property_names
Set.new.tap do |property_names|
if schema_node['properties'].respond_to?(:to_hash)
property_names.merge(schema_node['properties'].keys)
end
if schema_node['required'].respond_to?(:to_ary)
property_names.merge(schema_node['required'].to_ary)
end
# we _could_ look at the properties of 'default' and each 'enum' but ... nah.
# we should look at dependencies (TODO).
%w(oneOf allOf anyOf).select { |k| schema_node[k].respond_to?(:to_ary) }.each do |schemas_key|
schema_node[schemas_key].map(&:deref).map do |someof_node|
property_names.merge(self.class.new(someof_node).described_hash_property_names)
memoize(:described_hash_property_names) do
Set.new.tap do |property_names|
if schema_node['properties'].respond_to?(:to_hash)
property_names.merge(schema_node['properties'].keys)
end
if schema_node['required'].respond_to?(:to_ary)
property_names.merge(schema_node['required'].to_ary)
end
# we _could_ look at the properties of 'default' and each 'enum' but ... nah.
# we should look at dependencies (TODO).
%w(oneOf allOf anyOf).select { |k| schema_node[k].respond_to?(:to_ary) }.each do |schemas_key|
schema_node[schemas_key].map(&:deref).map do |someof_node|
property_names.merge(self.class.new(someof_node).described_hash_property_names)
end
end
end
end
Expand Down

0 comments on commit df023b9

Please sign in to comment.