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.
@@ -75,9 +75,9 @@ module CaRuby
75
75
  # guard against recursive call back into the same operation
76
76
  # the only allowed recursive call is a dependent create which first creates the owner
77
77
  if recursive_save?(obj, :create) then
78
- Jinx.fail(DatabaseError, "Create #{obj.qp} recursively called in context #{print_operations}")
78
+ raise DatabaseError.new("Create #{obj.qp} recursively called in context #{print_operations}")
79
79
  elsif obj.identifier then
80
- Jinx.fail(DatabaseError, "Create unsuccessful since #{obj.qp} already has identifier #{obj.identifier}")
80
+ raise DatabaseError.new("Create unsuccessful since #{obj.qp} already has identifier #{obj.identifier}")
81
81
  end
82
82
  # create the object
83
83
  perform(:create, obj) { create_object(obj) }
@@ -97,7 +97,7 @@ module CaRuby
97
97
  def update(obj)
98
98
  # guard against a recursive call back into the same operation.
99
99
  if recursive_save?(obj, :update) then
100
- Jinx.fail(DatabaseError, "Update #{obj.qp} recursively called in context #{print_operations}")
100
+ raise DatabaseError.new("Update #{obj.qp} recursively called in context #{print_operations}")
101
101
  end
102
102
  # update the object
103
103
  perform(:update, obj) { update_object(obj) }
@@ -140,7 +140,7 @@ module CaRuby
140
140
  # @raise [ArgumentError] if obj is nil or empty
141
141
  # @raise [DatabaseError] if obj could not be created
142
142
  def ensure_exists(obj)
143
- Jinx.fail(ArgumentError, "Database ensure_exists is missing a domain object argument") if obj.nil_or_empty?
143
+ raise ArgumentError.new("Database ensure_exists is missing a domain object argument") if obj.nil_or_empty?
144
144
  obj.enumerate { |ref| find(ref, :create) unless ref.identifier }
145
145
 
146
146
  end
@@ -190,7 +190,7 @@ module CaRuby
190
190
  result = create_dependent(owner, obj) if owner
191
191
  result ||= create_from_template(obj)
192
192
  if result.nil? then
193
- Jinx.fail(DatabaseError, "#{obj.class.qp} is not creatable in context #{print_operations}")
193
+ raise DatabaseError.new("#{obj.class.qp} is not creatable in context #{print_operations}")
194
194
  end
195
195
  ensure
196
196
  # since obj now has an id, removed from transients set
@@ -242,7 +242,7 @@ module CaRuby
242
242
  # @raise [DatabaseError] if obj does not have a proxy
243
243
  def save_with_proxy(obj)
244
244
  proxy = obj.saver_proxy
245
- if proxy.nil? then Jinx.fail(DatabaseError, "#{obj.class.qp} does not have a proxy") end
245
+ if proxy.nil? then raise DatabaseError.new("#{obj.class.qp} does not have a proxy") end
246
246
  if recursive_save?(proxy, :create) then
247
247
  logger.debug { "Foregoing #{obj.qp} save, since it will be handled by creating the proxy #{proxy}." }
248
248
  return
@@ -419,7 +419,7 @@ module CaRuby
419
419
  def update_object(obj)
420
420
  # database identifier is required for update
421
421
  if obj.identifier.nil? then
422
- Jinx.fail(DatabaseError, "Update target is missing a database identifier: #{obj}")
422
+ raise DatabaseError.new("Update target is missing a database identifier: #{obj}")
423
423
  end
424
424
 
425
425
  # If this object is proxied, then delegate to the proxy.
@@ -435,7 +435,9 @@ module CaRuby
435
435
 
436
436
  # Update a cascaded dependent by updating the owner.
437
437
  owner = cascaded_dependent_owner(obj)
438
- if owner then return update_cascaded_dependent(owner, obj) end
438
+ if owner and owner.updatable? then
439
+ return update_cascaded_dependent(owner, obj)
440
+ end
439
441
  # Not a cascaded dependent; update using a template.
440
442
  update_from_template(obj)
441
443
  end
@@ -460,6 +462,9 @@ module CaRuby
460
462
  end
461
463
 
462
464
  def update_from_template(obj)
465
+ unless obj.updatable? then
466
+ raise DatabaseError.new("#{obj.class.qp} update is not allowed by caTissue")
467
+ end
463
468
  tmpl = build_update_template(obj)
464
469
  # call the caCORE service with an obj update template
465
470
  save_with_template(obj, tmpl) { |svc| svc.update(tmpl) }
@@ -512,7 +517,7 @@ module CaRuby
512
517
  # @param [TemplateBuilder] builder the builder to use
513
518
  # @return [Jinx::Resource] the template to use as the save argument
514
519
  def build_save_template(obj, builder)
515
- builder.build_template(obj)
520
+ builder.build_template(obj, @operations.last.autogenerated?)
516
521
  end
517
522
 
518
523
  # @param (see #delete)
@@ -520,7 +525,7 @@ module CaRuby
520
525
  def delete_object(obj)
521
526
  # database identifier is required for delete
522
527
  if obj.identifier.nil? then
523
- Jinx.fail(DatabaseError, "Delete target is missing a database identifier: #{obj}")
528
+ raise DatabaseError.new("Delete target is missing a database identifier: #{obj}")
524
529
  end
525
530
  persistence_service(obj.class).delete_object(obj)
526
531
  end
@@ -561,8 +566,7 @@ module CaRuby
561
566
  # @param [Jinx::Resource] target the save argument
562
567
  # @param [Jinx::Resource] source the caCORE save result
563
568
  def sync_saved(target, source)
564
- # clear the toxic source attributes
565
- detoxify(source)
569
+ detoxify_save_result(source)
566
570
  # sync the save result source with the database
567
571
  sync_saved_result_with_database(source, target)
568
572
  # merge the source into the target
@@ -578,6 +582,10 @@ module CaRuby
578
582
  save_changed_dependents(target)
579
583
  end
580
584
 
585
+ def detoxify_save_result(source)
586
+ detoxify(source)
587
+ end
588
+
581
589
  # Synchronizes the given save result with the database content.
582
590
  # The source is synchronized by {#sync_save_result}.
583
591
  #
@@ -18,7 +18,7 @@ module CaRuby
18
18
  def initialize(database)
19
19
  @database = database
20
20
  unless block_given? then
21
- Jinx.fail(ArgumentError, "@{qp} is missing the required template copy attribute selector block")
21
+ raise ArgumentError.new("@{qp} is missing the required template copy attribute selector block")
22
22
  end
23
23
 
24
24
  # the mergeable attributes filter the given block with exclusions
@@ -79,12 +79,13 @@ module CaRuby
79
79
  # objects. These uncascaded objects should be ignored by the application but aren't.
80
80
  #
81
81
  # @param [Jinx::Resource] obj the domain object to save
82
+ # @param [Boolean] autogenerated whether the save is an auto-generated object update
82
83
  # @return [Jinx::Resource] the template to use as the caCORE argument
83
84
  def build_template(obj, autogenerated=false)
84
85
  # set the database operation subject
85
86
  @subject = obj
86
87
  # prepare the object for a store operation
87
- ensure_savable(obj)
88
+ ensure_savable(obj, autogenerated)
88
89
  # copy the cascade hierarchy
89
90
  logger.debug { "Building savable template for #{obj.qp}..." }
90
91
  tmpl = @copy_vstr.visit(obj)
@@ -105,8 +106,9 @@ module CaRuby
105
106
  # metadata and creates them if necessary.
106
107
  #
107
108
  # @param [Jinx::Resource] obj the object to save
109
+ # @param [Boolean] autogenerated whether the save is an auto-generated object update
108
110
  # @raise [ValidationError] if the object is invalid
109
- def ensure_savable(obj)
111
+ def ensure_savable(obj, autogenerated)
110
112
  # Ensure that an update is fetched to complete the object state with any missing references.
111
113
  if obj.identifier and not obj.fetched? then
112
114
  logger.debug { "Fetching #{obj} prior to building a save template..." }
@@ -123,8 +125,8 @@ module CaRuby
123
125
  @database.ensure_exists(prereqs)
124
126
  logger.debug { "Prerequisite references for #{obj.qp} exist: #{prereqs.map { |ref| ref }.to_series}." }
125
127
  end
126
- # Verify that the object is complete
127
- obj.validate
128
+ # Verify that the object is complete.
129
+ obj.validate(autogenerated)
128
130
  end
129
131
 
130
132
  # Fetches the given update object savable references on demand.
@@ -293,7 +295,7 @@ module CaRuby
293
295
  # @param [Jinx::Resource] obj the domain object to save
294
296
  # @return [<Jinx::Resource>] the references which must be created in order to store the object
295
297
  def collect_prerequisites(obj)
296
- prereqs = Set.new
298
+ prereqs = Array.new
297
299
  # visit the cascaded attributes
298
300
  @prereq_vstr.visit(obj) do |pref|
299
301
  # Check each mergeable attribute for prerequisites. The mergeable attributes includes
@@ -326,6 +328,8 @@ module CaRuby
326
328
  end
327
329
  end
328
330
  end
331
+ # Remove duplicates.
332
+ prereqs.uniq!
329
333
  prereqs
330
334
  end
331
335
  end
@@ -36,27 +36,28 @@ module CaRuby
36
36
  # The {Writer#save} method creates or updates references as necessary to persist its argument domain object.
37
37
  # It is not necessary to fetch references first or follow dependency ordering rules, which can be
38
38
  # implicit and tortuous in caBIG applications. Build the object you want to persist and call the
39
- # store method. {Jinx::Resource} sets reasonable default values, recognizes application dependencies and steers
40
- # around caBIG idiosyncracies to the extent possible.
39
+ # store method. {Jinx::Resource} sets reasonable default values, recognizes application dependencies and
40
+ # steers around caBIG idiosyncracies to the extent possible.
41
41
  class Database
42
42
  include Reader, Writer, Persistifier
43
43
 
44
44
  # The application and database connection access command line options.
45
45
  ACCESS_OPTS = [
46
- [:user, "--user USER", "the application login user"],
47
- [:password, "--password PSWD", "the application login password"],
48
- [:host, "--host HOST", "the application host name"],
49
- [:port, "--port PORT", "the application port number"],
50
- [:classpath, "--classpath PATH", "the application client classpath"],
51
- [:database_host, "--database_host HOST", "the database host name"],
52
- [:database_type, "--database_type TYPE", "the database type (mysql or oracle)"],
53
- [:database_driver, "--database_driver DRIVER", "the database driver string"],
54
- [:database_driver_class, "--database_driver_class CLASS", "the database driver class name"],
55
- [:database_port, "--database_port PORT", Integer, "the database port number"],
56
- [:database, "--database NAME", "the database name"],
57
- [:database_user, "--database_user USER", "the database login user"],
58
- [:database_password, "--database_password PSWD", "the database login password"]
59
- ]
46
+ [:user, '-u USER', '--user USER', 'the application login user'],
47
+ [:password, '-p PSWD', '--password PSWD', 'the application login password'],
48
+ [:host, '--host HOST', 'the application host name'],
49
+ [:port, '--port PORT', 'the application port number'],
50
+ [:classpath, '--classpath PATH', 'the application client classpath'],
51
+ [:database_host, '--database_host HOST', 'the database host name'],
52
+ [:database_port, '--database_port PORT', Integer, 'the database port number'],
53
+ [:database, '--database NAME', 'the database name'],
54
+ [:database_url, '--database_url URL', 'the database connection URL'],
55
+ [:database_type, '--database_type TYPE', 'the database type (mysql or oracle)'],
56
+ [:database_driver, '--database_driver DRIVER', 'the database driver string'],
57
+ [:database_driver_class, '--database_driver_class CLASS', 'the database driver class name'],
58
+ [:database_user, '--database_user USER', 'the database login user'],
59
+ [:database_password, '--database_password PSWD', 'the database login password']
60
+ ]
60
61
 
61
62
  attr_reader :operations
62
63
 
@@ -92,18 +93,14 @@ module CaRuby
92
93
  # Database.new(:user => 'perdita', :password => 'changeMe')
93
94
  def initialize(service_name, opts=nil)
94
95
  super()
95
- # the options can be defined in a block
96
+ # The options can be defined in a block.
96
97
  opts ||= yield if block_given?
97
- # import the Java classes on demand
98
- Database.import_java_classes
99
- # the fetched object cache
100
- @defaults = {}
101
- if opts.nil? then Jinx.fail(ArgumentError, "Missing required database access properties") end
98
+ if opts.nil? then raise ArgumentError.new("Missing required database access properties") end
102
99
  @user = Options.get(:user, opts)
103
100
  @password = Options.get(:password, opts)
104
101
  host = Options.get(:host, opts)
105
102
  port = Options.get(:port, opts)
106
- # class => service hash; default is the catissuecore app service
103
+ # The class => service hash is populated with the default service.
107
104
  @def_persist_svc = PersistenceService.new(service_name, :host => host, :port => port)
108
105
  @persistence_services = [@def_persist_svc].to_set
109
106
  @cls_svc_hash = Hash.new(@def_persist_svc)
@@ -112,33 +109,34 @@ module CaRuby
112
109
  # the objects for which exists? is unsuccessful in the context of a nested operation
113
110
  @transients = Set.new
114
111
  end
112
+
113
+ # @return [Boolean] whether there is an active session
114
+ def open?
115
+ !!@session
116
+ end
117
+
118
+ # @return [Boolean] whether this database is not {#open?}
119
+ def closed?
120
+ not open?
121
+ end
115
122
 
116
123
  # Calls the block given to this method with this database as an argument, and closes the
117
124
  # database when done.
118
125
  #
126
+ # @param [String, nil] user the application login user
127
+ # @param [String, nil] password the application login password
119
128
  # @yield [database] the operation to perform on the database
120
129
  # @yieldparam [Database] database self
121
- def open
130
+ def open(user=nil, password=nil)
131
+ raise ArgumentError.new("Database open requires an execution block") unless block_given?
132
+ raise DatabaseError.new("The caRuby application database is already in use.") if open?
122
133
  # reset the execution timers
123
134
  persistence_services.each { |svc| svc.timer.reset }
124
- # call the block and close when done
135
+ # Start the session.
136
+ start_session(user, password)
137
+ # Call the block and close when done.
125
138
  yield(self) ensure close
126
139
  end
127
-
128
- # Releases database resources. This method should be called when database interaction
129
- # is completed.
130
- def close
131
- return if @session.nil?
132
- begin
133
- @session.terminate_session
134
- rescue Exception => e
135
- logger.error("Session termination unsuccessful - #{e.message}")
136
- end
137
- # clear the cache
138
- clear
139
- logger.info("Disconnected from application server.")
140
- @session = nil
141
- end
142
140
 
143
141
  # @return [Numeric] the execution time in seconds spent since the last open
144
142
  def execution_time
@@ -157,9 +155,8 @@ module CaRuby
157
155
  # @return [PersistanceService] the corresponding service
158
156
  def persistence_service(klass)
159
157
  unless Class === klass then
160
- Jinx.fail(ArgumentError, "#{self} persistence_service argument is not a Class: {#klass.qp}")
158
+ raise ArgumentError.new("#{self} persistence_service argument is not a Class: {#klass.qp}")
161
159
  end
162
- start_session if @session.nil?
163
160
  @def_persist_svc
164
161
  end
165
162
 
@@ -169,6 +166,15 @@ module CaRuby
169
166
  def add_persistence_service(service)
170
167
  @persistence_services << service
171
168
  end
169
+
170
+ # Imports the caCORE +ClientSession+ class on demand.
171
+ def self.const_missing(sym)
172
+ if sym == :ClientSession then
173
+ java_import Java::gov.nih.nci.system.comm.client.ClientSession
174
+ else
175
+ super
176
+ end
177
+ end
172
178
 
173
179
  alias :to_s :print_class_and_id
174
180
 
@@ -178,10 +184,19 @@ module CaRuby
178
184
 
179
185
  private
180
186
 
181
- # Imports this class's Java classes on demand.
182
- def self.import_java_classes
183
- # The caBIG client session class.
184
- java_import Java::gov.nih.nci.system.comm.client.ClientSession unless const_defined?(:ClientSession)
187
+ # Releases database resources. This method should be called when database interaction
188
+ # is completed.
189
+ def close
190
+ return if @session.nil?
191
+ begin
192
+ @session.terminate_session
193
+ rescue Exception => e
194
+ logger.error("Session termination unsuccessful - #{e.message}")
195
+ end
196
+ # clear the cache
197
+ clear
198
+ logger.info("Disconnected from application server.")
199
+ @session = nil
185
200
  end
186
201
 
187
202
  # A mergeable autogenerated operation is recursively defined as:
@@ -213,22 +228,34 @@ module CaRuby
213
228
  # Performs the operation given by the given op symbol on obj by calling the block given to this method.
214
229
  # Lazy loading is suspended during the operation.
215
230
  #
216
- # @param [:find, :query, :create, :udate, :delete] op the database operation type
217
- # @param [Jinx::Resource] obj the domain object on which the operation is performed
231
+ # @param [:find, :query, :create, :update, :delete] op the database operation type
232
+ # @param [Resource] obj the domain object on which the operation is performed
218
233
  # @param opts (#see Operation#initialize)
219
234
  # @yield the database operation block
220
235
  # @return the result of calling the operation block
221
- def perform(op, obj, opts=nil)
236
+ def perform(op, obj, opts=nil, &block)
222
237
  op_s = op.to_s.capitalize_first
223
238
  pa = Options.get(:attribute, opts)
224
239
  attr_s = " #{pa}" if pa
225
240
  ag_s = " autogenerated" if Options.get(:autogenerated, opts)
226
241
  ctxt_s = " in context #{print_operations}" unless @operations.empty?
227
242
  logger.info(">> #{op_s}#{ag_s} #{obj.pp_s(:single_line)}#{attr_s}#{ctxt_s}...")
243
+ # Clear the error flag.
244
+ @error = nil
245
+ # Push the operation on the nested operation stack.
228
246
  @operations.push(Operation.new(op, obj, opts))
229
247
  begin
230
248
  # perform the operation
231
- result = @lazy_loader.suspend { yield }
249
+ result = perform_operation(&block)
250
+ rescue Exception => e
251
+ # If the current operation is the immediate cause, then print the
252
+ # error to the log.
253
+ if @error.nil? then
254
+ msg = "Error performing #{op} on #{obj}:\n#{e.message}\n#{obj.dump}\n#{e.backtrace.qp}"
255
+ logger.error(msg)
256
+ @error = e
257
+ end
258
+ raise e
232
259
  ensure
233
260
  # the operation is done
234
261
  @operations.pop
@@ -239,16 +266,31 @@ module CaRuby
239
266
  result
240
267
  end
241
268
 
269
+ # Calls the given block with the lazy loader suspended.
270
+ # The database is opened, if necessary.
271
+ #
272
+ # @yield the database operation block
273
+ # @return the result of calling the operation block
274
+ def perform_operation(&block)
275
+ if closed? then
276
+ open { perform_operation(&block) }
277
+ else
278
+ @lazy_loader.suspend { yield }
279
+ end
280
+ end
281
+
242
282
  def each_persistence_service(&block)
243
283
  ObjectSpace.each_object(PersistenceService, &block)
244
284
  end
245
285
 
246
286
  # Initializes the default application service.
247
- def start_session
248
- if @user.nil? then Jinx.fail(DatabaseError, 'Application user option missing') end
249
- if @password.nil? then Jinx.fail(DatabaseError, 'Application password option missing') end
287
+ def start_session(user=nil, password=nil)
288
+ user ||= @user
289
+ password ||= @password
290
+ if user.nil? then raise DatabaseError.new('The caRuby application is missing the login user') end
291
+ if password.nil? then raise DatabaseError.new('The caRuby application is missing the login password') end
250
292
  @session = ClientSession.instance
251
- connect(@user, @password)
293
+ connect(user, password)
252
294
  end
253
295
 
254
296
  # Returns the current database operation stack as a String.
@@ -266,7 +308,7 @@ module CaRuby
266
308
  begin
267
309
  @session.start_session(user, password)
268
310
  rescue Exception => e
269
- logger.error("Login of #{user} unsuccessful - #{e.message}")
311
+ logger.error("Login of #{user} with password #{password} was unsuccessful - #{e.message}")
270
312
  raise e
271
313
  end
272
314
  logger.info("Connected to application server.")
@@ -83,13 +83,13 @@ class Coordinate < Array
83
83
  # @raise [TypeError] if other is not a Coordinate
84
84
  def <=>(other)
85
85
  return 0 if equal?(other)
86
- Jinx.fail(TypeError, "Can't compare #{self} with #{other} since it is not a Coordinate") unless Coordinate === other
87
- Jinx.fail(ArgumentError, "Can't compare #{self} with #{other} since it has a different dimension count") unless size == other.size
86
+ raise TypeError.new("Can't compare #{self} with #{other} since it is not a Coordinate") unless Coordinate === other
87
+ raise ArgumentError.new("Can't compare #{self} with #{other} since it has a different dimension count") unless size == other.size
88
88
  REXML::SyncEnumerator.new(self.reverse, other.reverse).each_with_index do |pair, index|
89
89
  dim = pair.first
90
90
  odim = pair.last
91
- Jinx.fail(ArgumentError, "Can't compare #{self} with missing dimension #{index} to #{other}") unless dim
92
- Jinx.fail(ArgumentError, "Can't compare #{self} to #{other} with missing dimension #{index}") unless odim
91
+ raise ArgumentError.new("Can't compare #{self} with missing dimension #{index} to #{other}") unless dim
92
+ raise ArgumentError.new("Can't compare #{self} to #{other} with missing dimension #{index}") unless odim
93
93
  cmp = dim <=> odim
94
94
  return cmp unless cmp.zero?
95
95
  end
@@ -119,10 +119,10 @@ module CaRuby
119
119
  # or if there is a middle name but no first name
120
120
  def validate
121
121
  if last.nil? and first.nil? then
122
- Jinx.fail(Jinx::ValidationError, "Name is missing both the first and last fields")
122
+ raise Jinx::ValidationError.new("Name is missing both the first and last fields")
123
123
  end
124
124
  if !middle.nil? and first.nil? then
125
- Jinx.fail(Jinx::ValidationError, "Name with middle field #{middle} is missing the first field")
125
+ raise Jinx::ValidationError.new("Name with middle field #{middle} is missing the first field")
126
126
  end
127
127
  end
128
128
 
@@ -66,12 +66,12 @@ module CaRuby
66
66
  #
67
67
  # Raises ConfigurationError if file doesn't exist or couldn't be parsed.
68
68
  def load_properties(file)
69
- Jinx.fail(ConfigurationError, "Properties file not found: #{File.expand_path(file)}") unless File.exists?(file)
69
+ raise ConfigurationError.new("Properties file not found: #{File.expand_path(file)}") unless File.exists?(file)
70
70
  properties = {}
71
71
  begin
72
- YAML::load_file(file).each { |key, value| properties[key.to_sym] = value }
72
+ YAML.load_file(file).each { |key, value| properties[key.to_sym] = value }
73
73
  rescue
74
- Jinx.fail(ConfigurationError, "Could not read properties file #{file}: " + $!)
74
+ raise ConfigurationError.new("Could not read properties file #{file}: " + $!)
75
75
  end
76
76
  # Uncomment the following line to print detail properties.
77
77
  #logger.debug { "#{file} properties:\n#{properties.pp_s}" }
@@ -103,7 +103,7 @@ module CaRuby
103
103
  # Validates that the required properties exist.
104
104
  def validate_properties
105
105
  @required_properties.each do |key|
106
- Jinx.fail(ConfigurationError, "A required #{@application} property was not found: #{key}") unless has_property?(key)
106
+ raise ConfigurationError.new("A required #{@application} property was not found: #{key}") unless has_property?(key)
107
107
  end
108
108
  end
109
109
  end
@@ -6,7 +6,7 @@ class String
6
6
  when /^(I{0,3})$/ then $1.size
7
7
  when /^(I{0,3})(V|X)$/ then ROMAN_UNITS[$2] - $1.size
8
8
  when /^(V)(I{0,3})$/ then ROMAN_UNITS[$1] + $2.size
9
- else Jinx.fail(ArgumentError, "#{self} is not a roman numeral in the range I-X")
9
+ else raise ArgumentError.new("#{self} is not a roman numeral in the range I-X")
10
10
  end
11
11
  end
12
12
 
@@ -18,7 +18,7 @@ end
18
18
  class Integer
19
19
  # @return [String] the roman numeral equivalent of this integer
20
20
  def to_roman
21
- if self < 1 or self > 10 then Jinx.fail(ArgumentError, "#{self} cannot be converted to a roman numeral in the range I-X")
21
+ if self < 1 or self > 10 then raise ArgumentError.new("#{self} cannot be converted to a roman numeral in the range I-X")
22
22
  elsif self < 4 then 'I' * self
23
23
  elsif self < 6 then ('I' * (5 - self)) + 'V'
24
24
  elsif self < 9 then 'V' + ('I' * (self - 5))
@@ -26,7 +26,7 @@ class Version < Array
26
26
  # Version.new(1, 1, beta) > beta #=> true
27
27
  def <=>(other)
28
28
  return 0 if equal?(other)
29
- Jinx.fail(ArgumentError, "Comparand is not a #{self.class}: #{other}") unless self.class === other
29
+ raise ArgumentError.new("Comparand is not a #{self.class}: #{other}") unless self.class === other
30
30
  return -1 if other.predecessor == self
31
31
  return 1 unless predecessor.nil? or predecessor < other
32
32
  each_with_index do |component, index|
@@ -0,0 +1,17 @@
1
+ require 'jinx/json/serializable'
2
+
3
+ module CaRuby
4
+ module JSON
5
+ module Serializable
6
+ include Jinx::JSON::Serializable
7
+
8
+ # This method disables lazy-loading before delegating to Jinx.
9
+ #
10
+ # @param [State, Hash, nil] state the JSON state or serialization options
11
+ # @return [String] the JSON representation of this {Jinx::Resource}
12
+ def to_json(state=nil)
13
+ fetched? ? super : do_without_lazy_loader { super }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -168,9 +168,16 @@ module CaRuby
168
168
 
169
169
  alias :toxic_attributes :unfetched_attributes
170
170
 
171
+ # @quirk JRuby - This method body is copied to the +CaTissue::Metadata+ superclass since
172
+ # calling super from that class's overloaded +loadable_attributes+ method results in
173
+ # an infinite loop.
174
+ #
171
175
  # @return [<Symbol>] the Java attribute non-abstract {#unfetched_attributes}
172
176
  def loadable_attributes
173
- @ld_flt ||= unfetched_attributes.compose { |prop| prop.java_property? and not prop.type.abstract? }
177
+ # A change to this method body must be copied to CaTissue::Metadata; see rubydoc above.
178
+ @ld_flt ||= unfetched_attributes.compose do |prop|
179
+ prop.java_property? and not prop.type.abstract? and not prop.transient?
180
+ end
174
181
  end
175
182
 
176
183
  private