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 +5 -1
- data/lib/caruby/database/reader.rb +19 -15
- data/lib/caruby/database/saved_matcher.rb +1 -1
- data/lib/caruby/domain.rb +1 -148
- data/lib/caruby/domain/attributes.rb +19 -11
- data/lib/caruby/domain/dependency.rb +5 -20
- data/lib/caruby/domain/inverse.rb +0 -1
- data/lib/caruby/domain/metadata.rb +2 -1
- data/lib/caruby/migration/migrator.rb +58 -10
- data/lib/caruby/resource.rb +5 -1
- data/lib/caruby/util/collection.rb +2 -2
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/import/mixed_case_test.rb +14 -0
- data/test/lib/caruby/util/collection_test.rb +5 -0
- metadata +19 -4
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-
|
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
|
-
#
|
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
|
-
#
|
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
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
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
|
|
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
|
-
#
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
#
|
75
|
-
#
|
76
|
-
#
|
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
|
-
##
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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:
|
data/lib/caruby/resource.rb
CHANGED
@@ -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(
|
245
|
-
|
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.
|
data/lib/caruby/version.rb
CHANGED
@@ -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:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 1.5.
|
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:
|
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
|