Module: ASModel::CRUD
- Included in:
- RightsRestriction
- Defined in:
- backend/app/model/ASModel_crud.rb
Overview
Code for converting JSONModels into DB records and back again.
Defined Under Namespace
Modules: ClassMethods
Class Method Summary (collapse)
Instance Method Summary (collapse)
-
- (Object) apply_nested_records(json, new_record = false)
Several JSONModels consist of logical subrecords that are stored as separate models in the database (in separate tables).
-
- (Object) delete
Delete the current record using Sequel’s delete method, but clean up dependencies first.
-
- (Object) eagerly_load!
Do whatever is necessary to eaglerly load this object from the database.
-
- (Object) map_validation_to_json_property(columns, property)
When reporting a Sequel validation error against the set of ‘columns’, report it against the JSONModel ‘property’ instead.
-
- (Object) mark_as_system_modified
-
- (Object) remove_nested_records
-
- (Boolean) system_modified?
that their local copy of the record includes the system-generated data too.
-
- (Object) update_from_json(json, extra_values = {}, apply_nested_records = true)
-
- (Object) validate
Class Method Details
+ (Object) included(base)
8 9 10 11 12 |
# File 'backend/app/model/ASModel_crud.rb', line 8 def self.included(base) base.extend(ClassMethods) base.include(JSONModel) base.extend(JSONModel) end |
+ (Object) set_audit_fields(json, obj)
17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'backend/app/model/ASModel_crud.rb', line 17 def self.set_audit_fields(json, obj) ['created_by', 'last_modified_by'].each do |field| json[field] = obj[field.intern] if obj[field.intern] end ['system_mtime', 'user_mtime', 'create_time'].each do |field| val = obj[field.intern] next if !val json[field] = val.getutc.iso8601 end end |
Instance Method Details
- (Object) apply_nested_records(json, new_record = false)
Several JSONModels consist of logical subrecords that are stored as separate models in the database (in separate tables).
When we get a JSON blob for a record with subrecords, we want to create a database record for each subrecords (or, if a URI referencing an existing subrecord was given, use the existing object), then associate those subrecords with the main record.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'backend/app/model/ASModel_crud.rb', line 60 def apply_nested_records(json, new_record = false) self.remove_nested_records if !new_record self.class.nested_records.each do |nested_record| # Read the subrecords from our JSON blob and fetch or create # the corresponding subrecord from the database. model = Kernel.const_get(nested_record[:association][:class_name]) if nested_record[:association][:type] === :one_to_one add_record_method = nested_record[:association][:name].to_s elsif nested_record[:association][:type] === :many_to_one add_record_method = "#{nested_record[:association][:name].to_s.singularize}=" else add_record_method = "add_#{nested_record[:association][:name].to_s.singularize}" end records = json[nested_record[:json_property]] is_array = true if nested_record[:association][:type] === :one_to_one || nested_record[:is_array] === false is_array = false records = [records] end updated_records = [] (records or []).each_with_index do |json_or_uri, i| next if json_or_uri.nil? db_record = nil begin needs_linking = true if json_or_uri.kind_of? String # A URI. Just grab its database ID and look it up. db_record = model[JSONModel(nested_record[:jsonmodel]).id_for(json_or_uri)] updated_records << json_or_uri else # Create a database record for the JSON blob and return its ID subrecord_json = JSONModel(nested_record[:jsonmodel]).from_hash(json_or_uri, true, true) # The value of subrecord_json can be mutated by the various # transformations performed by the model layer. Make sure we # keep the modified version of the JSON here. updated_records << subrecord_json if model.respond_to? :ensure_exists # Give our classes an opportunity to provide their own logic here db_record = model.ensure_exists(subrecord_json, self) else extra_opts = {} if nested_record[:association][:key] extra_opts[nested_record[:association][:key]] = self.id # We'll skip the call to the .add method because this step # will have already linked the nested record to this one. needs_linking = false end db_record = model.create_from_json(subrecord_json, extra_opts) end end if db_record.system_modified? # If the subrecord got changed by the system, mark ourselves as # modified too. self.mark_as_system_modified end self.send(add_record_method, db_record) if (db_record && needs_linking) rescue Sequel::ValidationFailed => e # Modify the exception keys by prefixing each with the path up until this point. e.instance_eval do if @errors prefix = nested_record[:json_property] prefix = "#{prefix}/#{i}" if is_array new_errors = {} @errors.each do |k, v| new_errors["#{prefix}/#{k}"] = v end @errors = new_errors end end raise e end end json[nested_record[:json_property]] = is_array ? updated_records : updated_records[0] end end |
- (Object) delete
Delete the current record using Sequel’s delete method, but clean up dependencies first.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'backend/app/model/ASModel_crud.rb', line 236 def delete object_graph = self.object_graph deleted_uris = [] successfully_deleted_models = [] last_error = nil while true progressed = false object_graph.each do |model, ids_to_delete| next if successfully_deleted_models.include?(model) begin model.handle_delete(ids_to_delete) successfully_deleted_models << model progressed = true rescue Sequel::DatabaseError last_error = $! next end if model.my_jsonmodel(true) ids_to_delete.each do |id| deleted_uri = model.my_jsonmodel(true). uri_for(id, :repo_id => model.active_repository) if deleted_uri deleted_uris << deleted_uri end end end end break if object_graph.models.length == successfully_deleted_models.length unless progressed if last_error && DB.is_retriable_exception(last_error) # Give us a chance to retry after a deadlock raise last_error end raise ConflictException.new("Record deletion failed: #{last_error}") end end deleted_uris.each do |uri| Tombstone.create(:uri => uri) DB.after_commit do RealtimeIndexing.record_delete(uri) end end end |
- (Object) eagerly_load!
Do whatever is necessary to eaglerly load this object from the database.
This is designed to give mixins the options of eagerly loading an entire record and its components.
48 49 50 |
# File 'backend/app/model/ASModel_crud.rb', line 48 def eagerly_load! # Do nothing by default end |
- (Object) map_validation_to_json_property(columns, property)
When reporting a Sequel validation error against the set of ‘columns’, report it against the JSONModel ‘property’ instead.
For example, an identifier that must be unique to a repository might have a constraint against the columns [:repository, :identifier], but when we report this to the client we just want to tell them that the value for ‘identifier’ was incorrect.
299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'backend/app/model/ASModel_crud.rb', line 299 def map_validation_to_json_property(columns, property) errors = self.errors.clone self.errors.clear errors.each do |error, msg| if error == columns self.errors[property] = msg else self.errors[error] = msg end end end |
- (Object) mark_as_system_modified
326 327 328 |
# File 'backend/app/model/ASModel_crud.rb', line 326 def mark_as_system_modified @system_modified = true end |
- (Object) remove_nested_records
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'backend/app/model/ASModel_crud.rb', line 157 def remove_nested_records self.class.nested_records.each do |nested_record_defn| if [:one_to_one, :one_to_many].include?(nested_record_defn[:association][:type]) # If the current record "owns" its nested record, delete the nested record. model = Kernel.const_get(nested_record_defn[:association][:class_name]) # Tell the nested record to clear its own nested records Array(self.send(nested_record_defn[:association][:name])).each do |nested_record| # not stoked on this ,but some one_to_one's (collection_management # ) get indexed with an id that refs the parent. so we need to # tombstone the uri that points back to the parent uri if nested_record_defn[:association][:type] == :one_to_one context_uri = "#{self.uri}##{self.class.to_s.downcase}_#{nested_record.class.to_s.underscore}" Tombstone.create(:uri => context_uri) end nested_record.delete end elsif nested_record_defn[:association][:type] === :many_to_many # Just remove the links self.send("remove_all_#{nested_record_defn[:association][:name]}".intern) elsif nested_record_defn[:association][:type] === :many_to_one # Just remove the link self.send("#{nested_record_defn[:association][:name].intern}=", nil) end end end |
- (Boolean) system_modified?
that their local copy of the record includes the system-generated data too.
321 322 323 |
# File 'backend/app/model/ASModel_crud.rb', line 321 def system_modified? @system_modified end |
- (Object) update_from_json(json, extra_values = {}, apply_nested_records = true)
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'backend/app/model/ASModel_crud.rb', line 188 def update_from_json(json, extra_values = {}, apply_nested_records = true) if self.values.has_key?(:suppressed) if self[:suppressed] == 1 raise ReadOnlyException.new("Can't update an object that has been suppressed") end # No funny business. If you want to set this you need to do it via the # dedicated controller. json["suppressed"] = false end schema_defined_properties = json.class.schema["properties"].map{|prop, defn| prop if !defn['readonly'] }.compact # Start by assuming all existing properties were nil, then overlay the # updates plus any extra attributes. # # This has the effect of unsetting (or setting to NULL) any properties that # were removed by this update. updated = Hash[schema_defined_properties.map {|property| [property, nil]}]. merge(json.to_hash). merge(ASUtils.keys_as_strings(extra_values)) if updated.has_key?('lock_version') && !updated['lock_version'] raise ConflictException.new("You must provide a lock_version in your request") end self.class.strict_param_setting = false self.update(self.class.prepare_for_db(json.class, updated). merge(:user_mtime => Time.now, :last_modified_by => RequestContext.get(:current_username))) if apply_nested_records self.apply_nested_records(json) end self.class.fire_update(json, self) self end |
- (Object) validate
31 32 33 34 35 36 37 38 39 40 41 |
# File 'backend/app/model/ASModel_crud.rb', line 31 def validate # Check uniqueness constraints self.class.repo_unique_constraints.each do |constraint| validates_unique([:repo_id, constraint[:property]], :message => constraint[:message]) map_validation_to_json_property([:repo_id, constraint[:property]], constraint[:json_property]) end super end |