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 +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
|