class JSONModelType

A common base class for all JSONModel classes

Attributes

uri[R]

Public Class Methods

add_validation(name, level = :error, &block) click to toggle source

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
from_hash(hash, raise_errors = true, trusted = false) click to toggle source

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
from_json(s, raise_errors = true) click to toggle source

Create an instance of this JSONModel from a JSON string.

# File lib/aspace_client/jsonmodel_type.rb, line 88
def self.from_json(s, raise_errors = true)
  self.from_hash(ASUtils.json_parse(s), raise_errors)
end
id_for(uri, opts = {}, noerror = false) click to toggle source

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
init(type, schema, mixins = []) click to toggle source

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
new(params = {}, trusted = false) click to toggle source
# 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
record_type() click to toggle source

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
schema() click to toggle source

Return the JSON schema that defines this JSONModel class

# File lib/aspace_client/jsonmodel_type.rb, line 27
def self.schema
  find_ancestor_class_instance(:@schema)
end
schema_version() click to toggle source

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
to_s() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 45
def self.to_s
  "JSONModel(:#{self.record_type})"
end
type_of(path) click to toggle source

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
uri_and_remaining_options_for(id = nil, opts = {}) click to toggle source
# 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
uri_for(id = nil, opts = {}) click to toggle source

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

substitute_parameters(uri, opts = {}) click to toggle source

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(hash, raise_errors = true) click to toggle source

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(attributes) click to toggle source

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
find_ancestor_class_instance(variable) click to toggle source

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
id_to_int(id) click to toggle source
# 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

[](key) click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 205
def [](key)
  @data[key.to_s]
end
[]=(key, val) click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 210
def []=(key, val)
  @validated = false
  @data[key.to_s] = val
end
_always_valid!() click to toggle source

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
_exceptions() click to toggle source

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
_warnings() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 255
def _warnings
  exceptions = self._exceptions

  if exceptions.has_key?(:warnings)
    exceptions[:warnings]
  else
    []
  end
end
add_error(attribute, message) click to toggle source
Calls superclass method 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
clear_errors() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 239
def clear_errors
  # reset validation
  @validated = false
  @errors = nil
end
has_key?(key) click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 216
def has_key?(key)
  @data.has_key?(key)
end
id() click to toggle source

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
inspect() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 301
def inspect
  self.to_s
end
instance_data() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 200
def instance_data
  @instance_data
end
replace(params) click to toggle source

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
reset_from(another_jsonmodel) click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 291
def reset_from(another_jsonmodel)
  @data = another_jsonmodel.instance_eval { @data }
end
to_hash(mode = nil) click to toggle source

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
to_json(opts = {}) click to toggle source

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
to_s() click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 296
def to_s
  "#<JSONModel(:#{self.class.record_type}) #{@data.inspect}>"
end
update(params) click to toggle source

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
uri=(val) click to toggle source
# File lib/aspace_client/jsonmodel_type.rb, line 195
def uri=(val)
  @uri = val
  self['uri'] = val
end

Protected Instance Methods

validate(data, raise_errors = true) click to toggle source
# 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

set_data(data) click to toggle source
# 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