caruby-core 1.5.4 → 1.5.5

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