class JSONModelType
A common base class for all JSONModel
classes
Attributes
Public Class Methods
Add a custom validation to this model type.
The validation is a block that takes a hash of properties and returns an array of pairs like:
- [“propertyname”, “the problem with it”], …
# File lib/aspace_client/jsonmodel_type.rb, line 54 def self.add_validation(name, level = :error, &block) raise "Validation name already taken: #{name}" if JSONModel.custom_validations[name] JSONModel.custom_validations[name] = block self.schema["validations"] ||= [] self.schema["validations"] << [level, name] end
Create an instance of this JSONModel
from the data contained in ‘hash’.
# File lib/aspace_client/jsonmodel_type.rb, line 65 def self.from_hash(hash, raise_errors = true, trusted = false) hash["jsonmodel_type"] = self.record_type.to_s # If we're running in client mode, leave 'readonly' properties in place, # since they're intended for use by clients. Otherwise, we drop them. drop_system_properties = !JSONModel.client_mode? if trusted # We got this data from a trusted source (such as another JSONModel # that had already been validated itself). No need to double up self.new(hash, true) else cleaned = JSONSchemaUtils.drop_unknown_properties(hash, self.schema, drop_system_properties) cleaned = ASUtils.jsonmodels_to_hashes(cleaned) validate(cleaned, raise_errors) self.new(cleaned) end end
The inverse of uri_for
:
JSONModel(:archival_object).id_for("/repositories/123/archival_objects/500", :repo_id => 123) might yield 500
# File lib/aspace_client/jsonmodel_type.rb, line 130 def self.id_for(uri, opts = {}, noerror = false) if not self.schema['uri'] if noerror return nil else raise "Missing a URI definition for class #{self.class}" end end pattern = self.schema['uri'] pattern = pattern.gsub(/\/:[a-zA-Z_]+\//, '/[^/ ]+/') # IDs are either positive integers, or importer-provided logical IDs id_regexp = /([0-9]+|import_[a-f0-9-]+)/ if uri =~ /#{pattern}\/#{id_regexp}(\#.*)?$/ return id_to_int($1) elsif uri =~ /#{pattern.gsub(/\[\^\/ \]\+\/tree/, '')}#{id_regexp}\/tree$/ return id_to_int($1) else if noerror nil else raise "Couldn't make an ID out of URI: #{uri}" end end end
Class instance variables store the bits specific to this model
# File lib/aspace_client/jsonmodel_type.rb, line 5 def self.init(type, schema, mixins = []) @record_type = type @schema = schema # In client mode, mix in some extra convenience methods for querying the # ArchivesSpace backend service via HTTP. if JSONModel.client_mode? require_relative 'jsonmodel_client' include JSONModel::Client end define_accessors(schema['properties'].keys) mixins.each do |mixin| include(mixin) end end
# File lib/aspace_client/jsonmodel_type.rb, line 176 def initialize(params = {}, trusted = false) set_data(params) @uri ||= params['uri'] # a hash to store transient instance data @instance_data = {} self.class.define_accessors(@data.keys) if trusted @validated = {} @cleaned_data = @data end end
Return the type of this JSONModel
class (a keyword like :archival_object)
# File lib/aspace_client/jsonmodel_type.rb, line 40 def self.record_type find_ancestor_class_instance(:@record_type) end
Return the version number of this JSONModel’s schema
# File lib/aspace_client/jsonmodel_type.rb, line 33 def self.schema_version self.schema['version'] end
# File lib/aspace_client/jsonmodel_type.rb, line 45 def self.to_s "JSONModel(:#{self.record_type})" end
Return the type of the schema property defined by ‘path’
For example, type_of
(“names/items/type”) might return a JSONModel
class
# File lib/aspace_client/jsonmodel_type.rb, line 162 def self.type_of(path) type = JSONSchemaUtils.schema_path_lookup(self.schema, path)["type"] ref = JSONModel.parse_jsonmodel_ref(type) if ref JSONModel.JSONModel(ref.first) else Kernel.const_get(type.capitalize) end end
# File lib/aspace_client/jsonmodel_type.rb, line 93 def self.uri_and_remaining_options_for(id = nil, opts = {}) # Some schemas (like name schemas) don't have a URI because they don't # need endpoints. That's fine. if not self.schema['uri'] return nil end uri = self.schema['uri'] if not id.nil? uri += "/#{URI.escape(id.to_s)}" end self.substitute_parameters(uri, opts) end
Given a numeric internal ID and additional options produce a pair containing a URI reference. For example:
JSONModel(:archival_object).uri_for(500, :repo_id => 123) might yield "/repositories/123/archival_objects/500"
# File lib/aspace_client/jsonmodel_type.rb, line 117 def self.uri_for(id = nil, opts = {}) result = self.uri_and_remaining_options_for(id, opts) result ? result[0] : nil end
Protected Class Methods
Given a URI template like /repositories/:repo_id/something/:somevar, and a hash containing keys and replacement strings, return [uri, opts], where ‘uri’ is the template with values substituted for their placeholders, and ‘opts’ is any parameters that weren’t consumed.
# File lib/aspace_client/jsonmodel_type.rb, line 387 def self.substitute_parameters(uri, opts = {}) matched = [] opts.each do |k, v| old = uri uri = uri.gsub(":#{k}", URI.escape(v.to_s)) if old != uri if v.is_a? Symbol raise ("Tried to substitute the value '#{v.inspect}' for ':#{k}'." + " This is usually a sign that something has gone wrong" + " further up the stack. (URI was: '#{uri}')") end # Matched on this parameter. Remove it from the passed in hash matched << k end end if uri.include?(":") raise "Template substitution was incomplete: '#{uri}'" end remaining_opts = opts.clone matched.each do |k| remaining_opts.delete(k) end [uri, remaining_opts] end
Validate the supplied hash using the JSON
schema for this model. Raise a ValidationException if there are any fatal validation problems, or if strict mode is enabled and warnings were produced.
# File lib/aspace_client/jsonmodel_type.rb, line 362 def self.validate(hash, raise_errors = true) properties = JSONSchemaUtils.drop_unknown_properties(hash, self.schema) ValidatorCache.with_validator_for(self, properties) do |validator| messages = validator.validate exceptions = JSONSchemaUtils.parse_schema_messages(messages, validator) if raise_errors && (!exceptions[:errors].empty? || (JSONModel.strict_mode? && !exceptions[:warnings].empty?)) raise JSONModel::ValidationException.new(:invalid_object => self.new(hash), :warnings => exceptions[:warnings], :errors => exceptions[:errors], :attribute_types => exceptions[:attribute_types]) end exceptions.reject{|k, v| v.empty?} end end
Private Class Methods
Define accessors for all variable names listed in ‘attributes’
# File lib/aspace_client/jsonmodel_type.rb, line 441 def self.define_accessors(attributes) attributes.each do |attribute| if not method_defined? "#{attribute}" define_method "#{attribute}" do @data[attribute] end end if not method_defined? "#{attribute}=" define_method "#{attribute}=" do |value| @validated = false @data[attribute] = JSONModel.clean_data(value) end end end end
If this class is subclassed, we won’t be able to see our class instance variables unless we explicitly look up the inheritance chain.
# File lib/aspace_client/jsonmodel_type.rb, line 423 def self.find_ancestor_class_instance(variable) @ancestor_instance_variables ||= Atomic.new({}) if !@ancestor_instance_variables.value[variable] self.ancestors.each do |clz| val = clz.instance_variable_get(variable) if val @ancestor_instance_variables.update {|vs| vs.merge({variable => val})} break end end end @ancestor_instance_variables.value[variable] end
# File lib/aspace_client/jsonmodel_type.rb, line 461 def self.id_to_int(id) if id =~ /^import_/ id else id.to_i end end
Public Instance Methods
# File lib/aspace_client/jsonmodel_type.rb, line 205 def [](key) @data[key.to_s] end
# File lib/aspace_client/jsonmodel_type.rb, line 210 def []=(key, val) @validated = false @data[key.to_s] = val end
Set this object instance to always pass validation. Used so the frontend can create intentionally incomplete objects that will be filled out by the user.
# File lib/aspace_client/jsonmodel_type.rb, line 269 def _always_valid! @always_valid = true self end
Validate the current JSONModel
instance and return a list of exceptions produced.
# File lib/aspace_client/jsonmodel_type.rb, line 223 def _exceptions return @validated if @validated && @errors.nil? exceptions = {} if not @always_valid exceptions = self.validate(@data, false) end if @errors exceptions[:errors] = (exceptions[:errors] or {}).merge(@errors) end exceptions end
# File lib/aspace_client/jsonmodel_type.rb, line 255 def _warnings exceptions = self._exceptions if exceptions.has_key?(:warnings) exceptions[:warnings] else [] end end
JSONModel::Client#add_error
# File lib/aspace_client/jsonmodel_type.rb, line 246 def add_error(attribute, message) # reset validation @validated = false # call JSONModel::Client's version super end
# File lib/aspace_client/jsonmodel_type.rb, line 239 def clear_errors # reset validation @validated = false @errors = nil end
# File lib/aspace_client/jsonmodel_type.rb, line 216 def has_key?(key) @data.has_key?(key) end
Return the internal ID of this JSONModel
.
# File lib/aspace_client/jsonmodel_type.rb, line 340 def id ref = JSONModel::parse_reference(self.uri) if ref ref[:id] else nil end end
# File lib/aspace_client/jsonmodel_type.rb, line 301 def inspect self.to_s end
# File lib/aspace_client/jsonmodel_type.rb, line 200 def instance_data @instance_data end
Replace the values of the current JSONModel
instance with the contents of ‘params’, validating before accepting the replacement.
# File lib/aspace_client/jsonmodel_type.rb, line 285 def replace(params) @validated = false set_data(params) end
# File lib/aspace_client/jsonmodel_type.rb, line 291 def reset_from(another_jsonmodel) @data = another_jsonmodel.instance_eval { @data } end
Produce a (possibly nested) hash from the values of this JSONModel
. Any values that don’t appear in the JSON
schema will not appear in the result.
# File lib/aspace_client/jsonmodel_type.rb, line 309 def to_hash(mode = nil) mode = (mode || :validated) raise "Invalid .to_hash mode: #{mode}" unless [:trusted, :validated, :raw].include?(mode) return @data if mode == :raw if @validated and @cleaned_data return @cleaned_data end cleaned = JSONSchemaUtils.drop_unknown_properties(@data, self.class.schema) cleaned = ASUtils.jsonmodels_to_hashes(cleaned) if mode == :validated @validated = false self.validate(cleaned) end @cleaned_data = cleaned end
Produce a JSON
string from the values of this JSONModel
. Any values that don’t appear in the JSON
schema will not appear in the result.
# File lib/aspace_client/jsonmodel_type.rb, line 334 def to_json(opts = {}) ASUtils.to_json(self.to_hash(opts[:mode]), opts.is_a?(Hash) ? opts.merge(:max_nesting => false) : {}) end
# File lib/aspace_client/jsonmodel_type.rb, line 296 def to_s "#<JSONModel(:#{self.class.record_type}) #{@data.inspect}>" end
Update the values of the current JSONModel
instance with the contents of ‘params’, validating before accepting the update.
# File lib/aspace_client/jsonmodel_type.rb, line 277 def update(params) @validated = false replace(ASUtils.deep_merge(@data, params)) end
# File lib/aspace_client/jsonmodel_type.rb, line 195 def uri=(val) @uri = val self['uri'] = val end
Protected Instance Methods
# File lib/aspace_client/jsonmodel_type.rb, line 354 def validate(data, raise_errors = true) @validated = self.class.validate(data, raise_errors) end
Private Instance Methods
# File lib/aspace_client/jsonmodel_type.rb, line 470 def set_data(data) hash = JSONModel.clean_data(data) hash["jsonmodel_type"] = self.class.record_type.to_s hash = JSONSchemaUtils.apply_schema_defaults(hash, self.class.schema) @data = hash end