Module: CaRuby::Database::Writer
- Included in:
- CaRuby::Database
- Defined in:
- lib/caruby/database/writer.rb,
lib/caruby/database/writer_template_builder.rb
Overview
Database store operation mixin.
Defined Under Namespace
Classes: TemplateBuilder
Instance Method Summary collapse
-
#create(obj) ⇒ Jinx::Resource
Creates the specified domain object obj and returns obj.
-
#delete(obj) ⇒ Object
Deletes the specified domain object obj.
-
#ensure_exists(obj) ⇒ Object
Creates the domain object obj, if necessary.
-
#initialize ⇒ Object
Adds store capability to this Database.
-
#save(obj) ⇒ Jinx::Resource
(also: #store)
Updates the specified domain object if it exists, otherwise creates a new domain object.
-
#update(obj) ⇒ Object
Updates the specified domain object obj.
Instance Method Details
#create(obj) ⇒ Jinx::Resource
Creates the specified domain object obj and returns obj. The pre-condition for this method is as follows:
-
obj is a well-formed domain object with the necessary required attributes as determined by the Jinx::Resource#validate method
-
obj does not have a database identifier attribute value
-
obj does not yet exist in the database
The post-condition is that obj is created and assigned a database identifier.
An object referenced by obj is created or updated if and only if the change operation on the referenced object is necesssary to create obj. This behavior differs from the standard caCORE
behavior in that the client does not need to embed implicit prescriptive application rules in the sequence of database operation calls. The goal is to reflect the caRuby client declarative intent:
-
caRuby will do whatever is necessary to reflect the object state to the database.
-
No other changes will be made to the database.
By definition, a dependent object is not created directly. If obj is dependent and references its owner, then this method delegates to the owner by calling #store on the owner. The owner store operation will create or update the owner using #store and create the dependent obj in the process.
Note: the dependent identifier is not set if the owner dependent attribute is a collection and the dependent class does not have secondary key attributes. In that case, there is no reliable caCORE
query mechanism to match the obj dependent to a created or fetched dependent. The owner dependent collection content is replaced by new dependent objects fetched from the database, e.g. given a dependent
in the owner
dependents
collection, then:
dependents_count = owner.dependents.size
owner.dependents.include?(dependent) #=> true
database.create(dependent).identifier #=> nil
owner.dependents.include?(dependent) #=> false
owner.dependents.size == dependents_count #=> true
If obj is not dependent, then the create strategy is as follows:
-
add default attribute values using Jinx::Resource#add_defaults
-
validate obj using Jinx::Resource#validate
-
ensure that all saved independent reference attribute values exist in the database, creating them if necessary
-
ensure that all dependent reference attribute values can be created according to this set of rules
-
make a template for obj and its references that will result in saving obj saved attributes to the database
-
submit the template to the application service creation method.
-
copy the new database identifier to each created object, i.e. the transitive closure of obj and its dependents
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/caruby/database/writer.rb', line 74 def create(obj) # guard against recursive call back into the same operation # the only allowed recursive call is a dependent create which first creates the owner if recursive_save?(obj, :create) then raise DatabaseError.new("Create #{obj.qp} recursively called in context #{print_operations}") elsif obj.identifier then raise DatabaseError.new("Create unsuccessful since #{obj.qp} already has identifier #{obj.identifier}") end # create the object perform(:create, obj) { create_object(obj) } end |
#delete(obj) ⇒ Object
Deletes the specified domain object obj.
Note that some applications restrict or forbid delete operations. Check the specific application documentation to determine whether deletion is supported.
133 134 135 |
# File 'lib/caruby/database/writer.rb', line 133 def delete(obj) perform(:delete, obj) { delete_object(obj) } end |
#ensure_exists(obj) ⇒ Object
Creates the domain object obj, if necessary.
142 143 144 145 146 |
# File 'lib/caruby/database/writer.rb', line 142 def ensure_exists(obj) raise ArgumentError.new("Database ensure_exists is missing a domain object argument") if obj.nil_or_empty? obj.enumerate { |ref| find(ref, :create) unless ref.identifier } end |
#initialize ⇒ Object
Adds store capability to this Database.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/caruby/database/writer.rb', line 14 def initialize super @ftchd_vstr = Jinx::ReferenceVisitor.new { |tgt| tgt.class.fetched_domain_attributes } @cr_tmpl_bldr = TemplateBuilder.new(self) { |ref| creatable_domain_attributes(ref) } @upd_tmpl_bldr = TemplateBuilder.new(self) { |ref| updatable_domain_attributes(ref) } # the save result => argument reference matcher svd_mtchr = SavedMatcher.new # the save (result, argument) synchronization visitor @svd_sync_vstr = Jinx::MatchVisitor.new(:matcher => svd_mtchr) { |ref| ref.class.dependent_attributes } # the attributes to merge from the save result mgbl = Proc.new { |ref| ref.class.domain_attributes } # the save result => argument merge visitor @svd_mrg_vstr = Jinx::MergeVisitor.new(:matcher => svd_mtchr, :mergeable => mgbl) { |ref| ref.class.dependent_attributes } end |
#save(obj) ⇒ Jinx::Resource Also known as: store
Updates the specified domain object if it exists, otherwise creates a new domain object.
The database is queried based on the object attributes. If a match is found, then the obj database identifier attribute is set and update is called. If no matching database record is found, then the object is persisted using create.
118 119 120 121 122 |
# File 'lib/caruby/database/writer.rb', line 118 def save(obj) logger.debug { "Saving #{obj}..." } # if obj exists then update it, otherwise create it exists?(obj) ? update(obj) : create(obj) end |
#update(obj) ⇒ Object
Updates the specified domain object obj. The pre-condition for this method is that obj exists in the database and has a database identifier attribute value. The post-condition is that the database is changed to reflect the obj state. Each dependent object referenced by obj is also created or updated. No other object referenced by obj is changed.
The update strategy is the same as the #create strategy with the following exceptions:
-
validate that obj has a database identifier
-
the template is submitted to the application service update method
97 98 99 100 101 102 103 104 |
# File 'lib/caruby/database/writer.rb', line 97 def update(obj) # guard against a recursive call back into the same operation. if recursive_save?(obj, :update) then raise DatabaseError.new("Update #{obj.qp} recursively called in context #{print_operations}") end # update the object perform(:update, obj) { update_object(obj) } end |