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

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

Parameters:

  • the (Jinx::Resource)

    domain object to create

Returns:

  • (Jinx::Resource)

    obj

Raises:



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.

Parameters:

  • obj (Jinx::Resource)

    the domain object to delete

Raises:



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.

Parameters:

  • obj (Jinx::Resource, <Jinx::Resource>)

    the domain object or collection of domain objects to find or create

Raises:

  • (ArgumentError)

    if obj is nil or empty

  • (DatabaseError)

    if obj could not be created



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

#initializeObject

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.

Parameters:

  • obj (Jinx::Resource)

    the domain object to save

Returns:

  • (Jinx::Resource)

    obj

Raises:

See Also:



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

Parameters:

  • the (Jinx::Resource)

    object to update

Raises:



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