caruby-core 1.5.4 → 1.5.5

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/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
- 1.5.4 / 2011-07-19
4
+ 1.5.4 / 2011-09-22
5
+ ------------------
6
+ * Support mixed-case custom DE package name.
7
+
8
+ 1.5.4 / 2011-09-22
5
9
  ------------------
6
10
  * Add migration value filter option.
7
11
 
@@ -463,7 +463,7 @@ module CaRuby
463
463
  obj.class.attribute_metadata(attribute).collection? ? result : result.first
464
464
  end
465
465
 
466
- # Returns a copy of obj containing only those key attributes used in a find operation.
466
+ # @return [{Symbol => Object}, nil] the find operation key attributes, or nil if there is no complete key
467
467
  #
468
468
  # @quirk caCORE caCORE search fetches on all non-nil attributes, except occasionally the identifier
469
469
  # (cf. https://cabig-kc.nci.nih.gov/Bugzilla/show_bug.cgi?id=79).
@@ -475,28 +475,32 @@ module CaRuby
475
475
  key_value_hash(obj, obj.class.alternate_key_attributes)
476
476
  end
477
477
 
478
- # Returns the attribute => value hash suitable for a finder template if obj has searchable values
479
- # for all of the given key attributes, nil otherwise.
478
+ # @return [{Symbol => Object}, nil] the attribute => value hash suitable for a finder template
479
+ # if obj has searchable values for all of the given key attributes, nil otherwise
480
480
  def key_value_hash(obj, attributes)
481
481
  # the key must be non-trivial
482
482
  return if attributes.nil_or_empty?
483
483
  # the attribute => value hash
484
- attributes.to_compact_hash do |attr|
485
- value = obj.send(attr)
486
- # validate that no key attribute is missing and each reference exists
487
- if value.nil_or_empty? then
488
- logger.debug { "Can't fetch #{obj.qp} based on #{attributes.qp} since #{attr} does not have a value." }
489
- return
490
- elsif obj.class.domain_attribute?(attr) then
491
- unless exists?(value) then
492
- logger.debug { "Can't fetch #{obj.qp} based on #{attributes.qp} since #{attr} does not exist in the database: #{value}." }
493
- return
494
- end
484
+ attributes.to_compact_hash { |attr| finder_parameter(obj, attr) or return }
485
+ end
486
+
487
+ # @return a non-empty, existing find parameter for the given attribute
488
+ def finder_parameter(obj, attribute)
489
+ value = obj.send(attribute)
490
+ # validate that the key attribute is non-nil and each reference exists
491
+ if value.nil_or_empty? then
492
+ logger.debug { "Can't fetch #{obj.qp} based on key with missing attribute #{attribute}." }
493
+ nil
494
+ elsif obj.class.domain_attribute?(attribute) then
495
+ if exists?(value) then
495
496
  # the finder value is a copy of the reference with just the identifier
496
497
  value.copy(:identifier)
497
498
  else
498
- value
499
+ logger.debug { "Can't fetch #{obj.qp} based on key with domain attribute #{attribute}, since the referenced value does not exist in the database: #{value}." }
500
+ nil
499
501
  end
502
+ else
503
+ value
500
504
  end
501
505
  end
502
506
 
@@ -20,7 +20,7 @@ module CaRuby
20
20
  end
21
21
  matches
22
22
  end
23
-
23
+
24
24
  # Adds to the given target => source matches hash for the unmatched targets and sources
25
25
  # using {#match_minimal}.
26
26
  #
data/lib/caruby/domain.rb CHANGED
@@ -97,92 +97,9 @@ module CaRuby
97
97
  # Loading the access properties on demand sets the classpath.
98
98
  access_properties
99
99
  end
100
-
101
- # # @param [Class, String] class_or_name the class to import into this module
102
- # # @return [Class] the imported {Resource} class
103
- # def resource_import(class_or_name)
104
- # java_import(class_or_name)
105
- # klass = Class.to_ruby(class_or_name)
106
- # add_class(klass)
107
- # klass
108
- # end
109
- #
110
- # # @param [String] the class name to check
111
- # # @eturn [Class, nil] the domain class for the class name, or nil if none in this module
112
- # def domain_type_with_name(name)
113
- # pkg, base = Java.split_class_name(name)
114
- # return unless pkg.nil? or pkg == @java_package
115
- # begin
116
- # type = const_get(base)
117
- # rescue JavaImportError => e
118
- # # no such domain type; not an error.
119
- # # other exceptions indicate that there was a domain type but could not be loaded; these exceptions propagate up the call stack
120
- # logger.debug { "#{$!}" }
121
- # return
122
- # end
123
- # type if type < Resource
124
- # end
125
100
 
126
101
  private
127
-
128
- # # Adds the given class to this domain module. The class is extended with {Metadata} methods.
129
- # #
130
- # # @param [Class] the {Resource} class to add
131
- # def add_class(klass)
132
- # # This domain module's known Resource classes.
133
- # @rsc_classes ||= Set.new
134
- # # Bail if already added.
135
- # return if @rsc_classes.include?(klass)
136
- #
137
- # logger.debug { "Adding #{klass.java_class.name} to #{qp}..." }
138
- # # Add metadata to the class. This is done before adding the superclass and
139
- # # referenced metadata, since they in turn might reference the current class
140
- # # metadata.
141
- # Metadata.extend_class(klass, self)
142
- # # Add the class to the known Resource class set.
143
- # @rsc_classes << klass
144
- #
145
- # # Make the superclass a Resource, if necessary.
146
- # sc = klass.superclass
147
- # unless @rsc_classes.include?(sc) then
148
- # # the domain module includes the superclass on demand
149
- # pkg, sym = Java.split_class_name(sc)
150
- # if pkg == @java_package then
151
- # # Load the superclass on demand; don't need to make this class a Resource,
152
- # # but do need to import the class.
153
- # const_get(sym)
154
- # else
155
- # # Superclass is not a member of the domain package; make this class a Resource.
156
- # mod = @mixin
157
- # klass.class_eval { include mod }
158
- # end
159
- # end
160
- #
161
- # # Add referenced domain classes as necessary.
162
- # klass.each_attribute_metadata do |attr_md|
163
- # ref = attr_md.type
164
- # next if @rsc_classes.include?(ref)
165
- # pkg, sym = Java.split_class_name(ref)
166
- # # This domain module adds a referenced Domain class on demand.
167
- # if pkg == @java_package then
168
- # puts "rm ac1 #{klass} #{attr_md} -> #{ref}..."
169
- # logger.debug { "Loading #{klass.qp} #{attr_md} reference #{ref.qp}" }
170
- # const_get(sym)
171
- # end
172
- # end
173
- #
174
- # # Invoke the callback.
175
- # class_added(klass)
176
- # logger.debug { "#{klass.java_class.name} added to #{qp}." }
177
- # # print the class structure to the log
178
- # logger.info(klass.pp_s)
179
- # end
180
- #
181
- # # Callback invoked after the given domain class is added to this domain module.
182
- # #
183
- # # @param [Class] klass the class that was added
184
- # def class_added(klass); end
185
-
102
+
186
103
  # Loads the application start-up properties in the given file path.
187
104
  #
188
105
  # @return (see #access_properties)
@@ -248,70 +165,6 @@ module CaRuby
248
165
  value
249
166
  end
250
167
  end
251
- #
252
- # # Imports the domain Java class with specified class name_or_sym.
253
- # # This method enables the domain class extensions for storing and retrieving domain objects.
254
- # # The class is added to this module.
255
- # #
256
- # # The optional block overrides the native Java property access wrappers.
257
- # # For example:
258
- # # ClinicalTrials.java_import Java::edu.wustl.catissuecore.domain.Study do
259
- # # def study_code=(value)
260
- # # value = value.to_s if Integer === value
261
- # # setStudyCode(value)
262
- # # end
263
- # # end
264
- # # imports the ClinicalTrials Study class as ClinicalTrials::Study and overrides the
265
- # # +study_code+ setter method.
266
- # #
267
- # # Convenience aliases are added to the imported class, e.g. attribute +studyParticipantCollection+
268
- # # is aliased as +study_participants+. Specifically, each attribute reader and writer is aliased with
269
- # # the lower-case, underscore equivalent and a name ending in 'Collection' is changed to plural.
270
- # # Pluralization is smart, e.g. +studyCollection+ is aliased to +studies+ rather than +studys+.
271
- # #
272
- # # The optional aliases argument consists of additional alias => standard attribute associations.
273
- # # The optional owner_attr argument is a non-Java annotation owner attribute.
274
- # #
275
- # # @param [Symbol] symbol the class symbol
276
- # # @param [String, nil] pkg the Java class package name, or nil for the default module package
277
- # # @return [Class] the imported domain class
278
- # def import_domain_class(symbol, pkg=nil)
279
- # # import the Java class
280
- # pkg ||= @java_package
281
- # name = [pkg, symbol.to_s].join('.')
282
- # logger.debug { "Detecting whether #{symbol} is a #{pkg} Java class..." }
283
- # # Push each imported class onto a stack. When all referenced classes are imported,
284
- # # each class on the stack is post-initialized and the class structure is printed to
285
- # # the log.
286
- # @import_stack ||= []
287
- # @import_stack.push(symbol)
288
- # begin
289
- # resource_import(name)
290
- # ensure
291
- # @import_stack.pop
292
- # end
293
- #
294
- # # the imported Java class is registered as a constant in this module
295
- # klass = const_get(symbol)
296
- # add_class(klass)
297
- #
298
- # # if we are back to top of the stack, then print the imported Resources
299
- # if symbol == @import_stack.first then
300
- # # a referenced class could be imported on demand in the course of printing a referencing class;
301
- # # the referenced class is then pushed onto the stack. thus, the stack can grow during the
302
- # # course of printing, but each imported class is consumed and printed in the end.
303
- # until @import_stack.empty? do
304
- # # the class constant
305
- # sym = @import_stack.pop
306
- # # the imported class
307
- # kls = const_get(sym)
308
- # # print the class structure to the log
309
- # logger.info(kls.pp_s)
310
- # end
311
- # end
312
- #
313
- # klass
314
- # end
315
168
 
316
169
  # The property/value matcher, e.g.:
317
170
  # host: jacardi
@@ -7,7 +7,6 @@ module CaRuby
7
7
  module Domain
8
8
  # Meta-data mix-in for attribute accessors.
9
9
  module Attributes
10
-
11
10
  # @return [<Symbol>] this class's attributes
12
11
  attr_reader :attributes
13
12
 
@@ -37,7 +36,7 @@ module CaRuby
37
36
  attr_md
38
37
  end
39
38
 
40
- # Returns the +[:identifier]+ primary key attribute array.
39
+ # @return [(Symbol)] the +[:identifier]+ primary key attribute singleton array
41
40
  def primary_key_attributes
42
41
  IDENTIFIER_ATTR_ARRAY
43
42
  end
@@ -45,13 +44,17 @@ module CaRuby
45
44
  # Returns this class's secondary key attribute array.
46
45
  # If this class's secondary key is not set, then the secondary key is the Metadata superclass
47
46
  # secondary key, if any.
47
+ #
48
+ # @return [<Symbol>] the secondary key attributes
48
49
  def secondary_key_attributes
49
50
  @scndy_key_attrs or superclass < Resource ? superclass.secondary_key_attributes : Array::EMPTY_ARRAY
50
51
  end
51
52
 
52
- # Returns this class's alternate key attribute array.
53
- # If this class's secondary key is not set, then the alternate key is the Metadata superclass
54
- # alternate key, if any.
53
+ # Returns this class's alternate key attribute array.
54
+ # If this class's secondary key is not set, then the alternate key is the {Metadata} superclass
55
+ # alternate key, if any.
56
+ #
57
+ # @return [<Symbol>] the alternate key attributes
55
58
  def alternate_key_attributes
56
59
  @alt_key_attrs or superclass < Resource ? superclass.alternate_key_attributes : Array::EMPTY_ARRAY
57
60
  end
@@ -71,9 +74,10 @@ module CaRuby
71
74
  attr_md || (attribute_missing(attribute) && @local_attr_md_hash[attribute])
72
75
  end
73
76
 
74
- # Returns the standard attribute symbol for the given name_or_alias.
75
- #
76
- # Raises NameError if the attribute is not found
77
+ # @param [Symbol, String] name_or_alias the attribute name or alias
78
+ # @return [Symbol] the standard attribute symbol for the given name or alias
79
+ # @raise [ArgumentError] if the attribute name or alias argument is missing
80
+ # @raise [NameError] if the attribute is not found
77
81
  def standard_attribute(name_or_alias)
78
82
  if name_or_alias.nil? then
79
83
  raise ArgumentError.new("#{qp} standard attribute call is missing the attribute name/alias parameter")
@@ -81,7 +85,7 @@ module CaRuby
81
85
  @alias_std_attr_map[name_or_alias.to_sym] or raise NameError.new("#{self} attribute not found: #{name_or_alias}")
82
86
  end
83
87
 
84
- ## the built-in Metadata attribute filters ##
88
+ ## Metadata ATTRIBUTE FILTERS ##
85
89
 
86
90
  # @return [<Symbol>] the domain attributes which wrap a java property
87
91
  # @see Attribute#java_property?
@@ -228,7 +232,11 @@ module CaRuby
228
232
  def fetched_dependent_attributes
229
233
  @ftchd_dep_attrs ||= (fetched_domain_attributes & dependent_attributes).to_a
230
234
  end
231
-
235
+
236
+ def nonowner_attributes
237
+ @nownr_atts ||= attribute_filter { |attr_md| not attr_md.owner? }
238
+ end
239
+
232
240
  # @return [<Symbol>] the saved dependent attributes
233
241
  # @see Attribute#dependent?
234
242
  # @see Attribute#saved?
@@ -269,7 +277,7 @@ module CaRuby
269
277
  end
270
278
 
271
279
  # @return [<Symbol>] the domain attributes whose referents must exist before an instance of this
272
- # metadata's subject classcan be created
280
+ # metadata's subject class can be created
273
281
  # @see Attribute#storable_prerequisite?
274
282
  def storable_prerequisite_attributes
275
283
  @stbl_prereq_dom_attrs ||= attribute_filter { |attr_md| attr_md.storable_prerequisite? }
@@ -51,26 +51,6 @@ module CaRuby
51
51
  inv_type.add_owner(self, attribute, inverse)
52
52
  end
53
53
 
54
- # Makes a new owner attribute. The attribute name is the lower-case demodulized
55
- # owner class name. The owner class must reference this class via the given
56
- # inverse dependent attribute.
57
- #
58
- # @param klass (see #detect_owner_attribute)
59
- # @param [Symbol] the owner -> dependent inverse attribute
60
- # @return [Symbol] this class's new owner attribute
61
- # @raise [ArgumentError] if the inverse is nil
62
- def create_owner_attribute(klass, inverse)
63
- if inverse.nil? then
64
- raise ArgumentError.new("Cannot create a #{qp} owner attribute to #{klass} without a dependent attribute to this class.")
65
- end
66
- attr = klass.name.demodulize.underscore.to_sym
67
- attr_accessor(attr)
68
- attr_md = add_attribute(attr, klass)
69
- attr_md.inverse = inverse
70
- logger.debug { "Created #{qp} owner attribute #{attr} with inverse #{klass.qp}.#{inverse}." }
71
- attr
72
- end
73
-
74
54
  # @return [Boolean] whether this class depends on an owner
75
55
  def dependent?
76
56
  not owners.empty?
@@ -98,6 +78,11 @@ module CaRuby
98
78
  def owner_attributes
99
79
  @oattrs ||= owner_attribute_metadata_enumerator.transform { |attr_md| attr_md.to_sym }
100
80
  end
81
+
82
+ # @return [Boolean] whether this {Resource} class is dependent and reference its owners
83
+ def bidirectional_dependent?
84
+ dependent? and not owner_attributes.empty?
85
+ end
101
86
 
102
87
  # @return [<Class>] this class's dependent types
103
88
  def dependents
@@ -5,7 +5,6 @@ module CaRuby
5
5
  module Domain
6
6
  # Meta-data mix-in to infer and set inverse attributes.
7
7
  module Inverse
8
-
9
8
  # Returns the inverse of the given attribute. If the attribute has an #{Attribute#inverse_metadata},
10
9
  # then that attribute's inverse is returned. Otherwise, if the attribute is an #{Attribute#owner?},
11
10
  # then the target class dependent attribute which matches this type is returned, if it exists.
@@ -5,6 +5,7 @@ require 'caruby/domain/introspection'
5
5
  require 'caruby/domain/inverse'
6
6
  require 'caruby/domain/dependency'
7
7
  require 'caruby/domain/attributes'
8
+ require 'caruby/json/deserializer'
8
9
 
9
10
  module CaRuby
10
11
  module Domain
@@ -13,7 +14,7 @@ module CaRuby
13
14
 
14
15
  # Adds introspected metadata to a Class.
15
16
  module Metadata
16
- include Introspection, Inverse, Dependency, Attributes
17
+ include Introspection, Inverse, Dependency, Attributes, JSON::Deserializer
17
18
 
18
19
  # @return [Module] the {Domain} module context
19
20
  attr_accessor :domain_module
@@ -139,6 +139,9 @@ module CaRuby
139
139
  # make a CSV loader which only converts input fields corresponding to non-String attributes
140
140
  @loader = CsvIO.new(@input, &method(:convert))
141
141
  logger.debug { "Migration data input file #{@input} headers: #{@loader.headers.qp}" }
142
+
143
+ # add shim modifiers
144
+ load_shims(@shims)
142
145
 
143
146
  # create the class => path => default value hash
144
147
  @def_hash = @def_files ? load_defaults_files(@def_files) : {}
@@ -151,6 +154,7 @@ module CaRuby
151
154
  # create the path => class => header hash
152
155
  @header_map = create_header_map(fld_map)
153
156
  # add missing owner classes (copy the keys rather than using each_key since the hash is updated)
157
+ @owners = Set.new
154
158
  @cls_paths_hash.keys.each { |klass| add_owners(klass) }
155
159
  # order the creatable classes by dependency, owners first, to smooth the migration process
156
160
  @creatable_classes = @cls_paths_hash.keys.sort! { |klass, other| other.depends_on?(klass) ? -1 : (klass.depends_on?(other) ? 1 : 0) }
@@ -169,9 +173,6 @@ module CaRuby
169
173
  logger.info { "Migration creatable classes: #{@creatable_classes.qp}." }
170
174
  unless @def_hash.empty? then logger.info { "Migration defaults: #{@def_hash.qp}." } end
171
175
 
172
- # add shim modifiers
173
- load_shims(@shims)
174
-
175
176
  # the class => attribute migration methods hash
176
177
  create_migration_method_hashes
177
178
 
@@ -208,10 +209,14 @@ module CaRuby
208
209
  end
209
210
  end
210
211
 
211
- # Adds missing klass owner classes to the migration class path hash (with empty paths).
212
+ # Adds missing owner classes to the migration class path hash (with empty paths)
213
+ # for the the given migration class.
214
+ #
215
+ # @param [Class] klass the migration class
212
216
  def add_owners(klass)
213
217
  owner = missing_owner_for(klass) || return
214
218
  logger.debug { "Migrator adding #{klass.qp} owner #{owner}" }
219
+ @owners << owner
215
220
  @cls_paths_hash[owner] = Array::EMPTY_ARRAY
216
221
  add_owners(owner)
217
222
  end
@@ -312,7 +317,7 @@ module CaRuby
312
317
  # call the migration method
313
318
  Proc.new { |obj, value, row| obj.send(mth, value, row) }
314
319
  end
315
- else
320
+ elsif proc then
316
321
  # call the filter
317
322
  Proc.new { |obj, value, row| proc.call(value) }
318
323
  end
@@ -350,32 +355,64 @@ module CaRuby
350
355
  proc_hash
351
356
  end
352
357
 
358
+ # Builds a proc that filters the input value. The config filter mapping entry is one of the following:
359
+ # * literal: literal
360
+ # * regexp: literal
361
+ # * regexp: template
362
+ #
363
+ # The regexp template can include match references (+$1+, +$2+, etc.) corresponding to the regexp captures.
364
+ # If the input value equals a literal, then the mapped literal is returned. Otherwise, if the input value
365
+ # matches a regexp, then the mapped transformation is returned after reference substitution. Otherwise,
366
+ # the input value is returned unchanged.
367
+ #
368
+ # For example, the config:
369
+ # /(\d{1,2})\/x\/(\d{1,2})/: $1/1/$2
370
+ # n/a: ~
371
+ # converts the input value as follows:
372
+ # 3/12/02 => 3/12/02 (no match)
373
+ # 5/x/04 => 5/1/04
374
+ # n/a => nil
375
+ #
353
376
  # @param [{Object => Object}] filter the config value mapping
354
377
  # @return [Proc] the filter migration block
378
+ # @raise [MigrationError] if the filter includes a regexp option other than +i+ (case-insensitive)
355
379
  def to_filter_proc(filter)
356
- # Split the filter into a straight value => value hash and a regexp => value hash.
380
+ # Split the filter into a straight value => value hash and a pattern => value hash.
357
381
  ph, vh = filter.split { |k, v| k =~ REGEXP_PAT }
382
+ # The Regexp => value hash is built from the pattern => value hash.
358
383
  reh = {}
359
384
  ph.each do |k, v|
385
+ # The /pattern/opts string is parsed to the pattern and options.
360
386
  pat, opt = REGEXP_PAT.match(k).captures
387
+ # Convert the regexp i option character to a Regexp initializer parameter.
361
388
  reopt = if opt then
362
389
  case opt
363
390
  when 'i' then Regexp::IGNORECASE
364
391
  else raise MigrationError.new("Migration value filter regular expression #{k} qualifier not supported: expected 'i', found '#{opt}'")
365
392
  end
366
393
  end
394
+ # the Regexp object
367
395
  re = Regexp.new(pat, reopt)
368
- reh[re] = v.gsub(/\$\d/, '%s') if String === v
396
+ # The regexp value can include match references ($1, $2, etc.). In that case, replace the $
397
+ # match reference with a %s print reference, since the filter formats the matching input value.
398
+ reh[re] = String === v ? v.gsub(/\$\d/, '%s') : v
369
399
  end
400
+ # The new proc matches preferentially on the literal value, then the first matching regexp.
401
+ # If no match on either a literal or a regexp, then the value is preserved.
370
402
  Proc.new do |value|
371
403
  if vh.has_key?(value) then
372
404
  vh[value]
373
405
  else
374
- # the first regex which matches the value
406
+ # The first regex which matches the value.
375
407
  regexp = reh.detect_key { |re| value =~ re }
376
408
  # If there is a match, then apply the filter to the match data.
377
409
  # Otherwise, pass the value through unmodified.
378
- regexp ? (reh[regexp] % $~.captures) : value
410
+ if regexp then
411
+ v = reh[regexp]
412
+ String === v ? v % $~.captures : v
413
+ else
414
+ value
415
+ end
379
416
  end
380
417
  end
381
418
  end
@@ -510,7 +547,18 @@ module CaRuby
510
547
  logger.debug { "Invalidated migrated #{obj} since it does not have a valid owner." }
511
548
  end
512
549
  end
513
- valid
550
+
551
+ # Go back through the valid objects in reverse dependency order to invalidate owners
552
+ # created only to hold a dependent which was subsequently invalidated.
553
+ valid.reject do |obj|
554
+ if @owners.include?(obj.class) and obj.dependents.all? { |dep| invalid.include?(dep) } then
555
+ # clear all references from the invalidated owner
556
+ obj.class.domain_attributes.each_metadata { |attr_md| obj.clear_attribute(attr_md.to_sym) }
557
+ invalid << obj
558
+ logger.debug { "Invalidated #{obj.qp} since it was created solely to hold subsequently invalidated dependents." }
559
+ true
560
+ end
561
+ end
514
562
  end
515
563
 
516
564
  # Returns whether the given domain object satisfies at least one of the following conditions:
@@ -7,6 +7,7 @@ require 'caruby/util/collection'
7
7
  require 'caruby/domain'
8
8
  require 'caruby/domain/mixin'
9
9
  require 'caruby/domain/merge'
10
+ require 'caruby/json/serializer'
10
11
  require 'caruby/domain/reference_visitor'
11
12
  require 'caruby/database/persistable'
12
13
  require 'caruby/domain/inversible'
@@ -19,7 +20,7 @@ module CaRuby
19
20
  # This module defines essential common domain methods that enable the jRuby-Java API bridge.
20
21
  # Classes which include Domain must implement the +metadata+ Domain::Metadata accessor method.
21
22
  module Resource
22
- include Mergeable, Migratable, Persistable, Inversible
23
+ include Mergeable, Migratable, Persistable, Inversible, JSON::Serializer
23
24
 
24
25
  # @quirk JRuby Bug #5090 - JRuby 1.5 object_id is no longer a reserved method, and results
25
26
  # in a String value rather than an Integer (cf. http://jira.codehaus.org/browse/JRUBY-5090).
@@ -581,6 +582,9 @@ module CaRuby
581
582
  logger.error("Validation of #{qp} unsuccessful - missing #{invalid.join(', ')}:\n#{dump}")
582
583
  raise ValidationError.new("Required attribute value missing for #{self}: #{invalid.join(', ')}")
583
584
  end
585
+ if self.class.bidirectional_dependent? and not owner then
586
+ raise ValidationError.new("Dependent #{self} does not reference an owner")
587
+ end
584
588
  end
585
589
 
586
590
  # Enumerates the dependents for setting defaults. Subclasses can override if the
@@ -241,8 +241,8 @@ module Enumerable
241
241
 
242
242
  alias :wrap :transform
243
243
 
244
- def join(other)
245
- Joiner.new(self, other)
244
+ def join(sep = $,)
245
+ to_a.join(sep)
246
246
  end
247
247
 
248
248
  # Sorts this collection's members with a partial sort operator, i.e. the comparison returns -1, 0, 1 or nil.
@@ -1,3 +1,3 @@
1
1
  module CaRuby
2
- VERSION = "1.5.4"
2
+ VERSION = "1.5.5"
3
3
  end
@@ -0,0 +1,14 @@
1
+ # Add the Java jar file to the Java path.
2
+ require 'test/fixtures/caruby/import/ext/bin/mixed_case.jar'
3
+
4
+ require 'java'
5
+ require "test/unit"
6
+
7
+ # Verifies whether JRuby supports a mixed-case package. This can occur in caBIG applications, e.g.
8
+ # the +caTissue+ PSBIN custom dynamic extensions. Work-around is to
9
+ class MixedCaseTest < Test::Unit::TestCase
10
+ def test_import
11
+ assert_raises(NameError, "Mixed-case package not resolved") { Java::mixed.Case.Example }
12
+ assert_nothing_raised("Mixed-case JRuby module not resolved") { Java::MixedCase::Example }
13
+ end
14
+ end
@@ -77,6 +77,11 @@ class CollectionTest < Test::Unit::TestCase
77
77
  assert_equal([1, 2, 3, 4, 5], base.to_a, 'Filter does not modify the base')
78
78
  end
79
79
 
80
+ def test_enum_join
81
+ assert_equal("1", [1].filter { true }.join, "Enumerable singleton join incorrect")
82
+ assert_equal("1,2", [1, 2].filter { true }.join(','), "Enumerable join incorrect")
83
+ end
84
+
80
85
  def test_array_filter_without_block
81
86
  assert_equal([1, 3], [1, nil, 3, false].filter.to_a, 'Filter incorrect')
82
87
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caruby-core
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 5
9
- - 4
10
- version: 1.5.4
9
+ - 5
10
+ version: 1.5.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - OHSU
@@ -61,7 +61,7 @@ dependencies:
61
61
  type: :runtime
62
62
  version_requirements: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: uom
64
+ name: json
65
65
  prerelease: false
66
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
67
  none: false
@@ -74,6 +74,20 @@ dependencies:
74
74
  version: "0"
75
75
  type: :runtime
76
76
  version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: uom
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ type: :runtime
90
+ version_requirements: *id005
77
91
  description: " caRuby is a JRuby facade for interaction with caBIG applications.\n"
78
92
  email: caruby.org@gmail.com
79
93
  executables: []
@@ -166,6 +180,7 @@ files:
166
180
  - test/lib/caruby/domain/inversible_test.rb
167
181
  - test/lib/caruby/domain/reference_visitor_test.rb
168
182
  - test/lib/caruby/import/java_test.rb
183
+ - test/lib/caruby/import/mixed_case_test.rb
169
184
  - test/lib/caruby/migration/test_case.rb
170
185
  - test/lib/caruby/test_case.rb
171
186
  - test/lib/caruby/util/cache_test.rb