caruby-core 2.1.1 → 2.1.2
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.
- data/Gemfile +1 -0
- data/History.md +5 -1
- data/lib/caruby/database/cache.rb +20 -9
- data/lib/caruby/database/lazy_loader.rb +1 -1
- data/lib/caruby/database/operation.rb +2 -0
- data/lib/caruby/database/persistable.rb +19 -13
- data/lib/caruby/database/persistence_service.rb +14 -38
- data/lib/caruby/database/persistifier.rb +86 -37
- data/lib/caruby/database/reader.rb +87 -87
- data/lib/caruby/database/reader_template_builder.rb +7 -4
- data/lib/caruby/database/sql_executor.rb +32 -15
- data/lib/caruby/database/writer.rb +20 -12
- data/lib/caruby/database/writer_template_builder.rb +10 -6
- data/lib/caruby/database.rb +97 -55
- data/lib/caruby/helpers/coordinate.rb +4 -4
- data/lib/caruby/helpers/person.rb +2 -2
- data/lib/caruby/helpers/properties.rb +4 -4
- data/lib/caruby/helpers/roman.rb +2 -2
- data/lib/caruby/helpers/version.rb +1 -1
- data/lib/caruby/json/serializable.rb +17 -0
- data/lib/caruby/metadata/propertied.rb +8 -1
- data/lib/caruby/metadata/property_characteristics.rb +41 -46
- data/lib/caruby/metadata.rb +1 -2
- data/lib/caruby/migration/migrator.rb +108 -0
- data/lib/caruby/resource.rb +2 -2
- data/lib/caruby/version.rb +1 -1
- data/lib/caruby.rb +0 -2
- data/test/lib/caruby/database/cache_test.rb +1 -3
- metadata +6 -8
- data/lib/caruby/caruby-src.tar.gz +0 -0
- data/lib/caruby/json/deserializer.rb +0 -15
- data/lib/caruby/json/serializer.rb +0 -69
data/Gemfile
CHANGED
@@ -2,6 +2,7 @@ source :rubygems
|
|
2
2
|
gemspec
|
3
3
|
|
4
4
|
group :development do
|
5
|
+
# Uncomment to bind local gems in a local branch. Do not check in.
|
5
6
|
#gem 'jinx', :path => File.dirname(__FILE__) + '/../../jinx/core'
|
6
7
|
#gem 'jinx-json', :path => File.dirname(__FILE__) + '/../../jinx/json'
|
7
8
|
#gem 'jinx-migrate', :path => File.dirname(__FILE__) + '/../../jinx/migrate'
|
data/History.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
This history lists major release themes. See the GitHub Commits (https://github.com/caruby/core)
|
2
2
|
for change details.
|
3
3
|
|
4
|
-
2.1.
|
4
|
+
2.1.2 / 2012-06-12
|
5
|
+
------------------
|
6
|
+
* Support caSmall.
|
7
|
+
|
8
|
+
2.1.1 / 2012-04-13
|
5
9
|
------------------
|
6
10
|
* Simpler, more flexible meta-data loading.
|
7
11
|
|
@@ -1,21 +1,32 @@
|
|
1
1
|
require 'jinx/helpers/lazy_hash'
|
2
|
-
require 'jinx/helpers/
|
2
|
+
require 'jinx/helpers/associative'
|
3
3
|
|
4
4
|
module CaRuby
|
5
5
|
# Cache for objects held in memory and accessed by key.
|
6
6
|
class Cache
|
7
|
-
# The classes which are not cleared when {#clear} is called
|
7
|
+
# The classes which are not cleared when {#clear} is called.
|
8
8
|
attr_reader :sticky
|
9
9
|
|
10
|
-
# @yield [
|
11
|
-
# @yieldparam
|
10
|
+
# @yield [obj] returns the key for the given object to cache
|
11
|
+
# @yieldparam obj the object to cache
|
12
12
|
def initialize
|
13
|
-
# Make the class => {key =>
|
14
|
-
# The {key =>
|
15
|
-
#
|
13
|
+
# Make the class => {object => {key => object}} hash.
|
14
|
+
# The {object => {key => object}} hash is an Associative which converts the given
|
15
|
+
# object to its key by calling the block given to this initializer.
|
16
|
+
# The {{key => object} hash takes a key as an argument and returns the cached object.
|
17
|
+
# If there is no cached object, then the object passed to the Associative is cached.
|
16
18
|
@ckh = Jinx::LazyHash.new do
|
17
|
-
|
18
|
-
|
19
|
+
kh = Hash.new
|
20
|
+
# the obj => key associator
|
21
|
+
assoc = Jinx::Associative.new do |obj|
|
22
|
+
key = yield(obj)
|
23
|
+
kh[key] if key
|
24
|
+
end
|
25
|
+
# the obj => key => value writer
|
26
|
+
assoc.writer do |obj, value|
|
27
|
+
key = yield(obj)
|
28
|
+
raise ArgumentError.new("caRuby cannot cache object without a key: #{obj}") if key.nil?
|
29
|
+
kh[key] = value
|
19
30
|
end
|
20
31
|
end
|
21
32
|
@sticky = Set.new
|
@@ -23,7 +23,7 @@ module CaRuby
|
|
23
23
|
# @return the attribute value loaded from the database
|
24
24
|
# @raise [RuntimeError] if this loader is disabled
|
25
25
|
def load(subject, attribute)
|
26
|
-
if disabled? then
|
26
|
+
if disabled? then raise RuntimeError.new("#{subject.qp} lazy load called on disabled loader") end
|
27
27
|
logger.debug { "Lazy-loading #{subject.qp} #{attribute}..." }
|
28
28
|
# the current value
|
29
29
|
oldval = subject.send(attribute)
|
@@ -34,7 +34,7 @@ module CaRuby
|
|
34
34
|
# @return [Database] the data access mediator for this Persistable, if any
|
35
35
|
# @raise [DatabaseError] if the subclass does not override this method
|
36
36
|
def database
|
37
|
-
|
37
|
+
raise ValidationError.new("#{self} database is missing")
|
38
38
|
end
|
39
39
|
|
40
40
|
# @return [PersistenceService] the database application service for this Persistable
|
@@ -113,6 +113,12 @@ module CaRuby
|
|
113
113
|
def delete
|
114
114
|
database.delete(self)
|
115
115
|
end
|
116
|
+
|
117
|
+
# @return [Boolean] whether this domain object can be updated
|
118
|
+
# (default is true, subclasses can override)
|
119
|
+
def updatable?
|
120
|
+
true
|
121
|
+
end
|
116
122
|
|
117
123
|
alias :== :equal?
|
118
124
|
|
@@ -138,7 +144,7 @@ module CaRuby
|
|
138
144
|
# @raise [ValidationError] if this domain object does not have a snapshot
|
139
145
|
def merge_into_snapshot(other)
|
140
146
|
if @snapshot.nil? then
|
141
|
-
|
147
|
+
raise ValidationError.new("Cannot merge #{other.qp} content into #{qp} snapshot, since #{qp} does not have a snapshot.")
|
142
148
|
end
|
143
149
|
# the non-domain attribute => [target value, other value] difference hash
|
144
150
|
delta = diff(other)
|
@@ -164,7 +170,7 @@ module CaRuby
|
|
164
170
|
|
165
171
|
# @return [<Symbol>] the attributes which differ between the {#snapshot} and current content
|
166
172
|
def changed_attributes
|
167
|
-
|
173
|
+
if @snapshot then
|
168
174
|
ovh = value_hash(self.class.updatable_attributes)
|
169
175
|
diff = @snapshot.diff(ovh) { |pa, v, ov| Jinx::Resource.value_equal?(v, ov) }
|
170
176
|
diff.keys
|
@@ -190,7 +196,7 @@ module CaRuby
|
|
190
196
|
# @param loader [LazyLoader] the lazy loader to add
|
191
197
|
def add_lazy_loader(loader, attributes=nil)
|
192
198
|
# guard against invalid call
|
193
|
-
if identifier.nil? then
|
199
|
+
if identifier.nil? then raise ValidationError.new("Cannot add lazy loader to an unfetched domain object: #{self}") end
|
194
200
|
# the attributes to lazy-load
|
195
201
|
attributes ||= loadable_attributes
|
196
202
|
return if attributes.empty?
|
@@ -252,16 +258,16 @@ module CaRuby
|
|
252
258
|
|
253
259
|
# Validates this domain object and its #{Propertied#unproxied_savable_template_attributes}
|
254
260
|
# for consistency and completeness prior to a database create operation.
|
255
|
-
# An object
|
256
|
-
# Objects which have
|
261
|
+
# An object is valid if it contains a non-nil value for each mandatory attribute.
|
262
|
+
# Objects which have already been validated are skipped.
|
257
263
|
#
|
258
|
-
# A Persistable class should not override this method, but override the
|
259
|
-
# method instead.
|
264
|
+
# A Persistable class should not override this method, but override the
|
265
|
+
# private {#validate_local} method instead.
|
260
266
|
#
|
261
267
|
# @return [Persistable] this domain object
|
262
268
|
# @raise [Jinx::ValidationError] if the object state is invalid
|
263
|
-
def validate
|
264
|
-
if identifier.nil? and not @validated then
|
269
|
+
def validate(autogenerated=false)
|
270
|
+
if (identifier.nil? or autogenerated) and not @validated then
|
265
271
|
validate_local
|
266
272
|
@validated = true
|
267
273
|
end
|
@@ -386,7 +392,6 @@ module CaRuby
|
|
386
392
|
def copy_volatile_attributes(other)
|
387
393
|
pas = self.class.volatile_nondomain_attributes
|
388
394
|
return if pas.empty?
|
389
|
-
logger.debug { "Merging volatile attributes #{pas.to_series} from #{other.qp} into #{qp}..." }
|
390
395
|
pas.each do |pa|
|
391
396
|
val = send(pa)
|
392
397
|
oval = other.send(pa)
|
@@ -396,9 +401,10 @@ module CaRuby
|
|
396
401
|
logger.debug { "Set #{qp} volatile #{pa} to the fetched #{other.qp} database value #{oval.qp}." }
|
397
402
|
elsif oval != val and pa == :identifier then
|
398
403
|
# If this error occurs, then there is a serious match-merge flaw.
|
399
|
-
|
404
|
+
raise DatabaseError.new("Can't copy #{other} to #{self} with different identifier")
|
400
405
|
end
|
401
406
|
end
|
407
|
+
logger.debug { "Merged auto-generated attribute values #{pas.to_series} from #{other.qp} into #{self}..." }
|
402
408
|
end
|
403
409
|
|
404
410
|
private
|
@@ -515,7 +521,7 @@ module CaRuby
|
|
515
521
|
end
|
516
522
|
end
|
517
523
|
end
|
518
|
-
|
524
|
+
|
519
525
|
merged
|
520
526
|
end
|
521
527
|
|
@@ -19,11 +19,10 @@ module CaRuby
|
|
19
19
|
# @option opts [String] :host the service host (default +localhost+)
|
20
20
|
# @option opts [String] :version the caTissue version identifier
|
21
21
|
def initialize(name, opts={})
|
22
|
-
CaRuby::PersistenceService.import_java_classes
|
23
22
|
@name = name
|
24
23
|
ver_opt = opts[:version]
|
25
24
|
@version = ver_opt.to_s.to_version if ver_opt
|
26
|
-
@host = opts[:host] ||
|
25
|
+
@host = opts[:host] || 'localhost'
|
27
26
|
@port = opts[:port] || 8080
|
28
27
|
@url = "http://#{@host}:#{@port}/#{@name}/http/remoteService"
|
29
28
|
@timer = Jinx::Stopwatch.new
|
@@ -52,34 +51,19 @@ module CaRuby
|
|
52
51
|
# create method. Calling reference attributes of this result is broken by +caCORE+ design.
|
53
52
|
def create(obj)
|
54
53
|
logger.debug { "Submitting create #{obj.pp_s(:single_line)} to application service #{name}..." }
|
55
|
-
|
56
|
-
dispatch { |svc| svc.create_object(obj) }
|
57
|
-
rescue Exception => e
|
58
|
-
logger.error("Error creating #{obj} - #{e.message}\n#{dump(obj)}")
|
59
|
-
raise e
|
60
|
-
end
|
54
|
+
dispatch { |svc| svc.create_object(obj) }
|
61
55
|
end
|
62
56
|
|
63
57
|
# Submits the update to the application service and returns obj.
|
64
58
|
def update(obj)
|
65
59
|
logger.debug { "Submitting update #{obj.pp_s(:single_line)} to application service #{name}..." }
|
66
|
-
|
67
|
-
dispatch { |svc| svc.update_object(obj) }
|
68
|
-
rescue Exception => e
|
69
|
-
logger.error("Error updating #{obj} - #{e.message}\n#{dump(obj)}")
|
70
|
-
raise e
|
71
|
-
end
|
60
|
+
dispatch { |svc| svc.update_object(obj) }
|
72
61
|
end
|
73
62
|
|
74
63
|
# Submits the delete to the application service.
|
75
64
|
def delete(obj)
|
76
65
|
logger.debug { 'Deleting #{obj}.' }
|
77
|
-
|
78
|
-
dispatch { |svc| svc.remove_object(obj) }
|
79
|
-
rescue Exception => e
|
80
|
-
logger.error("Error deleting #{obj} - #{e.message}\n#{dump(obj)}")
|
81
|
-
raise e
|
82
|
-
end
|
66
|
+
dispatch { |svc| svc.remove_object(obj) }
|
83
67
|
end
|
84
68
|
|
85
69
|
# Returns the {ApplicationService} remote instance.
|
@@ -113,16 +97,6 @@ module CaRuby
|
|
113
97
|
time { yield app_service }
|
114
98
|
end
|
115
99
|
|
116
|
-
# @return [String] the default host name
|
117
|
-
def default_host
|
118
|
-
# # TODO - extract from the service config file
|
119
|
-
# xml = JRuby.runtime.jruby_class_loader.getResourceAsStream('remoteService.xml')
|
120
|
-
# if xml then
|
121
|
-
# # parse xml file
|
122
|
-
# end
|
123
|
-
'localhost'
|
124
|
-
end
|
125
|
-
|
126
100
|
# Dispatches the given HQL to the application service.
|
127
101
|
#
|
128
102
|
# @quirk caCORE query target parameter is necessary for caCORE 3.x but deprecated in caCORE 4+.
|
@@ -132,7 +106,7 @@ module CaRuby
|
|
132
106
|
logger.debug { "Building HQLCriteria..." }
|
133
107
|
criteria = HQLCriteria.new(hql)
|
134
108
|
target = hql[/from\s+(\S+)/i, 1]
|
135
|
-
|
109
|
+
raise DatabaseError.new("HQL does not contain a FROM clause: #{hql}") unless target
|
136
110
|
logger.debug { "Submitting search on target class #{target} with the following HQL:\n #{hql}" }
|
137
111
|
begin
|
138
112
|
dispatch { |svc| svc.query(criteria, target) }
|
@@ -151,7 +125,7 @@ module CaRuby
|
|
151
125
|
class_name_path = []
|
152
126
|
path.inject(template.class) do |type, pa|
|
153
127
|
ref_type = type.domain_type(pa)
|
154
|
-
|
128
|
+
raise DatabaseError.new("Property in search attribute path #{path.join('.')} is not a #{type} domain reference attribute: #{pa}") if ref_type.nil?
|
155
129
|
class_name_path << ref_type.java_class.name
|
156
130
|
ref_type
|
157
131
|
end
|
@@ -180,7 +154,7 @@ module CaRuby
|
|
180
154
|
begin
|
181
155
|
result = dispatch { |svc| svc.association(obj, assn) }
|
182
156
|
rescue Exception => e
|
183
|
-
logger.error("Error fetching association #{obj} - #{e
|
157
|
+
logger.error("Error fetching association #{obj} - #{e}")
|
184
158
|
raise
|
185
159
|
end
|
186
160
|
end
|
@@ -191,11 +165,13 @@ module CaRuby
|
|
191
165
|
|
192
166
|
private
|
193
167
|
|
194
|
-
# Imports
|
195
|
-
def self.
|
196
|
-
|
197
|
-
|
198
|
-
|
168
|
+
# Imports the caCORE +HQLCriteria+ Java class on demand.
|
169
|
+
def self.const_missing(sym)
|
170
|
+
if sym == :HQLCriteria then
|
171
|
+
java_import Java::gov.nih.nci.common.util.HQLCriteria
|
172
|
+
else
|
173
|
+
super
|
174
|
+
end
|
199
175
|
end
|
200
176
|
|
201
177
|
end
|
@@ -13,7 +13,18 @@ module CaRuby
|
|
13
13
|
# Adds query capability to this Database.
|
14
14
|
def initialize
|
15
15
|
super
|
16
|
-
|
16
|
+
# the fetched object cache
|
17
|
+
@cache = create_cache
|
18
|
+
# the general fetched visitor
|
19
|
+
@ftchd_vstr = Jinx::ReferenceVisitor.new() { |ref| ref.class.fetched_domain_attributes }
|
20
|
+
# The persistifier visitor recurses to references before adding a lazy loader to the parent.
|
21
|
+
# The fetched filter foregoes the visit to a previously fetched reference. The visitor
|
22
|
+
# replaces fetched objects with matching cached objects where possible. It is unnecessary
|
23
|
+
# to visit a previously persistified cached object.
|
24
|
+
pst_flt = Proc.new { |ref| @cache[ref].nil? and not ref.fetched? }
|
25
|
+
@pst_vstr = Jinx::ReferenceVisitor.new(:filter => pst_flt, :depth_first => true) do |ref|
|
26
|
+
ref.class.fetched_domain_attributes
|
27
|
+
end
|
17
28
|
# the demand loader
|
18
29
|
@lazy_loader = LazyLoader.new { |obj, pa| lazy_load(obj, pa) }
|
19
30
|
end
|
@@ -73,8 +84,14 @@ module CaRuby
|
|
73
84
|
end
|
74
85
|
|
75
86
|
# This method clears the given toxic domain objects fetched from the database.
|
76
|
-
#
|
77
|
-
#
|
87
|
+
#
|
88
|
+
# Detoxification consists of the following:
|
89
|
+
# * The fetched object's class might not have been previously referenced. In that
|
90
|
+
# case, introspect the fetched object's class.
|
91
|
+
# * Clear toxic references that will result in a caCORE missing session error
|
92
|
+
# due to the caCORE API deficiency described below.
|
93
|
+
# * Replaced fetched objects with the corresponding cached objects where possible.
|
94
|
+
# * Set inverses to enforce inverse integrity where necessary.
|
78
95
|
#
|
79
96
|
# @quirk caCORE Dereferencing a caCORE search result uncascaded collection attribute
|
80
97
|
# raises a Hibernate missing session error.
|
@@ -88,27 +105,63 @@ module CaRuby
|
|
88
105
|
# This situation is rectified in this detoxify method by setting the dependent owner
|
89
106
|
# attribute to the fetched owner in the detoxification {Jinx::ReferenceVisitor} copy-match-merge.
|
90
107
|
#
|
91
|
-
# @
|
108
|
+
# @param [Resource] toxic the fetched object to detoxify
|
109
|
+
# @return [Resource, <Resource>] the detoxified object(s)
|
92
110
|
def detoxify(toxic)
|
93
111
|
return if toxic.nil?
|
94
112
|
if toxic.collection? then
|
95
113
|
toxic.each { |obj| detoxify(obj) }
|
96
114
|
else
|
97
115
|
logger.debug { "Detoxifying the toxic caCORE result #{toxic.qp}..." }
|
98
|
-
@ftchd_vstr.visit(toxic) { |ref|
|
116
|
+
@ftchd_vstr.visit(toxic) { |ref| detoxify_object(ref) }
|
99
117
|
logger.debug { "Detoxified the toxic caCORE result #{toxic.qp}." }
|
100
118
|
end
|
101
119
|
toxic
|
102
120
|
end
|
103
121
|
|
122
|
+
# Detoxifies the given domain object. This method is called by {#detoxify} to detoxify a visited
|
123
|
+
# object fetched from the database. This method does not detoxify referenced objects.
|
124
|
+
#
|
125
|
+
# @param (see #detoxify)
|
126
|
+
def detoxify_object(toxic)
|
127
|
+
ensure_introspected(toxic.class)
|
128
|
+
reconcile_fetched_attributes(toxic)
|
129
|
+
clear_toxic_attributes(toxic)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Replaces fetched references with cached references where possible.
|
133
|
+
#
|
134
|
+
# @param (see #detoxify)
|
135
|
+
def reconcile_fetched_attributes(toxic)
|
136
|
+
toxic.class.fetched_domain_attributes.each_pair do |fa, fp|
|
137
|
+
# the fetched reference
|
138
|
+
ref = toxic.send(fa) || next
|
139
|
+
# the inverse attribute
|
140
|
+
fi = fp.inverse
|
141
|
+
# Replace each fetched reference with the cached equivalent, if it exists.
|
142
|
+
if fp.collection? then
|
143
|
+
ref.to_compact_hash { |mbr| @cache[mbr] }.each do |fmbr, cmbr|
|
144
|
+
if fmbr != cmbr and (fi.nil? or cmbr.send(fi).nil?) then
|
145
|
+
ref.delete(fmbr)
|
146
|
+
ref << cmbr
|
147
|
+
logger.debug { "Replaced fetched #{toxic} #{fa} #{fmbr} with cached #{cmbr}." }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
else
|
151
|
+
cref = @cache[ref]
|
152
|
+
if cref and cref != ref and (fi.nil? or cref.send(fi).nil?) then
|
153
|
+
toxic.set_property_value(fa, cref)
|
154
|
+
logger.debug { "Replaced fetched #{toxic} #{fa} #{ref} with cached #{cref}." }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
104
160
|
# Sets each of the toxic attributes in the given domain object to the corresponding
|
105
161
|
# {Metadata#empty_value}.
|
106
|
-
#
|
107
|
-
# @param
|
162
|
+
#
|
163
|
+
# @param (see #detoxify)
|
108
164
|
def clear_toxic_attributes(toxic)
|
109
|
-
# The result class might not have been previously referenced. In that case,
|
110
|
-
# introspect the class.
|
111
|
-
ensure_introspected(toxic.class)
|
112
165
|
pas = toxic.class.toxic_attributes
|
113
166
|
return if pas.empty?
|
114
167
|
logger.debug { "Clearing toxic #{toxic.qp} attributes #{pas.to_series}..." }
|
@@ -117,12 +170,11 @@ module CaRuby
|
|
117
170
|
next unless prop.java_property?
|
118
171
|
# the empty or nil value to set
|
119
172
|
value = toxic.class.empty_value(pa)
|
120
|
-
# Use the Java writer method rather than the standard
|
121
|
-
# The standard attribute writer enforces inverse integrity,
|
122
|
-
# accessing the current toxic value. The Java writer
|
123
|
-
|
124
|
-
|
125
|
-
toxic.send(writer, value)
|
173
|
+
# Clear the attribute. Use the Java writer method rather than the standard
|
174
|
+
# attribute writer method. The standard attribute writer enforces inverse integrity,
|
175
|
+
# which potential requires accessing the current toxic value. The Java writer
|
176
|
+
# bypasses inverse integrity.
|
177
|
+
toxic.send(prop.java_writer, value)
|
126
178
|
end
|
127
179
|
end
|
128
180
|
|
@@ -131,21 +183,25 @@ module CaRuby
|
|
131
183
|
# * Set the inverses to enforce inverse integrity.
|
132
184
|
#
|
133
185
|
# @param (see #persistify_object)
|
186
|
+
# @param [Jinx::Resource] other the source domain object
|
134
187
|
# @raise [ArgumentError] if obj is a collection and other is not nil
|
135
188
|
def persistify(obj, other=nil)
|
136
189
|
if obj.collection? then
|
137
|
-
|
190
|
+
# A source object is not meaningful for a collection.
|
191
|
+
if other then
|
192
|
+
raise DatabaseError.new("Database reader persistify other argument is not supported for collection #{obj.qp}")
|
193
|
+
end
|
138
194
|
obj.each { |ref| persistify(ref) }
|
139
195
|
return obj
|
140
196
|
end
|
141
|
-
#
|
142
|
-
# subtype. Introspect the object class if necessary.
|
143
|
-
ensure_introspected(obj.class)
|
144
|
-
# set the inverses before recursing to dependents
|
197
|
+
# set the inverses before recursing to references
|
145
198
|
set_inverses(obj)
|
146
|
-
|
147
|
-
|
148
|
-
|
199
|
+
@pst_vstr.visit(obj) do |ref|
|
200
|
+
persistify_object(ref) if ref.identifier and ref.snapshot.nil?
|
201
|
+
end
|
202
|
+
# merge the other object content if available
|
203
|
+
obj.merge_into_snapshot(other) if other
|
204
|
+
obj
|
149
205
|
end
|
150
206
|
|
151
207
|
# Introspects the given class, if necessary. The class must be either introspected
|
@@ -166,19 +222,22 @@ module CaRuby
|
|
166
222
|
# Takes a {Persistable#snapshot} of obj to track changes, adds a lazy loader and
|
167
223
|
# adds the object to the cache.
|
168
224
|
#
|
169
|
-
# If the other fetched source object is given, then the
|
225
|
+
# If the other fetched source object is given, then the snapshot is updated
|
170
226
|
# with the non-nil values from other.
|
171
227
|
#
|
172
228
|
# @param [Jinx::Resource] obj the domain object to make persistable
|
173
229
|
# @param [Jinx::Resource] other the source domain object
|
174
230
|
# @return [Jinx::Resource] obj
|
175
231
|
def persistify_object(obj, other=nil)
|
232
|
+
# The attribute type is introspected, but the object might be an unintrospected
|
233
|
+
# subtype. Introspect the object class if necessary.
|
234
|
+
ensure_introspected(obj.class)
|
176
235
|
# take a snapshot of the database content
|
177
236
|
snapshot(obj, other)
|
178
237
|
# add lazy loader to the unfetched attributes
|
179
238
|
add_lazy_loader(obj)
|
180
239
|
# add to the cache
|
181
|
-
|
240
|
+
@cache.add(obj)
|
182
241
|
obj
|
183
242
|
end
|
184
243
|
|
@@ -214,13 +273,6 @@ module CaRuby
|
|
214
273
|
# merge the other object content if available
|
215
274
|
obj.merge_into_snapshot(other) if other
|
216
275
|
end
|
217
|
-
|
218
|
-
# @param [Jinx::Resource] obj the object to cache
|
219
|
-
# @raise [ArgumentError] if the given item does not have an identifier
|
220
|
-
def encache(obj)
|
221
|
-
@cache ||= create_cache
|
222
|
-
@cache.add(obj)
|
223
|
-
end
|
224
276
|
|
225
277
|
# @quirk JRuby identifier is not a stable object when fetched from the database, i.e.:
|
226
278
|
# obj.identifier.equal?(obj.identifier) #=> false
|
@@ -231,10 +283,7 @@ module CaRuby
|
|
231
283
|
# @return [Cache] a new object cache
|
232
284
|
def create_cache
|
233
285
|
Cache.new do |obj|
|
234
|
-
if obj.identifier
|
235
|
-
Jinx.fail(ArgumentError, "Can't cache object without identifier: #{obj}")
|
236
|
-
end
|
237
|
-
obj.identifier.to_s.to_i
|
286
|
+
obj.identifier.to_s.to_i if obj.identifier
|
238
287
|
end
|
239
288
|
end
|
240
289
|
end
|