module JSONModel

Public Class Methods

JSONModel(source) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 57
def self.JSONModel(source)
  if !@@models.has_key?(source.to_s)
    load_schema(source.to_s)
  end

  @@models[source.to_s] or raise "JSONModel not found for #{source}"
end
add_error_handler(&block) click to toggle source
# File lib/aspace_client/jsonmodel_client.rb, line 52
def self.add_error_handler(&block)
  @@error_handlers << block
end
all(uri, type_descriptor) click to toggle source

Grab an array of JSON objects from ‘uri’ and use the ‘type_descriptor’ property of each object to cast it into a JSONModel.

# File lib/aspace_client/jsonmodel_client.rb, line 23
def self.all(uri, type_descriptor)
  JSONModel::HTTP.get_json(uri).map do |obj|
    JSONModel(obj[type_descriptor.to_s]).new(obj)
  end
end
allow_unmapped_enum_value(val, magic_value = 'other_unmapped') click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 125
def self.allow_unmapped_enum_value(val, magic_value = 'other_unmapped')
  if val.is_a? Array
    val.each { |elt| allow_unmapped_enum_value(elt) }
  elsif val.is_a? Hash
    val.each do |k, v|
      if k == 'enum'
        v << magic_value
       else
        allow_unmapped_enum_value(v)
      end
    end
  end
end
backend_url() click to toggle source
# File lib/aspace_client/jsonmodel_client.rb, line 41
def self.backend_url
  if Module.const_defined?(:BACKEND_SERVICE_URL)
    BACKEND_SERVICE_URL
  else
    init_args[:url]
  end
end
client_mode?() click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 298
def self.client_mode?
  @@init_args[:client_mode]
end
custom_validations() click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 18
def self.custom_validations
  @@custom_validations
end
destroy_model(type) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 100
def self.destroy_model(type)
  @@models.delete(type.to_s)
end
enum_default_value(name) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 293
def self.enum_default_value(name)
  @@init_args[:enum_source].default_value_for(name)
end
enum_values(name) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 288
def self.enum_values(name)
  @@init_args[:enum_source].values_for(name)
end
handle_error(err) click to toggle source
# File lib/aspace_client/jsonmodel_client.rb, line 56
def self.handle_error(err)
  @@error_handlers.each do |handler|
    handler.call(err)
  end
end
init(opts = {}) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 218
def self.init(opts = {})

  @@init_args ||= nil

  # Skip initialisation if this model has already been loaded.
  if @@init_args[:initialized]
    return true
  else 
     @@init_args[:initialized] = true
  end

  if opts.has_key?(:strict_mode)
    @@strict_mode = true
  end

  @@init_args = opts

  if !opts.has_key?(:enum_source)
    if opts[:client_mode]
      require_relative 'jsonmodel_client'
      opts[:enum_source] = JSONModel::Client::EnumSource.new
    else
      raise "Required JSONModel.init arg :enum_source was missing"
    end
  end

  # Load all JSON schemas from the schemas subdirectory
  # Create a model class for each one.
  Dir.glob(File.join(File.dirname(__FILE__),
                     "schemas",
                     "*.rb")).sort.each do |schema|
    schema_name = File.basename(schema, ".rb")
    load_schema(schema_name)
  end

  require_relative "validations"

  # For dynamic enums, automatically slot in the 'other_unmapped' string as an allowable value
  if @@init_args[:allow_other_unmapped]
    enum_wrapper = Struct.new(:enum_source).new(@@init_args[:enum_source])

    def enum_wrapper.valid?(name, value)
      value == 'other_unmapped' || enum_source.valid?(name, value)
    end

    def enum_wrapper.editable?(name)
      enum_source.editable?(name)
    end

    def enum_wrapper.values_for(name)
      enum_source.values_for(name) + ['other_unmapped']
    end

    def enum_wrapper.default_value_for(name)
      enum_source.default_value_for(name)
    end

    @@init_args[:enum_source] = enum_wrapper
  end

  true

rescue
  # If anything went wrong we're not initialised.
  @@init_args = nil

  raise $!
end
load_schema(schema_name) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 140
def self.load_schema(schema_name)
  if not @@models[schema_name]

    old_verbose = $VERBOSE
    $VERBOSE = nil
    src = schema_src(schema_name)

    return if !src

    entry = eval(src)
    $VERBOSE = old_verbose

    parent = entry[:schema]["parent"]
    if parent
      load_schema(parent)

      base = @@models[parent].schema["properties"].clone
      properties = ASUtils.deep_merge(base, entry[:schema]["properties"])

      # Maybe we'll eventually want the version of a schema to be
      # automatically set to max(my_version, parent_version), but for now...
      if entry[:schema]["version"] < @@models[parent].schema_version
        raise ("Can't inherit from a JSON schema whose version is newer than ours " +
               "(our (#{schema_name}) version: #{entry[:schema]['version']}; " +
               "parent (#{parent}) version: #{@@models[parent].schema_version})")
      end

      entry[:schema]["properties"] = properties
    end

    # All records have a lock_version property that we use for optimistic concurrency control.
    entry[:schema]["properties"]["lock_version"] = {"type" => ["integer", "string"], "required" => false}

    # All records must indicate their model type
    entry[:schema]["properties"]["jsonmodel_type"] = {"type" => "string", "ifmissing" => "error"}

    # All records have audit fields
    entry[:schema]["properties"]["created_by"] = {"type" => "string", "readonly" => true}
    entry[:schema]["properties"]["last_modified_by"] = {"type" => "string", "readonly" => true}
    entry[:schema]["properties"]["user_mtime"] = {"type" => "date-time", "readonly" => true}
    entry[:schema]["properties"]["system_mtime"] = {"type" => "date-time", "readonly" => true}
    entry[:schema]["properties"]["create_time"] = {"type" => "date-time", "readonly" => true}

    # Records may include a reference to the repository that contains them
    entry[:schema]["properties"]["repository"] ||= {
      "type" => "object",
      "subtype" => "ref",
      "readonly" => "true",
      "properties" => {
        "ref" => {
          "type" => "JSONModel(:repository) uri",
          "ifmissing" => "error",
          "readonly" => "true"
        },
        "_resolved" => {
          "type" => "object",
          "readonly" => "true"
        }
      }
    }


    if @@init_args && @@init_args[:allow_other_unmapped]
      allow_unmapped_enum_value(entry[:schema]['properties'])
    end

    ASUtils.find_local_directories("schemas/#{schema_name}_ext.rb").
            select {|path| File.exists?(path)}.
            each do |schema_extension|
      entry[:schema]['properties'] = ASUtils.deep_merge(entry[:schema]['properties'],
                                                        eval(File.open(schema_extension).read))
    end

    self.create_model_for(schema_name, entry[:schema])
  end
end
parse_jsonmodel_ref(ref) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 303
def self.parse_jsonmodel_ref(ref)
  if ref.is_a? String and ref =~ /JSONModel\(:([a-zA-Z_\-]+)\) (.*)/
    [$1.intern, $2]
  else
    nil
  end
end
parse_reference(reference, opts = {}) click to toggle source

Parse a URI reference like /repositories/123/archival_objects/500 into {:id => 500, :type => :archival_object}

# File lib/aspace_client/jsonmodel.rb, line 88
def self.parse_reference(reference, opts = {})
  @@models.each do |type, model|
    id = model.id_for(reference, opts, true)
    if id
      return {:id => id, :type => type, :repository => repository_for(reference)}
    end
  end

  nil
end
repository() click to toggle source

The currently selected repository (if any)

# File lib/aspace_client/jsonmodel_client.rb, line 16
def self.repository
  Thread.current[:selected_repo_id]
end
repository_for(reference) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 77
def self.repository_for(reference)
  if reference =~ /^(\/repositories\/[0-9]+)\//
    return $1
  else
    return nil
  end
end
schema_src(schema_name) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 105
def self.schema_src(schema_name)

  if schema_name.to_s !~ /\A[0-9A-Za-z_-]+\z/
    raise "Invalid schema name: #{schema_name}"
  end

  [*ASUtils.find_local_directories('schemas'),
   File.join(File.dirname(__FILE__), "schemas")].each do |dir|

    schema = File.join(dir, "#{schema_name}.rb")

    if File.exists?(schema)
      return File.open(schema).read
    end
  end

  nil
end
set_repository(id) click to toggle source

Set the repository that subsequent operations will apply to.

# File lib/aspace_client/jsonmodel_client.rb, line 10
def self.set_repository(id)
  Thread.current[:selected_repo_id] = id
end
strict_mode?() click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 27
def self.strict_mode?
  @@strict_mode
end
with_repository(id) { || ... } click to toggle source
# File lib/aspace_client/jsonmodel_client.rb, line 30
def self.with_repository(id)
  old_repo = Thread.current[:selected_repo_id]
  begin
    self.set_repository(id)
    yield
  ensure
    self.set_repository(old_repo)
  end
end

Protected Class Methods

clean_data(data) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 315
def self.clean_data(data)
  data = ASUtils.keys_as_strings(data) if data.is_a?(Hash)
  data = ASUtils.jsonmodels_to_hashes(data)

  data
end
create_model_for(type, schema) click to toggle source

Create and return a new JSONModel class called ‘type’, based on the JSONSchema ‘schema’

# File lib/aspace_client/jsonmodel.rb, line 325
def self.create_model_for(type, schema)

  cls = Class.new(JSONModelType)
  cls.init(type, schema, Array(@@init_args[:mixins]))

  @@models[type] = cls
end
init_args() click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 334
def self.init_args
  @@init_args
end

Public Instance Methods

JSONModel(source) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 66
def JSONModel(source)
  JSONModel.JSONModel(source)
end
models() click to toggle source

Yield all known JSONModel classes

# File lib/aspace_client/jsonmodel.rb, line 72
def models
  @@models
end
strict_mode(val) click to toggle source
# File lib/aspace_client/jsonmodel.rb, line 22
def strict_mode(val)
  @@strict_mode = val
end