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

#dataObject

Returns the value of attribute data



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

def data
  @data
end

#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 += "/#{URI.escape(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



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

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

#[]=(key, val) ⇒ Object



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

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.



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

def _always_valid!
  @always_valid = true
  self
end

#_exceptionsObject

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



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

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



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

def _warnings
  exceptions = self._exceptions

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

#add_error(attribute, message) ⇒ Object



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

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

  # call JSONModel::Client's version
  super
end

#clear_errorsObject



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

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

#has_key?(key) ⇒ Boolean Also known as: key?

Returns:

  • (Boolean)


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

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

#idObject

Return the internal ID of this JSONModel.



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

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

  if ref
    ref[:id]
  else
    nil
  end
end

#inspectObject



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

def inspect
  self.to_s
end

#instance_dataObject



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

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.



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

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

#reset_from(another_jsonmodel) ⇒ Object



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

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.



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

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.



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

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

#to_sObject



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

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.



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

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.



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

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