aspace_client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aspace_client.rb +15 -0
  3. data/lib/aspace_client/archivesspace_json_schema.rb +210 -0
  4. data/lib/aspace_client/asutils.rb +142 -0
  5. data/lib/aspace_client/client_enum_source.rb +30 -0
  6. data/lib/aspace_client/exceptions.rb +15 -0
  7. data/lib/aspace_client/helpers.rb +0 -0
  8. data/lib/aspace_client/json_schema_concurrency_fix.rb +52 -0
  9. data/lib/aspace_client/json_schema_utils.rb +414 -0
  10. data/lib/aspace_client/jsonmodel.rb +342 -0
  11. data/lib/aspace_client/jsonmodel_client.rb +528 -0
  12. data/lib/aspace_client/jsonmodel_i18n_mixin.rb +77 -0
  13. data/lib/aspace_client/jsonmodel_type.rb +478 -0
  14. data/lib/aspace_client/memoryleak.rb +59 -0
  15. data/lib/aspace_client/schemas/abstract_agent.rb +51 -0
  16. data/lib/aspace_client/schemas/abstract_agent_relationship.rb +12 -0
  17. data/lib/aspace_client/schemas/abstract_archival_object.rb +96 -0
  18. data/lib/aspace_client/schemas/abstract_classification.rb +44 -0
  19. data/lib/aspace_client/schemas/abstract_name.rb +23 -0
  20. data/lib/aspace_client/schemas/abstract_note.rb +13 -0
  21. data/lib/aspace_client/schemas/accession.rb +174 -0
  22. data/lib/aspace_client/schemas/accession_parts_relationship.rb +31 -0
  23. data/lib/aspace_client/schemas/accession_sibling_relationship.rb +31 -0
  24. data/lib/aspace_client/schemas/active_edits.rb +23 -0
  25. data/lib/aspace_client/schemas/advanced_query.rb +12 -0
  26. data/lib/aspace_client/schemas/agent_contact.rb +25 -0
  27. data/lib/aspace_client/schemas/agent_corporate_entity.rb +32 -0
  28. data/lib/aspace_client/schemas/agent_family.rb +29 -0
  29. data/lib/aspace_client/schemas/agent_person.rb +31 -0
  30. data/lib/aspace_client/schemas/agent_relationship_associative.rb +28 -0
  31. data/lib/aspace_client/schemas/agent_relationship_earlierlater.rb +28 -0
  32. data/lib/aspace_client/schemas/agent_relationship_parentchild.rb +26 -0
  33. data/lib/aspace_client/schemas/agent_relationship_subordinatesuperior.rb +26 -0
  34. data/lib/aspace_client/schemas/agent_software.rb +22 -0
  35. data/lib/aspace_client/schemas/archival_object.rb +60 -0
  36. data/lib/aspace_client/schemas/archival_record_children.rb +15 -0
  37. data/lib/aspace_client/schemas/boolean_field_query.rb +13 -0
  38. data/lib/aspace_client/schemas/boolean_query.rb +13 -0
  39. data/lib/aspace_client/schemas/classification.rb +10 -0
  40. data/lib/aspace_client/schemas/classification_term.rb +38 -0
  41. data/lib/aspace_client/schemas/classification_tree.rb +17 -0
  42. data/lib/aspace_client/schemas/collection_management.rb +27 -0
  43. data/lib/aspace_client/schemas/container.rb +29 -0
  44. data/lib/aspace_client/schemas/container_location.rb +19 -0
  45. data/lib/aspace_client/schemas/date.rb +19 -0
  46. data/lib/aspace_client/schemas/date_field_query.rb +14 -0
  47. data/lib/aspace_client/schemas/deaccession.rb +20 -0
  48. data/lib/aspace_client/schemas/defaults.rb +104 -0
  49. data/lib/aspace_client/schemas/digital_object.rb +64 -0
  50. data/lib/aspace_client/schemas/digital_object_component.rb +53 -0
  51. data/lib/aspace_client/schemas/digital_object_tree.rb +19 -0
  52. data/lib/aspace_client/schemas/digital_record_children.rb +15 -0
  53. data/lib/aspace_client/schemas/enumeration.rb +29 -0
  54. data/lib/aspace_client/schemas/enumeration_migration.rb +14 -0
  55. data/lib/aspace_client/schemas/event.rb +88 -0
  56. data/lib/aspace_client/schemas/extent.rb +17 -0
  57. data/lib/aspace_client/schemas/external_document.rb +12 -0
  58. data/lib/aspace_client/schemas/external_id.rb +11 -0
  59. data/lib/aspace_client/schemas/field_query.rb +15 -0
  60. data/lib/aspace_client/schemas/file_version.rb +26 -0
  61. data/lib/aspace_client/schemas/group.rb +17 -0
  62. data/lib/aspace_client/schemas/instance.rb +27 -0
  63. data/lib/aspace_client/schemas/job.rb +57 -0
  64. data/lib/aspace_client/schemas/location.rb +36 -0
  65. data/lib/aspace_client/schemas/location_batch.rb +45 -0
  66. data/lib/aspace_client/schemas/merge_request.rb +48 -0
  67. data/lib/aspace_client/schemas/name_corporate_entity.rb +15 -0
  68. data/lib/aspace_client/schemas/name_family.rb +13 -0
  69. data/lib/aspace_client/schemas/name_form.rb +15 -0
  70. data/lib/aspace_client/schemas/name_person.rb +19 -0
  71. data/lib/aspace_client/schemas/name_software.rb +14 -0
  72. data/lib/aspace_client/schemas/note_abstract.rb +17 -0
  73. data/lib/aspace_client/schemas/note_bibliography.rb +29 -0
  74. data/lib/aspace_client/schemas/note_bioghist.rb +22 -0
  75. data/lib/aspace_client/schemas/note_chronology.rb +28 -0
  76. data/lib/aspace_client/schemas/note_citation.rb +32 -0
  77. data/lib/aspace_client/schemas/note_definedlist.rb +25 -0
  78. data/lib/aspace_client/schemas/note_digital_object.rb +23 -0
  79. data/lib/aspace_client/schemas/note_index.rb +29 -0
  80. data/lib/aspace_client/schemas/note_index_item.rb +25 -0
  81. data/lib/aspace_client/schemas/note_multipart.rb +25 -0
  82. data/lib/aspace_client/schemas/note_orderedlist.rb +27 -0
  83. data/lib/aspace_client/schemas/note_outline.rb +20 -0
  84. data/lib/aspace_client/schemas/note_outline_level.rb +21 -0
  85. data/lib/aspace_client/schemas/note_singlepart.rb +24 -0
  86. data/lib/aspace_client/schemas/note_text.rb +17 -0
  87. data/lib/aspace_client/schemas/permission.rb +15 -0
  88. data/lib/aspace_client/schemas/preference.rb +16 -0
  89. data/lib/aspace_client/schemas/record_tree.rb +17 -0
  90. data/lib/aspace_client/schemas/repository.rb +32 -0
  91. data/lib/aspace_client/schemas/repository_with_agent.rb +14 -0
  92. data/lib/aspace_client/schemas/resource.rb +112 -0
  93. data/lib/aspace_client/schemas/resource_tree.rb +20 -0
  94. data/lib/aspace_client/schemas/rights_statement.rb +35 -0
  95. data/lib/aspace_client/schemas/subject.rb +30 -0
  96. data/lib/aspace_client/schemas/term.rb +16 -0
  97. data/lib/aspace_client/schemas/user.rb +56 -0
  98. data/lib/aspace_client/schemas/user_defined.rb +42 -0
  99. data/lib/aspace_client/schemas/vocabulary.rb +15 -0
  100. data/lib/aspace_client/validations.rb +434 -0
  101. data/lib/aspace_client/validator_cache.rb +47 -0
  102. data/lib/aspace_client/version.rb +3 -0
  103. metadata +244 -0
@@ -0,0 +1,77 @@
1
+
2
+ module JSONModelI18nMixin
3
+
4
+ def t(*args)
5
+ JSONModel::init_args[:i18n_source].t(*args)
6
+ end
7
+
8
+ def _exceptions
9
+ exceptions = super
10
+
11
+ return exceptions unless JSONModel::init_args[:i18n_source]
12
+
13
+ already_translated = exceptions.instance_variable_get(:@translated) || false
14
+
15
+ unless already_translated
16
+ [:errors, :warnings].each do |level|
17
+ next unless exceptions[level]
18
+ exceptions[level].clone.each do |path, msgs|
19
+ exceptions[level][path] = msgs.map{|m| translate_exception_message(m)}
20
+ end
21
+ end
22
+
23
+ exceptions.instance_variable_set(:@translated, true)
24
+ end
25
+
26
+ exceptions
27
+ end
28
+
29
+
30
+ def translate_exception_message(msg)
31
+ msg_data = case msg
32
+ when "Can't be empty"
33
+ [:cant_be_empty]
34
+ when "entered value didn't match password"
35
+ [:password_did_not_match]
36
+ when "Group code must be unique within a repository"
37
+ [:group_code_already_in_use]
38
+ when "Property is required but was missing"
39
+ [:missing_required_property]
40
+ when "Property was missing"
41
+ [:missing_property]
42
+ when "Not a valid date", "not a valid date"
43
+ [:not_a_valid_date]
44
+ when "is required unless a begin or end date is given"
45
+ [:is_required_unless_a_begin_or_end_date_is_given]
46
+ when "is required unless an expression or an end date is given"
47
+ [:is_required_unless_an_expression_or_an_end_date_is_given]
48
+ when "That ID is already in use"
49
+ [:id_already_in_use]
50
+ when /^Username '(.+)' is already in use/
51
+ [:username_already_in_use, {:username => $1}]
52
+ when /^Did not match regular expression: (.+)/
53
+ [:did_not_match_regular_expression, {:regexp => $1}]
54
+ when /^Must be at least ([0-9]+) characters/
55
+ [:too_few_characters, {:min_length => $1}]
56
+ when /^Must be ([0-9]+) characters or fewer/
57
+ [:too_many_characters, {:max_length => $1}]
58
+ when /^At least ([0-9]+) item\(s\) is required/
59
+ [:too_few_items, {:min_items => $1}]
60
+ when /^Invalid value '(.*?)'. Must be one of: (.*)/
61
+ [:invalid_value, {:value => $1, :valid_set => $2}]
62
+ when /^Must be a (.*?) \(you provided a (.*)\)/
63
+ [:wrong_type, {:desired_type => $1, :actual_type => $2}]
64
+ when /^Must be one of: (.*?) \(you provided a (.*)\)/
65
+ [:must_be_one_of, {:allowed_types => $1, :actual_type => $2}]
66
+ when /Username '(.*)' is already in use/
67
+ [:username_already_in_use, {:username => $1}]
68
+ else
69
+ [msg.downcase.gsub(/[\s,':]/, '_')]
70
+ end
71
+
72
+ key, vars = *msg_data
73
+ t("validation_errors.#{key.to_s}", vars)
74
+ end
75
+
76
+ end
77
+
@@ -0,0 +1,478 @@
1
+ # A common base class for all JSONModel classes
2
+ class JSONModelType
3
+
4
+ # Class instance variables store the bits specific to this model
5
+ def self.init(type, schema, mixins = [])
6
+ @record_type = type
7
+ @schema = schema
8
+
9
+ # In client mode, mix in some extra convenience methods for querying the
10
+ # ArchivesSpace backend service via HTTP.
11
+ if JSONModel.client_mode?
12
+ require_relative 'jsonmodel_client'
13
+ include JSONModel::Client
14
+ end
15
+
16
+
17
+ define_accessors(schema['properties'].keys)
18
+
19
+
20
+ mixins.each do |mixin|
21
+ include(mixin)
22
+ end
23
+ end
24
+
25
+
26
+ # Return the JSON schema that defines this JSONModel class
27
+ def self.schema
28
+ find_ancestor_class_instance(:@schema)
29
+ end
30
+
31
+
32
+ # Return the version number of this JSONModel's schema
33
+ def self.schema_version
34
+ self.schema['version']
35
+ end
36
+
37
+
38
+ # Return the type of this JSONModel class (a keyword like
39
+ # :archival_object)
40
+ def self.record_type
41
+ find_ancestor_class_instance(:@record_type)
42
+ end
43
+
44
+
45
+ def self.to_s
46
+ "JSONModel(:#{self.record_type})"
47
+ end
48
+
49
+
50
+ # Add a custom validation to this model type.
51
+ #
52
+ # The validation is a block that takes a hash of properties and returns an array of pairs like:
53
+ # [["propertyname", "the problem with it"], ...]
54
+ def self.add_validation(name, level = :error, &block)
55
+ raise "Validation name already taken: #{name}" if JSONModel.custom_validations[name]
56
+
57
+ JSONModel.custom_validations[name] = block
58
+
59
+ self.schema["validations"] ||= []
60
+ self.schema["validations"] << [level, name]
61
+ end
62
+
63
+
64
+ # Create an instance of this JSONModel from the data contained in 'hash'.
65
+ def self.from_hash(hash, raise_errors = true, trusted = false)
66
+ hash["jsonmodel_type"] = self.record_type.to_s
67
+
68
+ # If we're running in client mode, leave 'readonly' properties in place,
69
+ # since they're intended for use by clients. Otherwise, we drop them.
70
+ drop_system_properties = !JSONModel.client_mode?
71
+
72
+ if trusted
73
+ # We got this data from a trusted source (such as another JSONModel
74
+ # that had already been validated itself). No need to double up
75
+ self.new(hash, true)
76
+ else
77
+ cleaned = JSONSchemaUtils.drop_unknown_properties(hash, self.schema, drop_system_properties)
78
+ cleaned = ASUtils.jsonmodels_to_hashes(cleaned)
79
+
80
+ validate(cleaned, raise_errors)
81
+
82
+ self.new(cleaned)
83
+ end
84
+ end
85
+
86
+
87
+ # Create an instance of this JSONModel from a JSON string.
88
+ def self.from_json(s, raise_errors = true)
89
+ self.from_hash(ASUtils.json_parse(s), raise_errors)
90
+ end
91
+
92
+
93
+ def self.uri_and_remaining_options_for(id = nil, opts = {})
94
+ # Some schemas (like name schemas) don't have a URI because they don't
95
+ # need endpoints. That's fine.
96
+ if not self.schema['uri']
97
+ return nil
98
+ end
99
+
100
+ uri = self.schema['uri']
101
+
102
+ if not id.nil?
103
+ uri += "/#{URI.escape(id.to_s)}"
104
+ end
105
+
106
+ self.substitute_parameters(uri, opts)
107
+ end
108
+
109
+
110
+ # Given a numeric internal ID and additional options produce a pair containing a URI reference.
111
+ # For example:
112
+ #
113
+ # JSONModel(:archival_object).uri_for(500, :repo_id => 123)
114
+ #
115
+ # might yield "/repositories/123/archival_objects/500"
116
+ #
117
+ def self.uri_for(id = nil, opts = {})
118
+ result = self.uri_and_remaining_options_for(id, opts)
119
+
120
+ result ? result[0] : nil
121
+ end
122
+
123
+
124
+ # The inverse of uri_for:
125
+ #
126
+ # JSONModel(:archival_object).id_for("/repositories/123/archival_objects/500", :repo_id => 123)
127
+ #
128
+ # might yield 500
129
+ #
130
+ def self.id_for(uri, opts = {}, noerror = false)
131
+ if not self.schema['uri']
132
+ if noerror
133
+ return nil
134
+ else
135
+ raise "Missing a URI definition for class #{self.class}"
136
+ end
137
+ end
138
+
139
+ pattern = self.schema['uri']
140
+ pattern = pattern.gsub(/\/:[a-zA-Z_]+\//, '/[^/ ]+/')
141
+
142
+ # IDs are either positive integers, or importer-provided logical IDs
143
+ id_regexp = /([0-9]+|import_[a-f0-9-]+)/
144
+
145
+ if uri =~ /#{pattern}\/#{id_regexp}(\#.*)?$/
146
+ return id_to_int($1)
147
+ elsif uri =~ /#{pattern.gsub(/\[\^\/ \]\+\/tree/, '')}#{id_regexp}\/tree$/
148
+ return id_to_int($1)
149
+ else
150
+ if noerror
151
+ nil
152
+ else
153
+ raise "Couldn't make an ID out of URI: #{uri}"
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ # Return the type of the schema property defined by 'path'
160
+ #
161
+ # For example, type_of("names/items/type") might return a JSONModel class
162
+ def self.type_of(path)
163
+ type = JSONSchemaUtils.schema_path_lookup(self.schema, path)["type"]
164
+
165
+ ref = JSONModel.parse_jsonmodel_ref(type)
166
+
167
+ if ref
168
+ JSONModel.JSONModel(ref.first)
169
+ else
170
+ Kernel.const_get(type.capitalize)
171
+ end
172
+ end
173
+
174
+
175
+
176
+ def initialize(params = {}, trusted = false)
177
+ set_data(params)
178
+
179
+ @uri ||= params['uri']
180
+
181
+ # a hash to store transient instance data
182
+ @instance_data = {}
183
+
184
+ self.class.define_accessors(@data.keys)
185
+
186
+ if trusted
187
+ @validated = {}
188
+ @cleaned_data = @data
189
+ end
190
+ end
191
+
192
+
193
+ attr_reader :uri
194
+
195
+ def uri=(val)
196
+ @uri = val
197
+ self['uri'] = val
198
+ end
199
+
200
+ def instance_data
201
+ @instance_data
202
+ end
203
+
204
+
205
+ def [](key)
206
+ @data[key.to_s]
207
+ end
208
+
209
+
210
+ def []=(key, val)
211
+ @validated = false
212
+ @data[key.to_s] = val
213
+ end
214
+
215
+
216
+ def has_key?(key)
217
+ @data.has_key?(key)
218
+ end
219
+
220
+
221
+ # Validate the current JSONModel instance and return a list of exceptions
222
+ # produced.
223
+ def _exceptions
224
+ return @validated if @validated && @errors.nil?
225
+
226
+ exceptions = {}
227
+ if not @always_valid
228
+ exceptions = self.validate(@data, false)
229
+ end
230
+
231
+ if @errors
232
+ exceptions[:errors] = (exceptions[:errors] or {}).merge(@errors)
233
+ end
234
+
235
+ exceptions
236
+ end
237
+
238
+
239
+ def clear_errors
240
+ # reset validation
241
+ @validated = false
242
+ @errors = nil
243
+ end
244
+
245
+
246
+ def add_error(attribute, message)
247
+ # reset validation
248
+ @validated = false
249
+
250
+ # call JSONModel::Client's version
251
+ super
252
+ end
253
+
254
+
255
+ def _warnings
256
+ exceptions = self._exceptions
257
+
258
+ if exceptions.has_key?(:warnings)
259
+ exceptions[:warnings]
260
+ else
261
+ []
262
+ end
263
+ end
264
+
265
+
266
+ # Set this object instance to always pass validation. Used so the
267
+ # frontend can create intentionally incomplete objects that will be filled
268
+ # out by the user.
269
+ def _always_valid!
270
+ @always_valid = true
271
+ self
272
+ end
273
+
274
+
275
+ # Update the values of the current JSONModel instance with the contents of
276
+ # 'params', validating before accepting the update.
277
+ def update(params)
278
+ @validated = false
279
+ replace(ASUtils.deep_merge(@data, params))
280
+ end
281
+
282
+
283
+ # Replace the values of the current JSONModel instance with the contents
284
+ # of 'params', validating before accepting the replacement.
285
+ def replace(params)
286
+ @validated = false
287
+ set_data(params)
288
+ end
289
+
290
+
291
+ def reset_from(another_jsonmodel)
292
+ @data = another_jsonmodel.instance_eval { @data }
293
+ end
294
+
295
+
296
+ def to_s
297
+ "#<JSONModel(:#{self.class.record_type}) #{@data.inspect}>"
298
+ end
299
+
300
+
301
+ def inspect
302
+ self.to_s
303
+ end
304
+
305
+
306
+ # Produce a (possibly nested) hash from the values of this JSONModel. Any
307
+ # values that don't appear in the JSON schema will not appear in the
308
+ # result.
309
+ def to_hash(mode = nil)
310
+ mode = (mode || :validated)
311
+
312
+ raise "Invalid .to_hash mode: #{mode}" unless [:trusted, :validated, :raw].include?(mode)
313
+
314
+ return @data if mode == :raw
315
+
316
+ if @validated and @cleaned_data
317
+ return @cleaned_data
318
+ end
319
+
320
+ cleaned = JSONSchemaUtils.drop_unknown_properties(@data, self.class.schema)
321
+ cleaned = ASUtils.jsonmodels_to_hashes(cleaned)
322
+
323
+ if mode == :validated
324
+ @validated = false
325
+ self.validate(cleaned)
326
+ end
327
+
328
+ @cleaned_data = cleaned
329
+ end
330
+
331
+
332
+ # Produce a JSON string from the values of this JSONModel. Any values
333
+ # that don't appear in the JSON schema will not appear in the result.
334
+ def to_json(opts = {})
335
+ ASUtils.to_json(self.to_hash(opts[:mode]), opts.is_a?(Hash) ? opts.merge(:max_nesting => false) : {})
336
+ end
337
+
338
+
339
+ # Return the internal ID of this JSONModel.
340
+ def id
341
+ ref = JSONModel::parse_reference(self.uri)
342
+
343
+ if ref
344
+ ref[:id]
345
+ else
346
+ nil
347
+ end
348
+ end
349
+
350
+
351
+ protected
352
+
353
+
354
+ def validate(data, raise_errors = true)
355
+ @validated = self.class.validate(data, raise_errors)
356
+ end
357
+
358
+
359
+ # Validate the supplied hash using the JSON schema for this model. Raise
360
+ # a ValidationException if there are any fatal validation problems, or if
361
+ # strict mode is enabled and warnings were produced.
362
+ def self.validate(hash, raise_errors = true)
363
+
364
+ properties = JSONSchemaUtils.drop_unknown_properties(hash, self.schema)
365
+ ValidatorCache.with_validator_for(self, properties) do |validator|
366
+
367
+ messages = validator.validate
368
+ exceptions = JSONSchemaUtils.parse_schema_messages(messages, validator)
369
+
370
+ if raise_errors && (!exceptions[:errors].empty? || (JSONModel.strict_mode? && !exceptions[:warnings].empty?))
371
+ raise JSONModel::ValidationException.new(:invalid_object => self.new(hash),
372
+ :warnings => exceptions[:warnings],
373
+ :errors => exceptions[:errors],
374
+ :attribute_types => exceptions[:attribute_types])
375
+ end
376
+
377
+ exceptions.reject{|k, v| v.empty?}
378
+ end
379
+ end
380
+
381
+
382
+ # Given a URI template like /repositories/:repo_id/something/:somevar, and
383
+ # a hash containing keys and replacement strings, return [uri, opts],
384
+ # where 'uri' is the template with values substituted for their
385
+ # placeholders, and 'opts' is any parameters that weren't consumed.
386
+ #
387
+ def self.substitute_parameters(uri, opts = {})
388
+ matched = []
389
+ opts.each do |k, v|
390
+ old = uri
391
+ uri = uri.gsub(":#{k}", URI.escape(v.to_s))
392
+
393
+ if old != uri
394
+
395
+ if v.is_a? Symbol
396
+ raise ("Tried to substitute the value '#{v.inspect}' for ':#{k}'." +
397
+ " This is usually a sign that something has gone wrong" +
398
+ " further up the stack. (URI was: '#{uri}')")
399
+ end
400
+
401
+ # Matched on this parameter. Remove it from the passed in hash
402
+ matched << k
403
+ end
404
+ end
405
+
406
+ if uri.include?(":")
407
+ raise "Template substitution was incomplete: '#{uri}'"
408
+ end
409
+
410
+ remaining_opts = opts.clone
411
+ matched.each do |k|
412
+ remaining_opts.delete(k)
413
+ end
414
+
415
+ [uri, remaining_opts]
416
+ end
417
+
418
+
419
+ private
420
+
421
+ # If this class is subclassed, we won't be able to see our class instance
422
+ # variables unless we explicitly look up the inheritance chain.
423
+ def self.find_ancestor_class_instance(variable)
424
+ @ancestor_instance_variables ||= Atomic.new({})
425
+
426
+ if !@ancestor_instance_variables.value[variable]
427
+ self.ancestors.each do |clz|
428
+ val = clz.instance_variable_get(variable)
429
+ if val
430
+ @ancestor_instance_variables.update {|vs| vs.merge({variable => val})}
431
+ break
432
+ end
433
+ end
434
+ end
435
+
436
+ @ancestor_instance_variables.value[variable]
437
+ end
438
+
439
+
440
+ # Define accessors for all variable names listed in 'attributes'
441
+ def self.define_accessors(attributes)
442
+ attributes.each do |attribute|
443
+
444
+ if not method_defined? "#{attribute}"
445
+ define_method "#{attribute}" do
446
+ @data[attribute]
447
+ end
448
+ end
449
+
450
+
451
+ if not method_defined? "#{attribute}="
452
+ define_method "#{attribute}=" do |value|
453
+ @validated = false
454
+ @data[attribute] = JSONModel.clean_data(value)
455
+ end
456
+ end
457
+ end
458
+ end
459
+
460
+
461
+ def self.id_to_int(id)
462
+ if id =~ /^import_/
463
+ id
464
+ else
465
+ id.to_i
466
+ end
467
+ end
468
+
469
+
470
+ def set_data(data)
471
+ hash = JSONModel.clean_data(data)
472
+ hash["jsonmodel_type"] = self.class.record_type.to_s
473
+ hash = JSONSchemaUtils.apply_schema_defaults(hash, self.class.schema)
474
+
475
+ @data = hash
476
+ end
477
+
478
+ end