aspace_client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/aspace_client.rb +15 -0
- data/lib/aspace_client/archivesspace_json_schema.rb +210 -0
- data/lib/aspace_client/asutils.rb +142 -0
- data/lib/aspace_client/client_enum_source.rb +30 -0
- data/lib/aspace_client/exceptions.rb +15 -0
- data/lib/aspace_client/helpers.rb +0 -0
- data/lib/aspace_client/json_schema_concurrency_fix.rb +52 -0
- data/lib/aspace_client/json_schema_utils.rb +414 -0
- data/lib/aspace_client/jsonmodel.rb +342 -0
- data/lib/aspace_client/jsonmodel_client.rb +528 -0
- data/lib/aspace_client/jsonmodel_i18n_mixin.rb +77 -0
- data/lib/aspace_client/jsonmodel_type.rb +478 -0
- data/lib/aspace_client/memoryleak.rb +59 -0
- data/lib/aspace_client/schemas/abstract_agent.rb +51 -0
- data/lib/aspace_client/schemas/abstract_agent_relationship.rb +12 -0
- data/lib/aspace_client/schemas/abstract_archival_object.rb +96 -0
- data/lib/aspace_client/schemas/abstract_classification.rb +44 -0
- data/lib/aspace_client/schemas/abstract_name.rb +23 -0
- data/lib/aspace_client/schemas/abstract_note.rb +13 -0
- data/lib/aspace_client/schemas/accession.rb +174 -0
- data/lib/aspace_client/schemas/accession_parts_relationship.rb +31 -0
- data/lib/aspace_client/schemas/accession_sibling_relationship.rb +31 -0
- data/lib/aspace_client/schemas/active_edits.rb +23 -0
- data/lib/aspace_client/schemas/advanced_query.rb +12 -0
- data/lib/aspace_client/schemas/agent_contact.rb +25 -0
- data/lib/aspace_client/schemas/agent_corporate_entity.rb +32 -0
- data/lib/aspace_client/schemas/agent_family.rb +29 -0
- data/lib/aspace_client/schemas/agent_person.rb +31 -0
- data/lib/aspace_client/schemas/agent_relationship_associative.rb +28 -0
- data/lib/aspace_client/schemas/agent_relationship_earlierlater.rb +28 -0
- data/lib/aspace_client/schemas/agent_relationship_parentchild.rb +26 -0
- data/lib/aspace_client/schemas/agent_relationship_subordinatesuperior.rb +26 -0
- data/lib/aspace_client/schemas/agent_software.rb +22 -0
- data/lib/aspace_client/schemas/archival_object.rb +60 -0
- data/lib/aspace_client/schemas/archival_record_children.rb +15 -0
- data/lib/aspace_client/schemas/boolean_field_query.rb +13 -0
- data/lib/aspace_client/schemas/boolean_query.rb +13 -0
- data/lib/aspace_client/schemas/classification.rb +10 -0
- data/lib/aspace_client/schemas/classification_term.rb +38 -0
- data/lib/aspace_client/schemas/classification_tree.rb +17 -0
- data/lib/aspace_client/schemas/collection_management.rb +27 -0
- data/lib/aspace_client/schemas/container.rb +29 -0
- data/lib/aspace_client/schemas/container_location.rb +19 -0
- data/lib/aspace_client/schemas/date.rb +19 -0
- data/lib/aspace_client/schemas/date_field_query.rb +14 -0
- data/lib/aspace_client/schemas/deaccession.rb +20 -0
- data/lib/aspace_client/schemas/defaults.rb +104 -0
- data/lib/aspace_client/schemas/digital_object.rb +64 -0
- data/lib/aspace_client/schemas/digital_object_component.rb +53 -0
- data/lib/aspace_client/schemas/digital_object_tree.rb +19 -0
- data/lib/aspace_client/schemas/digital_record_children.rb +15 -0
- data/lib/aspace_client/schemas/enumeration.rb +29 -0
- data/lib/aspace_client/schemas/enumeration_migration.rb +14 -0
- data/lib/aspace_client/schemas/event.rb +88 -0
- data/lib/aspace_client/schemas/extent.rb +17 -0
- data/lib/aspace_client/schemas/external_document.rb +12 -0
- data/lib/aspace_client/schemas/external_id.rb +11 -0
- data/lib/aspace_client/schemas/field_query.rb +15 -0
- data/lib/aspace_client/schemas/file_version.rb +26 -0
- data/lib/aspace_client/schemas/group.rb +17 -0
- data/lib/aspace_client/schemas/instance.rb +27 -0
- data/lib/aspace_client/schemas/job.rb +57 -0
- data/lib/aspace_client/schemas/location.rb +36 -0
- data/lib/aspace_client/schemas/location_batch.rb +45 -0
- data/lib/aspace_client/schemas/merge_request.rb +48 -0
- data/lib/aspace_client/schemas/name_corporate_entity.rb +15 -0
- data/lib/aspace_client/schemas/name_family.rb +13 -0
- data/lib/aspace_client/schemas/name_form.rb +15 -0
- data/lib/aspace_client/schemas/name_person.rb +19 -0
- data/lib/aspace_client/schemas/name_software.rb +14 -0
- data/lib/aspace_client/schemas/note_abstract.rb +17 -0
- data/lib/aspace_client/schemas/note_bibliography.rb +29 -0
- data/lib/aspace_client/schemas/note_bioghist.rb +22 -0
- data/lib/aspace_client/schemas/note_chronology.rb +28 -0
- data/lib/aspace_client/schemas/note_citation.rb +32 -0
- data/lib/aspace_client/schemas/note_definedlist.rb +25 -0
- data/lib/aspace_client/schemas/note_digital_object.rb +23 -0
- data/lib/aspace_client/schemas/note_index.rb +29 -0
- data/lib/aspace_client/schemas/note_index_item.rb +25 -0
- data/lib/aspace_client/schemas/note_multipart.rb +25 -0
- data/lib/aspace_client/schemas/note_orderedlist.rb +27 -0
- data/lib/aspace_client/schemas/note_outline.rb +20 -0
- data/lib/aspace_client/schemas/note_outline_level.rb +21 -0
- data/lib/aspace_client/schemas/note_singlepart.rb +24 -0
- data/lib/aspace_client/schemas/note_text.rb +17 -0
- data/lib/aspace_client/schemas/permission.rb +15 -0
- data/lib/aspace_client/schemas/preference.rb +16 -0
- data/lib/aspace_client/schemas/record_tree.rb +17 -0
- data/lib/aspace_client/schemas/repository.rb +32 -0
- data/lib/aspace_client/schemas/repository_with_agent.rb +14 -0
- data/lib/aspace_client/schemas/resource.rb +112 -0
- data/lib/aspace_client/schemas/resource_tree.rb +20 -0
- data/lib/aspace_client/schemas/rights_statement.rb +35 -0
- data/lib/aspace_client/schemas/subject.rb +30 -0
- data/lib/aspace_client/schemas/term.rb +16 -0
- data/lib/aspace_client/schemas/user.rb +56 -0
- data/lib/aspace_client/schemas/user_defined.rb +42 -0
- data/lib/aspace_client/schemas/vocabulary.rb +15 -0
- data/lib/aspace_client/validations.rb +434 -0
- data/lib/aspace_client/validator_cache.rb +47 -0
- data/lib/aspace_client/version.rb +3 -0
- 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
|