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.
@@ -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