caruby-core 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|