caruby-core 1.5.5 → 2.1.1

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.
Files changed (126) hide show
  1. data/Gemfile +9 -0
  2. data/History.md +5 -1
  3. data/lib/caruby.rb +3 -5
  4. data/lib/caruby/caruby-src.tar.gz +0 -0
  5. data/lib/caruby/database.rb +53 -69
  6. data/lib/caruby/database/application_service.rb +25 -0
  7. data/lib/caruby/database/cache.rb +60 -0
  8. data/lib/caruby/database/fetched_matcher.rb +52 -38
  9. data/lib/caruby/database/lazy_loader.rb +4 -4
  10. data/lib/caruby/database/operation.rb +34 -0
  11. data/lib/caruby/database/persistable.rb +171 -86
  12. data/lib/caruby/database/persistence_service.rb +32 -34
  13. data/lib/caruby/database/persistifier.rb +100 -43
  14. data/lib/caruby/database/reader.rb +107 -85
  15. data/lib/caruby/database/reader_template_builder.rb +60 -0
  16. data/lib/caruby/database/saved_matcher.rb +3 -3
  17. data/lib/caruby/database/sql_executor.rb +88 -17
  18. data/lib/caruby/database/writer.rb +213 -177
  19. data/lib/caruby/database/writer_template_builder.rb +334 -0
  20. data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
  21. data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
  22. data/lib/caruby/{util → helpers}/person.rb +3 -3
  23. data/lib/caruby/{util → helpers}/properties.rb +7 -9
  24. data/lib/caruby/{util → helpers}/roman.rb +2 -2
  25. data/lib/caruby/{util → helpers}/version.rb +1 -1
  26. data/lib/caruby/json/deserializer.rb +2 -2
  27. data/lib/caruby/json/serializer.rb +49 -7
  28. data/lib/caruby/metadata.rb +30 -0
  29. data/lib/caruby/metadata/java_property.rb +21 -0
  30. data/lib/caruby/metadata/propertied.rb +191 -0
  31. data/lib/caruby/metadata/property.rb +22 -0
  32. data/lib/caruby/metadata/property_characteristics.rb +201 -0
  33. data/lib/caruby/migration/migratable.rb +11 -182
  34. data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
  35. data/lib/caruby/resource.rb +20 -823
  36. data/lib/caruby/version.rb +1 -1
  37. data/test/lib/caruby/database/cache_test.rb +54 -0
  38. data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
  39. data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
  40. data/test/lib/caruby/helpers/properties_test.rb +34 -0
  41. data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
  42. data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
  43. data/test/lib/helper.rb +7 -0
  44. metadata +161 -214
  45. data/lib/caruby/cli/application.rb +0 -36
  46. data/lib/caruby/cli/command.rb +0 -202
  47. data/lib/caruby/csv/csv_mapper.rb +0 -159
  48. data/lib/caruby/csv/csvio.rb +0 -203
  49. data/lib/caruby/database/search_template_builder.rb +0 -56
  50. data/lib/caruby/database/store_template_builder.rb +0 -278
  51. data/lib/caruby/domain.rb +0 -193
  52. data/lib/caruby/domain/attribute.rb +0 -584
  53. data/lib/caruby/domain/attributes.rb +0 -628
  54. data/lib/caruby/domain/dependency.rb +0 -225
  55. data/lib/caruby/domain/id_alias.rb +0 -22
  56. data/lib/caruby/domain/importer.rb +0 -183
  57. data/lib/caruby/domain/introspection.rb +0 -176
  58. data/lib/caruby/domain/inverse.rb +0 -172
  59. data/lib/caruby/domain/inversible.rb +0 -90
  60. data/lib/caruby/domain/java_attribute.rb +0 -173
  61. data/lib/caruby/domain/merge.rb +0 -185
  62. data/lib/caruby/domain/metadata.rb +0 -142
  63. data/lib/caruby/domain/mixin.rb +0 -35
  64. data/lib/caruby/domain/properties.rb +0 -95
  65. data/lib/caruby/domain/reference_visitor.rb +0 -428
  66. data/lib/caruby/domain/uniquify.rb +0 -50
  67. data/lib/caruby/import/java.rb +0 -387
  68. data/lib/caruby/migration/migrator.rb +0 -918
  69. data/lib/caruby/migration/resource_module.rb +0 -9
  70. data/lib/caruby/migration/uniquify.rb +0 -17
  71. data/lib/caruby/util/attribute_path.rb +0 -44
  72. data/lib/caruby/util/cache.rb +0 -56
  73. data/lib/caruby/util/class.rb +0 -149
  74. data/lib/caruby/util/collection.rb +0 -1152
  75. data/lib/caruby/util/domain_extent.rb +0 -46
  76. data/lib/caruby/util/file_separator.rb +0 -65
  77. data/lib/caruby/util/inflector.rb +0 -27
  78. data/lib/caruby/util/log.rb +0 -95
  79. data/lib/caruby/util/math.rb +0 -12
  80. data/lib/caruby/util/merge.rb +0 -59
  81. data/lib/caruby/util/module.rb +0 -18
  82. data/lib/caruby/util/options.rb +0 -97
  83. data/lib/caruby/util/partial_order.rb +0 -35
  84. data/lib/caruby/util/pretty_print.rb +0 -204
  85. data/lib/caruby/util/stopwatch.rb +0 -74
  86. data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
  87. data/lib/caruby/util/transitive_closure.rb +0 -55
  88. data/lib/caruby/util/tree.rb +0 -48
  89. data/lib/caruby/util/trie.rb +0 -37
  90. data/lib/caruby/util/uniquifier.rb +0 -30
  91. data/lib/caruby/util/validation.rb +0 -20
  92. data/lib/caruby/util/visitor.rb +0 -365
  93. data/lib/caruby/util/weak_hash.rb +0 -36
  94. data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
  95. data/test/lib/caruby/csv/csvio_test.rb +0 -69
  96. data/test/lib/caruby/database/persistable_test.rb +0 -92
  97. data/test/lib/caruby/domain/domain_test.rb +0 -112
  98. data/test/lib/caruby/domain/inversible_test.rb +0 -99
  99. data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
  100. data/test/lib/caruby/import/java_test.rb +0 -80
  101. data/test/lib/caruby/import/mixed_case_test.rb +0 -14
  102. data/test/lib/caruby/migration/test_case.rb +0 -102
  103. data/test/lib/caruby/test_case.rb +0 -230
  104. data/test/lib/caruby/util/cache_test.rb +0 -23
  105. data/test/lib/caruby/util/class_test.rb +0 -61
  106. data/test/lib/caruby/util/collection_test.rb +0 -398
  107. data/test/lib/caruby/util/command_test.rb +0 -55
  108. data/test/lib/caruby/util/domain_extent_test.rb +0 -60
  109. data/test/lib/caruby/util/file_separator_test.rb +0 -30
  110. data/test/lib/caruby/util/inflector_test.rb +0 -12
  111. data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
  112. data/test/lib/caruby/util/merge_test.rb +0 -83
  113. data/test/lib/caruby/util/module_test.rb +0 -25
  114. data/test/lib/caruby/util/options_test.rb +0 -59
  115. data/test/lib/caruby/util/partial_order_test.rb +0 -42
  116. data/test/lib/caruby/util/pretty_print_test.rb +0 -85
  117. data/test/lib/caruby/util/properties_test.rb +0 -50
  118. data/test/lib/caruby/util/stopwatch_test.rb +0 -18
  119. data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
  120. data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
  121. data/test/lib/caruby/util/tree_test.rb +0 -23
  122. data/test/lib/caruby/util/trie_test.rb +0 -14
  123. data/test/lib/caruby/util/visitor_test.rb +0 -278
  124. data/test/lib/caruby/util/weak_hash_test.rb +0 -45
  125. data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
  126. data/test/lib/examples/clinical_trials/migration/test_case.rb +0 -38
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ group :development do
5
+ #gem 'jinx', :path => File.dirname(__FILE__) + '/../../jinx/core'
6
+ #gem 'jinx-json', :path => File.dirname(__FILE__) + '/../../jinx/json'
7
+ #gem 'jinx-migrate', :path => File.dirname(__FILE__) + '/../../jinx/migrate'
8
+ gem 'caruby-core', :path => File.dirname(__FILE__), :require => 'caruby'
9
+ end
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-09-22
4
+ 2.1.1 / 2011-04-13
5
+ ------------------
6
+ * Simpler, more flexible meta-data loading.
7
+
8
+ 1.5.5 / 2011-09-22
5
9
  ------------------
6
10
  * Support mixed-case custom DE package name.
7
11
 
data/lib/caruby.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'caruby/resource'
2
-
3
- # CaRuby is a JRuby facade for the caBIG application APIs.
4
- # See the [http://http://caruby.rubyforge.org/](caRuby) home page for more information.
5
- module CaRuby;end
1
+ require 'jinx'
2
+ require 'jinx/helpers/log'
3
+ require 'jinx/helpers/error'
Binary file
@@ -1,20 +1,17 @@
1
1
  require 'generator'
2
- require 'caruby/util/log'
3
- require 'caruby/util/collection'
4
- require 'caruby/util/validation'
5
- require 'caruby/util/options'
6
- require 'caruby/util/visitor'
7
- require 'caruby/util/inflector'
2
+ require 'jinx/helpers/collection'
3
+ require 'jinx/helpers/validation'
4
+ require 'jinx/helpers/options'
5
+ require 'jinx/helpers/visitor'
6
+ require 'jinx/helpers/inflector'
8
7
  require 'caruby/database/persistable'
9
8
  require 'caruby/database/persistence_service'
9
+ require 'caruby/database/operation'
10
10
  require 'caruby/database/reader'
11
11
  require 'caruby/database/writer'
12
12
  require 'caruby/database/persistifier'
13
13
 
14
14
  module CaRuby
15
- # The caBIG client session class.
16
- java_import Java::gov.nih.nci.system.comm.client.ClientSession
17
-
18
15
  # Database operation error.
19
16
  class DatabaseError < RuntimeError; end
20
17
 
@@ -39,36 +36,27 @@ module CaRuby
39
36
  # The {Writer#save} method creates or updates references as necessary to persist its argument domain object.
40
37
  # It is not necessary to fetch references first or follow dependency ordering rules, which can be
41
38
  # implicit and tortuous in caBIG applications. Build the object you want to persist and call the
42
- # store method. {Resource} sets reasonable default values, recognizes application dependencies and steers
39
+ # store method. {Jinx::Resource} sets reasonable default values, recognizes application dependencies and steers
43
40
  # around caBIG idiosyncracies to the extent possible.
44
41
  class Database
45
42
  include Reader, Writer, Persistifier
46
43
 
47
- # Database CRUD operation.
48
- class Operation
49
- attr_reader :type, :subject, :attribute
50
-
51
- # @param [:find, :query, :create, :udate, :delete] type the database operation type
52
- # @param [Persistable] subject the domain object on which the operation is performed
53
- # @param [{Symbol => Object}, Symbol, nil] the operation characteristics
54
- # @option opts [Symbol] :attribute the query attribute
55
- # @option opts [Boolean] :autogenerated whether this is an auto-generated subject update
56
- def initialize(type, subject, opts=nil)
57
- @type = type
58
- @subject = subject
59
- @attribute = Options.get(:attribute, opts)
60
- @autogenerated = Options.get(:autogenerated, opts, false)
61
- end
62
-
63
- # @return [Boolean] whether this operation is an update of an auto-generated subject
64
- def autogenerated?
65
- @autogenerated
66
- end
67
-
68
- def to_s
69
- "#{@subject.qp} #{attribute} #{type}"
70
- end
71
- end
44
+ # The application and database connection access command line options.
45
+ ACCESS_OPTS = [
46
+ [:user, "--user USER", "the application login user"],
47
+ [:password, "--password PSWD", "the application login password"],
48
+ [:host, "--host HOST", "the application host name"],
49
+ [:port, "--port PORT", "the application port number"],
50
+ [:classpath, "--classpath PATH", "the application client classpath"],
51
+ [:database_host, "--database_host HOST", "the database host name"],
52
+ [:database_type, "--database_type TYPE", "the database type (mysql or oracle)"],
53
+ [:database_driver, "--database_driver DRIVER", "the database driver string"],
54
+ [:database_driver_class, "--database_driver_class CLASS", "the database driver class name"],
55
+ [:database_port, "--database_port PORT", Integer, "the database port number"],
56
+ [:database, "--database NAME", "the database name"],
57
+ [:database_user, "--database_user USER", "the database login user"],
58
+ [:database_password, "--database_password PSWD", "the database login password"]
59
+ ]
72
60
 
73
61
  attr_reader :operations
74
62
 
@@ -95,24 +83,28 @@ module CaRuby
95
83
  # to the start_session method and run the +PSBIN::MigrationTest+ biopsy save test case.
96
84
  #
97
85
  # @param [String] service_name the name of the default {PersistenceService}
98
- # @param [{Symbol => String}] opts access options
86
+ # @param [{Symbol => String}, nil] opts the access options, or nil if specified as a block
99
87
  # @option opts [String] :host application service host name
100
88
  # @option opts [String] :login application service login user
101
89
  # @option opts [String] :password application service login password
90
+ # @yield the access options defined by a block rather than a parameter
102
91
  # @example
103
92
  # Database.new(:user => 'perdita', :password => 'changeMe')
104
- def initialize(service_name, opts)
93
+ def initialize(service_name, opts=nil)
105
94
  super()
95
+ # the options can be defined in a block
96
+ opts ||= yield if block_given?
97
+ # import the Java classes on demand
98
+ Database.import_java_classes
106
99
  # the fetched object cache
107
- @cache = create_cache
108
100
  @defaults = {}
101
+ if opts.nil? then Jinx.fail(ArgumentError, "Missing required database access properties") end
109
102
  @user = Options.get(:user, opts)
110
103
  @password = Options.get(:password, opts)
111
104
  host = Options.get(:host, opts)
112
105
  port = Options.get(:port, opts)
113
106
  # class => service hash; default is the catissuecore app service
114
107
  @def_persist_svc = PersistenceService.new(service_name, :host => host, :port => port)
115
- @def_persist_svc.app_service
116
108
  @persistence_services = [@def_persist_svc].to_set
117
109
  @cls_svc_hash = Hash.new(@def_persist_svc)
118
110
  # the create/update nested operations
@@ -132,11 +124,6 @@ module CaRuby
132
124
  # call the block and close when done
133
125
  yield(self) ensure close
134
126
  end
135
-
136
- # Clears the cache.
137
- def clear
138
- @cache.clear
139
- end
140
127
 
141
128
  # Releases database resources. This method should be called when database interaction
142
129
  # is completed.
@@ -161,15 +148,17 @@ module CaRuby
161
148
  end
162
149
  end
163
150
 
164
- # Returns the PersistanceService to use for the given domain object.
151
+ # Returns the PersistanceService to use for the given {Jinx::Resource} class.
165
152
  # This base method always returns the standard application service.
166
- # Subclasses can override for specialized services. A session is
167
- # started on demand if necessary.
153
+ # Subclasses can override for specialized services. A session is started
154
+ # on demand if necessary.
168
155
  #
169
- # @param [Persistable, Class] obj the domain object or {Resource} class
170
- # @return [PersistanceService] the service for the domain object
156
+ # @param [Class] klass the domain object class
157
+ # @return [PersistanceService] the corresponding service
171
158
  def persistence_service(klass)
172
- unless Class === klass then raise ArgumentError.new("#{self} persistence_service argument is not a Class: {#klass.qp}") end
159
+ unless Class === klass then
160
+ Jinx.fail(ArgumentError, "#{self} persistence_service argument is not a Class: {#klass.qp}")
161
+ end
173
162
  start_session if @session.nil?
174
163
  @def_persist_svc
175
164
  end
@@ -188,12 +177,18 @@ module CaRuby
188
177
  ## Utility classes and methods, used by Query and Store mix-ins ##
189
178
 
190
179
  private
180
+
181
+ # Imports this class's Java classes on demand.
182
+ def self.import_java_classes
183
+ # The caBIG client session class.
184
+ java_import Java::gov.nih.nci.system.comm.client.ClientSession unless const_defined?(:ClientSession)
185
+ end
191
186
 
192
187
  # A mergeable autogenerated operation is recursively defined as:
193
188
  # * a create of an object with auto-generated dependents
194
189
  # * an update of an auto-generated dependent in the context of a mergeable autogenerated operation
195
190
  #
196
- # @return whether the innermost operation conforms to the above criterion
191
+ # @return [Boolean] whether the innermost operation conforms to the above criterion
197
192
  def mergeable_autogenerated_operation?
198
193
  # the inner operation subject
199
194
  inner = nil
@@ -219,14 +214,14 @@ module CaRuby
219
214
  # Lazy loading is suspended during the operation.
220
215
  #
221
216
  # @param [:find, :query, :create, :udate, :delete] op the database operation type
222
- # @param [Resource] obj the domain object on which the operation is performed
217
+ # @param [Jinx::Resource] obj the domain object on which the operation is performed
223
218
  # @param opts (#see Operation#initialize)
224
219
  # @yield the database operation block
225
220
  # @return the result of calling the operation block
226
221
  def perform(op, obj, opts=nil)
227
222
  op_s = op.to_s.capitalize_first
228
- attr = Options.get(:attribute, opts)
229
- attr_s = " #{attr}" if attr
223
+ pa = Options.get(:attribute, opts)
224
+ attr_s = " #{pa}" if pa
230
225
  ag_s = " autogenerated" if Options.get(:autogenerated, opts)
231
226
  ctxt_s = " in context #{print_operations}" unless @operations.empty?
232
227
  logger.info(">> #{op_s}#{ag_s} #{obj.pp_s(:single_line)}#{attr_s}#{ctxt_s}...")
@@ -248,22 +243,10 @@ module CaRuby
248
243
  ObjectSpace.each_object(PersistenceService, &block)
249
244
  end
250
245
 
251
- # @return [Cache] a new object cache.
252
- def create_cache
253
- # @quirk JRuby identifier is not a stable object when fetched from the database, i.e.:
254
- # obj.identifier.equal?(obj.identifier) #=> false
255
- # This is probably an artifact of jRuby Numeric - Java Long conversion interaction
256
- # combined with hash access use of the eql? method. Work-around is to make a Ruby Integer.
257
- Cache.new do |obj|
258
- raise ArgumentError.new("Can't cache object without identifier: #{obj}") unless obj.identifier
259
- obj.identifier.to_s.to_i
260
- end
261
- end
262
-
263
246
  # Initializes the default application service.
264
247
  def start_session
265
- if @user.nil? then raise DatabaseError.new('Application user option missing') end
266
- if @password.nil? then raise DatabaseError.new('Application password option missing') end
248
+ if @user.nil? then Jinx.fail(DatabaseError, 'Application user option missing') end
249
+ if @password.nil? then Jinx.fail(DatabaseError, 'Application password option missing') end
267
250
  @session = ClientSession.instance
268
251
  connect(@user, @password)
269
252
  end
@@ -283,7 +266,8 @@ module CaRuby
283
266
  begin
284
267
  @session.start_session(user, password)
285
268
  rescue Exception => e
286
- logger.error("Login of #{user} unsuccessful - #{e.message}") and raise
269
+ logger.error("Login of #{user} unsuccessful - #{e.message}")
270
+ raise e
287
271
  end
288
272
  logger.info("Connected to application server.")
289
273
  end
@@ -0,0 +1,25 @@
1
+ module CaRuby
2
+ # An ApplicationService wraps a caCORE application service.
3
+ class ApplicationService
4
+ # @quirk caCORE When more than one application service is used, then the remote
5
+ # instance mustb e reinitialized every time a different application service
6
+ # is used.
7
+ #
8
+ # @param [String] the service URL
9
+ # @return the caCORE application service remote instance
10
+ def self.for(url)
11
+ # Load the Java class on demand the first time this method is called.
12
+ if @url.nil? then
13
+ java_import Java::gov.nih.nci.system.applicationservice.ApplicationServiceProvider
14
+ end
15
+ # If the url differs from the current remote instance, then reinitialize.
16
+ unless @url == url then
17
+ @url = url
18
+ logger.debug { "Connecting to service provider at #{@url}..." }
19
+ @current = ApplicationServiceProvider.remote_instance(@url)
20
+ end
21
+ @current
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,60 @@
1
+ require 'jinx/helpers/lazy_hash'
2
+ require 'jinx/helpers/key_transformer_hash'
3
+
4
+ module CaRuby
5
+ # Cache for objects held in memory and accessed by key.
6
+ class Cache
7
+ # The classes which are not cleared when {#clear} is called without the +all+ flag.
8
+ attr_reader :sticky
9
+
10
+ # @yield [item] returns the key for the given item to cache
11
+ # @yieldparam item the object to cache
12
+ def initialize
13
+ # Make the class => {key => item} hash.
14
+ # The {key => item} hash takes an item as an argument and converts
15
+ # it to the key by calling the block given to this initializer.
16
+ @ckh = Jinx::LazyHash.new do
17
+ Jinx::KeyTransformerHash.new do |obj|
18
+ yield(obj) or Jinx.fail(ArgumentError, "The object to cache does not have a key: #{obj}")
19
+ end
20
+ end
21
+ @sticky = Set.new
22
+ end
23
+
24
+ # If there is already a cached object with the same key as the given item,
25
+ # then this method returns that cached object. Otherwise, this method caches
26
+ # the given item and returns that item.
27
+ #
28
+ # @param item the object to resolve
29
+ # @return the object cached with the same class and key as the given item
30
+ # @raise [ArgumentError] if the item does not have a key
31
+ def [](item)
32
+ @ckh[item.class][item]
33
+ end
34
+
35
+ # Adds the given item to this cache, unless one already exists.
36
+ #
37
+ # @param item the object to cache
38
+ # @return the cached item
39
+ def add(item)
40
+ @ckh[item.class][item] ||= item
41
+ end
42
+
43
+ # Adds the given item to this cache. Overwrites an existing cache entry
44
+ # for the given item's key, if one already exists.
45
+ #
46
+ # @param item the object to cache
47
+ def add!(item)
48
+ @ckh[item.class][item] = item
49
+ end
50
+
51
+ # Clears the non-sticky class caches.
52
+ def clear
53
+ if @sticky.empty? then
54
+ @ckh.clear
55
+ else
56
+ @ckh.each { |klass, ch| ch.clear unless @sticky.include?(klass) }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,58 +1,72 @@
1
- require 'caruby/util/options'
2
- require 'caruby/util/collection'
1
+ require 'jinx/helpers/options'
2
+ require 'jinx/helpers/collection'
3
3
 
4
4
  module CaRuby
5
5
  class Database
6
- # Proc that matches fetched sources to targets.
6
+ # Helper that matches fetched sources to targets.
7
7
  class FetchedMatcher
8
- # Initializes a new FetchedMatcher.
9
- def match(srcs, tgts)
10
- match_fetched(srcs, tgts)
8
+ # Returns a target => source match hash for the given targets and sources using
9
+ # {Jinx::Resource#match_in_owner_scope}.
10
+ #
11
+ # caCORE alert = caCORE does not enforce reference identity integrity, i.e. a search on object _a_
12
+ # with database record references _a_ => _b_ => _a_, the search result might be _a_ => _b_ => _a'_,
13
+ # where _a.identifier_ == _a'.identifier_. This visit method remedies this caCORE defect by matching
14
+ # source references on a previously matched identifier where possible.
15
+ #
16
+ # @param [<Jinx::Resource>] sources the domain objects to match against
17
+ # @param [<Jinx::Resource>] targets the domain objects to match
18
+ # @param [Resource] from the visiting domain object
19
+ # @param [Symbol] attribute the visiting attribute
20
+ # @return [{Jinx::Resource => Jinx::Resource}] the source => target matches
21
+ def match(sources, targets, from, attribute)
22
+ if sources.empty? or targets.empty? then
23
+ Hash::EMPTY_HASH
24
+ elsif from.class.property(attribute).owner? then
25
+ match_owner(sources.first, targets.first, from, attribute)
26
+ else
27
+ match_fetched(sources, targets)
28
+ end
11
29
  end
12
30
 
13
- alias :call :match
14
-
15
31
  private
32
+
33
+ # @param [<Jinx::Resource>] source the fetched owner to match against
34
+ # @param [<Jinx::Resource>] target the owner to match
35
+ # @param [Resource] dependent the visiting dependent
36
+ # @param attribute (see #match)
37
+ # @return [{Resource => Resource}] the source => target singleton hash, if the source
38
+ # and target identifiers don't conflict, otherwise an empty hash
39
+ def match_owner(source, target, dependent, attribute)
40
+ return Hash::EMPTY_HASH unless source.minimal_match?(target)
41
+ logger.debug { "Matched #{dependent} #{attribute} owner #{source} to #{target}." }
42
+ {source => target}
43
+ end
16
44
 
17
- # Returns a target => source match hash for the given targets and sources using
18
- # {Resource#match_in_owner_scope}.
19
- #
20
- # @param [<Resource>] sources the domain objects to match with targets
21
- # @param [<Resource>] targets the domain objects to match with targets
22
- # @return [{Resource => Resource}] the source => target matches
23
- def match_fetched(sources, targets)
24
- return Hash::EMPTY_HASH if sources.empty? or targets.empty?
25
- # the domain class
26
- klass = sources.first.class
27
- # the non-owner secondary key domain attributes
28
- attrs = klass.secondary_key_attributes.select do |attr|
29
- attr_md = klass.attribute_metadata(attr)
30
- attr_md.domain? and not attr_md.owner?
31
- end
32
-
33
- # fetch the non-owner secondary key domain attributes as necessary
34
- unless attrs.empty? then
35
- sources.each do |src|
36
- attrs.each do |attr|
37
- next if src.send(attr)
38
- logger.debug { "Fetching #{src.qp} #{attr} in order to match on the secondary key..." }
39
- ref = src.query(attr).first || next
40
- src.set_attribute(attr, ref)
41
- logger.debug { "Set fetched #{src.qp} secondary key attribute #{attr} to fetched #{ref}." }
42
- end
45
+ # Fetches the given domain objects non-owner secondary key domain attributes as necessary.
46
+ def fetch_secondary_key_references(sources)
47
+ # fetch the secondary key reference if necessary
48
+ sources.each do |src|
49
+ src.class.secondary_key_non_owner_domain_attributes.each do |pa|
50
+ next if src.send(pa)
51
+ logger.debug { "Fetching #{src.qp} #{pa} in order to match on the secondary key..." }
52
+ ref = src.query(pa).first || next
53
+ src.set_property_value(pa, ref)
54
+ logger.debug { "Set fetched #{src.qp} secondary key attribute #{pa} to fetched #{ref}." }
43
55
  end
44
56
  end
45
-
57
+ end
58
+
59
+ def match_fetched(sources, targets)
60
+ # complete the key
61
+ fetch_secondary_key_references(sources)
46
62
  # match source => target based on the secondary key
47
63
  unmatched = Set === sources ? sources.dup : sources.to_set
48
64
  matches = {}
49
65
  targets.each do |tgt|
50
- src = tgt.match_in_owner_scope(unmatched)
51
- next unless src
66
+ src = tgt.match_in_owner_scope(unmatched) || next
52
67
  matches[src] = tgt
53
68
  unmatched.delete(src)
54
69
  end
55
-
56
70
  matches
57
71
  end
58
72
  end