1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
module JsonSchema
# Attributes mixes in some useful attribute-related methods for use in
# defining schema classes in a spirit similar to Ruby's attr_accessor and
# friends.
module Attributes
# Provides class-level methods for the Attributes module.
module ClassMethods
# Attributes that should be copied between classes when invoking
# Attributes#copy_from.
#
# Hash contains instance variable names mapped to a default value for the
# field.
attr_reader :copyable_attrs
# Attributes that are part of the JSON schema and hyper-schema
# specifications. These are allowed to be accessed with the [] operator.
#
# Hash contains the access key mapped to the name of the method that should
# be invoked to retrieve a value. For example, `type` maps to `type` and
# `additionalItems` maps to `additional_items`.
attr_reader :schema_attrs
# identical to attr_accessible, but allows us to copy in values from a
# target schema to help preserve our hierarchy during reference expansion
def attr_copyable(attr, options = {})
attr_accessor(attr)
ref = :"@#{attr}"
# Usually the default being assigned here is nil.
self.copyable_attrs[ref] = options[:default]
if default = options[:default]
# remove the reader already created by attr_accessor
remove_method(attr)
need_dup = [Array, Hash, Set].include?(default.class)
define_method(attr) do
val = instance_variable_get(ref)
if !val.nil?
val
else
need_dup ? default.class.new : default
end
end
end
if options[:clear_cache]
remove_method(:"#{attr}=")
define_method(:"#{attr}=") do |value|
instance_variable_set(options[:clear_cache], nil)
instance_variable_set(ref, value)
end
end
end
def attr_schema(attr, options = {})
attr_copyable(attr, :default => options[:default], :clear_cache => options[:clear_cache])
self.schema_attrs[options[:schema_name] || attr] = attr
end
# Directive indicating that attributes should be inherited from a parent
# class.
#
# Must appear as first statement in class that mixes in (or whose parent
# mixes in) the Attributes module.
def inherit_attrs
@copyable_attrs = self.superclass.instance_variable_get(:@copyable_attrs).dup
@schema_attrs = self.superclass.instance_variable_get(:@schema_attrs).dup
end
# Initializes some class instance variables required to make other
# methods in the Attributes module work. Run automatically when the
# module is mixed into another class.
def initialize_attrs
@copyable_attrs = {}
@schema_attrs = {}
end
end
def self.included(klass)
klass.extend(ClassMethods)
klass.send(:initialize_attrs)
end
# Allows the values of schema attributes to be accessed with a symbol or a
# string. So for example, the value of `schema.additional_items` could be
# procured with `schema[:additionalItems]`. This only works for attributes
# that are part of the JSON schema specification; other methods on the
# class are not available (e.g. `expanded`.)
#
# This is implemented so that `JsonPointer::Evaluator` can evaluate a
# reference on an sintance of this class (as well as plain JSON data).
def [](name)
name = name.to_sym
if self.class.schema_attrs.key?(name)
send(self.class.schema_attrs[name])
else
raise NoMethodError, "Schema does not respond to ##{name}"
end
end
def copy_from(schema)
self.class.copyable_attrs.each do |copyable, _|
instance_variable_set(copyable, schema.instance_variable_get(copyable))
end
end
def initialize_attrs
self.class.copyable_attrs.each do |attr, _|
instance_variable_set(attr, nil)
end
end
end
end
|