Class: JSONModelType

Inherits:
Object
  • Object
show all
Defined in:
common/jsonmodel_type.rb

Overview

A common base class for all JSONModel classes

Constant Summary collapse

ID_REGEXP =

The inverse of uri_for:

JSONModel(:archival_object).id_for("/repositories/123/archival_objects/500", :repo_id => 123)

might yield 500

IDs are either positive integers, or importer-provided logical IDs

/([0-9]+|import_[a-f0-9-]+)/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}, trusted = false) ⇒ JSONModelType

Returns a new instance of JSONModelType.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'common/jsonmodel_type.rb', line 184

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

Instance Attribute Details

#uriObject

Returns the value of attribute uri



201
202
203
# File 'common/jsonmodel_type.rb', line 201

def uri
  @uri
end

Class Method Details

.add_validation(name, level = :error, &block) ⇒ Object

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”], …]



62
63
64
65
66
67
68
69
# File 'common/jsonmodel_type.rb', line 62

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) ⇒ Object

Create an instance of this JSONModel from the data contained in ‘hash’.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'common/jsonmodel_type.rb', line 73

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) ⇒ Object

Create an instance of this JSONModel from a JSON string.



96
97
98
# File 'common/jsonmodel_type.rb', line 96

def self.from_json(s, raise_errors = true)
  self.from_hash(ASUtils.json_parse(s), raise_errors)
end

.id_for(uri, opts = {}, noerror = false) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'common/jsonmodel_type.rb', line 141

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_]+(\/|$)/, '/[^/ ]+\1')

  if uri =~ /#{pattern}\/#{ID_REGEXP}(\#.*)?$/
    return id_to_int($1)
  elsif uri =~ /#{pattern.gsub(/\[\^\/ \]\+\/tree/, '')}#{ID_REGEXP}\/(tree|ordered_records)$/
    # FIXME: gross hardcoding...
    return id_to_int($1)
  else
    if noerror
      nil
    else
      raise "Couldn't make an ID out of URI: #{uri}"
    end
  end
end

.inherited(child) ⇒ Object

If a JSONModel is extended, make its schema and record type class variables available on the subclass too.



28
29
30
31
# File 'common/jsonmodel_type.rb', line 28

def self.inherited(child)
  child.instance_variable_set(:@schema, schema)
  child.instance_variable_set(:@record_type, record_type)
end

.init(type, schema, mixins = []) ⇒ Object

Class instance variables store the bits specific to this model



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'common/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

.record_typeObject

Return the type of this JSONModel class (a keyword like :archival_object)



48
49
50
# File 'common/jsonmodel_type.rb', line 48

def self.record_type
  @record_type
end

.schemaObject

Return the JSON schema that defines this JSONModel class



35
36
37
# File 'common/jsonmodel_type.rb', line 35

def self.schema
  @schema
end

.schema_versionObject

Return the version number of this JSONModel’s schema



41
42
43
# File 'common/jsonmodel_type.rb', line 41

def self.schema_version
  self.schema['version']
end

.to_sObject



53
54
55
# File 'common/jsonmodel_type.rb', line 53

def self.to_s
  "JSONModel(:#{self.record_type})"
end

.type_of(path) ⇒ Object

Return the type of the schema property defined by ‘path’

For example, type_of(“names/items/type”) might return a JSONModel class



171
172
173
174
175
176
177
178
179
180
181
# File 'common/jsonmodel_type.rb', line 171

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 = {}) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'common/jsonmodel_type.rb', line 101

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 += "/#{self.escape_uri_component(id.to_s)}"
  end

  self.substitute_parameters(uri, opts)
end

.uri_for(id = nil, opts = {}) ⇒ Object

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”



125
126
127
128
129
# File 'common/jsonmodel_type.rb', line 125

def self.uri_for(id = nil, opts = {})
  result = self.uri_and_remaining_options_for(id, opts)

  result ? result[0] : nil
end

Instance Method Details

#[](key) ⇒ Object



213
214
215
# File 'common/jsonmodel_type.rb', line 213

def [](key)
  @data[key.to_s]
end

#[]=(key, val) ⇒ Object



218
219
220
221
# File 'common/jsonmodel_type.rb', line 218

def []=(key, val)
  @validated = false
  @data[key.to_s] = val
end

#_always_valid!Object

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.



278
279
280
281
# File 'common/jsonmodel_type.rb', line 278

def _always_valid!
  @always_valid = true
  self
end

#_exceptionsObject

Validate the current JSONModel instance and return a list of exceptions produced.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'common/jsonmodel_type.rb', line 232

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

#_warningsObject



264
265
266
267
268
269
270
271
272
# File 'common/jsonmodel_type.rb', line 264

def _warnings
  exceptions = self._exceptions

  if exceptions.has_key?(:warnings)
    exceptions[:warnings]
  else
    []
  end
end

#add_error(attribute, message) ⇒ Object



255
256
257
258
259
260
261
# File 'common/jsonmodel_type.rb', line 255

def add_error(attribute, message)
  # reset validation
  @validated = false

  # call JSONModel::Client's version
  super
end

#clear_errorsObject



248
249
250
251
252
# File 'common/jsonmodel_type.rb', line 248

def clear_errors
  # reset validation
  @validated = false
  @errors = nil
end

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


224
225
226
# File 'common/jsonmodel_type.rb', line 224

def has_key?(key)
  @data.has_key?(key)
end

#idObject

Return the internal ID of this JSONModel.



355
356
357
358
359
360
361
362
363
# File 'common/jsonmodel_type.rb', line 355

def id
  ref = JSONModel::parse_reference(self.uri)

  if ref
    ref[:id]
  else
    nil
  end
end

#inspectObject



316
317
318
# File 'common/jsonmodel_type.rb', line 316

def inspect
  self.to_s
end

#instance_dataObject



208
209
210
# File 'common/jsonmodel_type.rb', line 208

def instance_data
  @instance_data
end

#replace(params) ⇒ Object

Replace the values of the current JSONModel instance with the contents of ‘params’, validating before accepting the replacement.



300
301
302
303
# File 'common/jsonmodel_type.rb', line 300

def replace(params)
  @validated = false
  set_data(params)
end

#reset_from(another_jsonmodel) ⇒ Object



306
307
308
# File 'common/jsonmodel_type.rb', line 306

def reset_from(another_jsonmodel)
  @data = another_jsonmodel.instance_eval { @data }
end

#to_hash(mode = nil) ⇒ Object

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.



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'common/jsonmodel_type.rb', line 324

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 = {}) ⇒ Object

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.



349
350
351
# File 'common/jsonmodel_type.rb', line 349

def to_json(opts = {})
  ASUtils.to_json(self.to_hash(opts[:mode]), opts.is_a?(Hash) ? opts.merge(:max_nesting => false) : {})
end

#to_sObject



311
312
313
# File 'common/jsonmodel_type.rb', line 311

def to_s
  "#<JSONModel(:#{self.class.record_type}) #{@data.inspect}>"
end

#update(params) ⇒ Object

Update the values of the current JSONModel instance with the contents of ‘params’, validating before accepting the update.



286
287
288
289
# File 'common/jsonmodel_type.rb', line 286

def update(params)
  @validated = false
  replace(ASUtils.deep_merge(@data, params))
end

#update_concat(params) ⇒ Object

Update the values of the current JSONModel instance with the contents of ‘params’, validating before accepting the update.



293
294
295
296
# File 'common/jsonmodel_type.rb', line 293

def update_concat(params)
  @validated = false
  replace(ASUtils.deep_merge_concat(@data, params))
end