caruby-core 1.4.9 → 1.5.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.
- data/History.md +48 -0
 - data/lib/caruby/cli/command.rb +2 -1
 - data/lib/caruby/csv/csv_mapper.rb +8 -8
 - data/lib/caruby/database/persistable.rb +44 -65
 - data/lib/caruby/database/persistence_service.rb +12 -9
 - data/lib/caruby/database/persistifier.rb +14 -14
 - data/lib/caruby/database/reader.rb +53 -51
 - data/lib/caruby/database/search_template_builder.rb +9 -10
 - data/lib/caruby/database/store_template_builder.rb +58 -58
 - data/lib/caruby/database/writer.rb +96 -96
 - data/lib/caruby/database.rb +19 -19
 - data/lib/caruby/domain/attribute.rb +581 -0
 - data/lib/caruby/domain/attributes.rb +615 -0
 - data/lib/caruby/domain/dependency.rb +240 -0
 - data/lib/caruby/domain/importer.rb +183 -0
 - data/lib/caruby/domain/introspection.rb +176 -0
 - data/lib/caruby/domain/inverse.rb +173 -0
 - data/lib/caruby/domain/inversible.rb +1 -2
 - data/lib/caruby/domain/java_attribute.rb +173 -0
 - data/lib/caruby/domain/merge.rb +13 -10
 - data/lib/caruby/domain/metadata.rb +141 -0
 - data/lib/caruby/domain/mixin.rb +35 -0
 - data/lib/caruby/domain/reference_visitor.rb +5 -3
 - data/lib/caruby/domain.rb +340 -0
 - data/lib/caruby/import/java.rb +29 -25
 - data/lib/caruby/migration/migratable.rb +5 -5
 - data/lib/caruby/migration/migrator.rb +19 -15
 - data/lib/caruby/migration/resource_module.rb +1 -1
 - data/lib/caruby/resource.rb +39 -30
 - data/lib/caruby/util/collection.rb +94 -33
 - data/lib/caruby/util/coordinate.rb +28 -2
 - data/lib/caruby/util/log.rb +4 -4
 - data/lib/caruby/util/module.rb +12 -28
 - data/lib/caruby/util/partial_order.rb +9 -10
 - data/lib/caruby/util/pretty_print.rb +46 -26
 - data/lib/caruby/util/topological_sync_enumerator.rb +10 -4
 - data/lib/caruby/util/transitive_closure.rb +2 -2
 - data/lib/caruby/util/visitor.rb +1 -1
 - data/lib/caruby/version.rb +1 -1
 - data/test/lib/caruby/database/persistable_test.rb +1 -1
 - data/test/lib/caruby/domain/domain_test.rb +14 -28
 - data/test/lib/caruby/domain/inversible_test.rb +1 -1
 - data/test/lib/caruby/import/java_test.rb +5 -0
 - data/test/lib/caruby/migration/test_case.rb +0 -1
 - data/test/lib/caruby/test_case.rb +9 -10
 - data/test/lib/caruby/util/collection_test.rb +23 -5
 - data/test/lib/caruby/util/module_test.rb +10 -14
 - data/test/lib/caruby/util/partial_order_test.rb +16 -15
 - data/test/lib/caruby/util/visitor_test.rb +1 -1
 - data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +1 -1
 - metadata +16 -15
 - data/History.txt +0 -44
 - data/lib/caruby/domain/attribute_metadata.rb +0 -551
 - data/lib/caruby/domain/java_attribute_metadata.rb +0 -183
 - data/lib/caruby/domain/resource_attributes.rb +0 -565
 - data/lib/caruby/domain/resource_dependency.rb +0 -217
 - data/lib/caruby/domain/resource_introspection.rb +0 -160
 - data/lib/caruby/domain/resource_inverse.rb +0 -151
 - data/lib/caruby/domain/resource_metadata.rb +0 -155
 - data/lib/caruby/domain/resource_module.rb +0 -370
 - data/lib/caruby/yard/resource_metadata_handler.rb +0 -8
 
| 
         @@ -7,7 +7,7 @@ module CaRuby 
     | 
|
| 
       7 
7 
     | 
    
         
             
                # the template are determined by the block given to this initializer, filtered as follows:
         
     | 
| 
       8 
8 
     | 
    
         
             
                # * If the save operation is a create, then exclude the auto-generated attributes.
         
     | 
| 
       9 
9 
     | 
    
         
             
                # * If the visited object has an identifier, then include only those attributes
         
     | 
| 
       10 
     | 
    
         
            -
                #   which { 
     | 
| 
      
 10 
     | 
    
         
            +
                #   which {Attribute#cascade_update_to_create?} or have an identifier. 
         
     | 
| 
       11 
11 
     | 
    
         
             
                #
         
     | 
| 
       12 
12 
     | 
    
         
             
                # @param [Database] database the target database 
         
     | 
| 
       13 
13 
     | 
    
         
             
                # @yield [ref] the required selector block which determines which attributes are copied into the template
         
     | 
| 
         @@ -21,23 +21,23 @@ module CaRuby 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  # the mergeable attributes filter the given block with exclusions
         
     | 
| 
       22 
22 
     | 
    
         
             
                  @mergeable = Proc.new { |obj| mergeable_attributes(obj, yield(obj)) }
         
     | 
| 
       23 
23 
     | 
    
         
             
                  # the storable prerequisite reference visitor
         
     | 
| 
       24 
     | 
    
         
            -
                  @prereq_vstr = ReferenceVisitor.new(:prune_cycle) { |ref|  
     | 
| 
      
 24 
     | 
    
         
            +
                  @prereq_vstr = ReferenceVisitor.new(:prune_cycle) { |ref| savable_template_attributes(ref) }
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                  # the savable attributes filter the given block with exclusions
         
     | 
| 
       27 
27 
     | 
    
         
             
                  savable = Proc.new { |obj| savable_attributes(obj, yield(obj)) }
         
     | 
| 
       28 
28 
     | 
    
         
             
                  # the domain attributes to copy is determined by the constructor caller
         
     | 
| 
       29 
     | 
    
         
            -
                  # caTissue  
     | 
| 
      
 29 
     | 
    
         
            +
                  # @quirk caTissue must copy all of the non-domain attributes rather than just the identifier,
         
     | 
| 
       30 
30 
     | 
    
         
             
                  # since caTissue auto-generated Specimen update requires the parent collection status. This
         
     | 
| 
       31 
31 
     | 
    
         
             
                  # is the only known occurrence of a referenced object required non-identifier attribute.
         
     | 
| 
       32 
32 
     | 
    
         
             
                  # The copy attributes are parameterized by the top-level save target.
         
     | 
| 
       33 
33 
     | 
    
         
             
                  copier = Proc.new do |src|
         
     | 
| 
       34 
     | 
    
         
            -
                     
     | 
| 
       35 
     | 
    
         
            -
                    logger.debug { "Store template builder copied #{src.qp} into #{ 
     | 
| 
       36 
     | 
    
         
            -
                    copy_proxied_save_references(src,  
     | 
| 
       37 
     | 
    
         
            -
                     
     | 
| 
      
 34 
     | 
    
         
            +
                    tgt = src.copy
         
     | 
| 
      
 35 
     | 
    
         
            +
                    logger.debug { "Store template builder copied #{src.qp} into #{tgt}." }
         
     | 
| 
      
 36 
     | 
    
         
            +
                    copy_proxied_save_references(src, tgt)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    tgt
         
     | 
| 
       38 
38 
     | 
    
         
             
                  end
         
     | 
| 
       39 
39 
     | 
    
         
             
                  # the template copier
         
     | 
| 
       40 
     | 
    
         
            -
                  @copy_vstr = CopyVisitor.new(:mergeable => savable, :copier => copier) { |ref|  
     | 
| 
      
 40 
     | 
    
         
            +
                  @copy_vstr = CopyVisitor.new(:mergeable => savable, :copier => copier) { |ref| savable_template_attributes(ref) }
         
     | 
| 
       41 
41 
     | 
    
         
             
                end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                # Returns a new domain object which serves as the argument for obj create or update.
         
     | 
| 
         @@ -47,31 +47,31 @@ module CaRuby 
     | 
|
| 
       47 
47 
     | 
    
         
             
                # to store obj. The template object graph contains only those references which are
         
     | 
| 
       48 
48 
     | 
    
         
             
                # essential to the store operation.
         
     | 
| 
       49 
49 
     | 
    
         
             
                #
         
     | 
| 
       50 
     | 
    
         
            -
                # caCORE  
     | 
| 
       51 
     | 
    
         
            -
                # 
     | 
| 
       52 
     | 
    
         
            -
                # 
     | 
| 
       53 
     | 
    
         
            -
                # 
     | 
| 
       54 
     | 
    
         
            -
                # 
     | 
| 
       55 
     | 
    
         
            -
                # 
     | 
| 
       56 
     | 
    
         
            -
                # 
     | 
| 
       57 
     | 
    
         
            -
                # 
     | 
| 
      
 50 
     | 
    
         
            +
                # @quirk caCORE +caCORE+ expects the store argument to be carefully prepared prior to
         
     | 
| 
      
 51 
     | 
    
         
            +
                #   the create or update. build_storable_template culls the target object with a template
         
     | 
| 
      
 52 
     | 
    
         
            +
                #   which includes only those references which are necessary for the store to succeed.
         
     | 
| 
      
 53 
     | 
    
         
            +
                #   This template builder ensures that mandatory independent references exist. Cascaded
         
     | 
| 
      
 54 
     | 
    
         
            +
                #   dependent references are included in the template but are not created before submission
         
     | 
| 
      
 55 
     | 
    
         
            +
                #   to +caCORE+. These reference attribute distinctions are implicit application rules which
         
     | 
| 
      
 56 
     | 
    
         
            +
                #   are explicated in the +caRuby+ application domain class definition using Metadata
         
     | 
| 
      
 57 
     | 
    
         
            +
                #   methods.
         
     | 
| 
       58 
58 
     | 
    
         
             
                #
         
     | 
| 
       59 
     | 
    
         
            -
                # caCORE  
     | 
| 
       60 
     | 
    
         
            -
                # 
     | 
| 
       61 
     | 
    
         
            -
                # 
     | 
| 
       62 
     | 
    
         
            -
                # 
     | 
| 
      
 59 
     | 
    
         
            +
                # @quirk caCORE +caCORE+ create issues an error if a create argument directly or
         
     | 
| 
      
 60 
     | 
    
         
            +
                #   indirectly references a non-cascaded domain object without an identifier, even if the
         
     | 
| 
      
 61 
     | 
    
         
            +
                #   reference is not relevant to the create. The template returned by this method elides
         
     | 
| 
      
 62 
     | 
    
         
            +
                #   all non-essential references.
         
     | 
| 
       63 
63 
     | 
    
         
             
                #
         
     | 
| 
       64 
     | 
    
         
            -
                # caCORE  
     | 
| 
       65 
     | 
    
         
            -
                # 
     | 
| 
       66 
     | 
    
         
            -
                # 
     | 
| 
       67 
     | 
    
         
            -
                # 
     | 
| 
       68 
     | 
    
         
            -
                # 
     | 
| 
       69 
     | 
    
         
            -
                # 
     | 
| 
       70 
     | 
    
         
            -
                # 
     | 
| 
       71 
     | 
    
         
            -
                # 
     | 
| 
       72 
     | 
    
         
            -
                # 
     | 
| 
       73 
     | 
    
         
            -
                # 
     | 
| 
       74 
     | 
    
         
            -
                # 
     | 
| 
      
 64 
     | 
    
         
            +
                # @quirk caCORE application business logic performs unnecessary verification
         
     | 
| 
      
 65 
     | 
    
         
            +
                #   of uncascaded references as if they were a cascaded create. This can result in
         
     | 
| 
      
 66 
     | 
    
         
            +
                #   an obscure ApplicationException. The server.log stack trace indicates the
         
     | 
| 
      
 67 
     | 
    
         
            +
                #   extraneous verification code. For example, +caTissue+ +NewSpecimenBizLogic.validateStorageContainer+
         
     | 
| 
      
 68 
     | 
    
         
            +
                #   is unnecessarily called on a SpecimenCollectionGroup (SCG) update. SCG does not
         
     | 
| 
      
 69 
     | 
    
         
            +
                #   cascade to Specimen, but caTissue considers the SCG update a Specimen create
         
     | 
| 
      
 70 
     | 
    
         
            +
                #   anyway if the SCG references a Specimen without an identifier. The Specimen
         
     | 
| 
      
 71 
     | 
    
         
            +
                #   business logic then raises an exception when it finds a StorageContainer
         
     | 
| 
      
 72 
     | 
    
         
            +
                #   without an identifier in the Specimen object graph. Therefore, an update must
         
     | 
| 
      
 73 
     | 
    
         
            +
                #   build a storable template which prunes the update object graph to exclude uncascaded
         
     | 
| 
      
 74 
     | 
    
         
            +
                #   objects. These uncascaded objects should be ignored by the application but aren't.
         
     | 
| 
       75 
75 
     | 
    
         
             
                #
         
     | 
| 
       76 
76 
     | 
    
         
             
                # @param [Resource] obj the domain object to save
         
     | 
| 
       77 
77 
     | 
    
         
             
                # @return [Resource] the template to use as the caCORE argument
         
     | 
| 
         @@ -92,10 +92,10 @@ module CaRuby 
     | 
|
| 
       92 
92 
     | 
    
         
             
                # Ensure that the given domain object obj can be created or updated by setting the identifier for
         
     | 
| 
       93 
93 
     | 
    
         
             
                # each independent reference in the create template object graph.
         
     | 
| 
       94 
94 
     | 
    
         
             
                #
         
     | 
| 
       95 
     | 
    
         
            -
                # caCORE  
     | 
| 
       96 
     | 
    
         
            -
                # 
     | 
| 
       97 
     | 
    
         
            -
                # 
     | 
| 
       98 
     | 
    
         
            -
                # 
     | 
| 
      
 95 
     | 
    
         
            +
                # @quirk caCORE +caCORE+ raises an ApplicationException if an independent reference in the create or
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   update argument does not have an identifier. The +caCORE+ server log error is as follows:
         
     | 
| 
      
 97 
     | 
    
         
            +
                #     java.lang.IllegalArgumentException: id to load is required for loading
         
     | 
| 
      
 98 
     | 
    
         
            +
                #   The server log stack trace indicates a bizlogic line that offers a clue to the offending reference.
         
     | 
| 
       99 
99 
     | 
    
         
             
                def ensure_storable(obj)
         
     | 
| 
       100 
100 
     | 
    
         
             
                  # Add defaults, which might introduce independent references. Enable the lazy loader to fetch
         
     | 
| 
       101 
101 
     | 
    
         
             
                  # create references from the database where needed to build defaults.
         
     | 
| 
         @@ -113,24 +113,24 @@ module CaRuby 
     | 
|
| 
       113 
113 
     | 
    
         | 
| 
       114 
114 
     | 
    
         
             
                # Returns the attributes to visit in building the template for the given
         
     | 
| 
       115 
115 
     | 
    
         
             
                # domain object. The visitable attributes consist of the following:
         
     | 
| 
       116 
     | 
    
         
            -
                # * The { 
     | 
| 
      
 116 
     | 
    
         
            +
                # * The {Attributes#unproxied_savable_template_attributes} filtered as follows:
         
     | 
| 
       117 
117 
     | 
    
         
             
                #   * If the database operation is a create, then exclude the cascaded attributes.
         
     | 
| 
       118 
118 
     | 
    
         
             
                #   * If the given object has an identifier, then exclude the attributes which
         
     | 
| 
       119 
119 
     | 
    
         
             
                #     have the the :no_cascade_update_to_create flag set.
         
     | 
| 
       120 
     | 
    
         
            -
                # * The { 
     | 
| 
      
 120 
     | 
    
         
            +
                # * The {Attributes#proxied_savable_template_attributes} are included if and
         
     | 
| 
       121 
121 
     | 
    
         
             
                #   only if every referenced object has an identifier, and therefore does not
         
     | 
| 
       122 
122 
     | 
    
         
             
                #   need to be proxied.
         
     | 
| 
       123 
123 
     | 
    
         
             
                #
         
     | 
| 
       124 
     | 
    
         
            -
                # caTissue  
     | 
| 
       125 
     | 
    
         
            -
                # 
     | 
| 
       126 
     | 
    
         
            -
                # 
     | 
| 
       127 
     | 
    
         
            -
                # 
     | 
| 
      
 124 
     | 
    
         
            +
                # @quirk caTissue caTissue ignores some references, e.g. Participant CPR, and auto-generates
         
     | 
| 
      
 125 
     | 
    
         
            +
                #   the values instead. Therefore, the create template builder excludes these auto-generated
         
     | 
| 
      
 126 
     | 
    
         
            +
                #   attributes. After the create, the auto-generated references are merged into the created
         
     | 
| 
      
 127 
     | 
    
         
            +
                #   object graph and the references are updated if necessary.
         
     | 
| 
       128 
128 
     | 
    
         
             
                #
         
     | 
| 
       129 
129 
     | 
    
         
             
                # @param [Resource] obj the domain object copied to the update template
         
     | 
| 
       130 
130 
     | 
    
         
             
                # @return [<Symbol>] the reference attributes to include in the update template
         
     | 
| 
       131 
     | 
    
         
            -
                def  
     | 
| 
      
 131 
     | 
    
         
            +
                def savable_template_attributes(obj)
         
     | 
| 
       132 
132 
     | 
    
         
             
                  # The starting set of candidate attributes is the unproxied cascaded references.
         
     | 
| 
       133 
     | 
    
         
            -
                  unproxied = savable_attributes(obj, obj.class. 
     | 
| 
      
 133 
     | 
    
         
            +
                  unproxied = savable_attributes(obj, obj.class.unproxied_savable_template_attributes)
         
     | 
| 
       134 
134 
     | 
    
         
             
                  # The proxied attributes to save.
         
     | 
| 
       135 
135 
     | 
    
         
             
                  proxied = savable_proxied_attributes(obj)
         
     | 
| 
       136 
136 
     | 
    
         
             
                  # The combined set of savable attributes
         
     | 
| 
         @@ -139,11 +139,11 @@ module CaRuby 
     | 
|
| 
       139 
139 
     | 
    
         | 
| 
       140 
140 
     | 
    
         
             
                # Filters the given attributes, if necessary, to exclude attributes as follows:
         
     | 
| 
       141 
141 
     | 
    
         
             
                # * If the save operation is a create, then exclude the
         
     | 
| 
       142 
     | 
    
         
            -
                #   { 
     | 
| 
      
 142 
     | 
    
         
            +
                #   {Attribute#autogenerated_on_create?} attributes.
         
     | 
| 
       143 
143 
     | 
    
         
             
                #
         
     | 
| 
       144 
144 
     | 
    
         
             
                # @param [Resource] obj the visited domain object
         
     | 
| 
       145 
     | 
    
         
            -
                # @param [ 
     | 
| 
       146 
     | 
    
         
            -
                # @return [ 
     | 
| 
      
 145 
     | 
    
         
            +
                # @param [Attributes::Filter] the savable attribute filter
         
     | 
| 
      
 146 
     | 
    
         
            +
                # @return [Attributes::Filter] the composed attribute filter
         
     | 
| 
       147 
147 
     | 
    
         
             
                def mergeable_attributes(obj, attributes)
         
     | 
| 
       148 
148 
     | 
    
         
             
                  # If this is an update, then there is no filter on the given attributes.
         
     | 
| 
       149 
149 
     | 
    
         
             
                  return attributes if @subject.identifier
         
     | 
| 
         @@ -155,7 +155,7 @@ module CaRuby 
     | 
|
| 
       155 
155 
     | 
    
         
             
                # Composes the given attributes, if necessary, to exclude attributes as follows:
         
     | 
| 
       156 
156 
     | 
    
         
             
                # * If the save operation is a create, then exclude the auto-generated attributes.
         
     | 
| 
       157 
157 
     | 
    
         
             
                # * If the visited object has an identifier, then include only those attributes
         
     | 
| 
       158 
     | 
    
         
            -
                #   which { 
     | 
| 
      
 158 
     | 
    
         
            +
                #   which {Attribute#cascade_update_to_create?} or have an identifier. 
         
     | 
| 
       159 
159 
     | 
    
         
             
                #
         
     | 
| 
       160 
160 
     | 
    
         
             
                # @param (see #mergeable_attributes)
         
     | 
| 
       161 
161 
     | 
    
         
             
                # @return (see #mergeable_attributes)
         
     | 
| 
         @@ -178,7 +178,7 @@ module CaRuby 
     | 
|
| 
       178 
178 
     | 
    
         
             
                def savable_proxied_attributes(obj)
         
     | 
| 
       179 
179 
     | 
    
         
             
                  # Include a proxied reference only if the proxied dependents have an identifier,
         
     | 
| 
       180 
180 
     | 
    
         
             
                  # since those without an identifer are created separately via the proxy.
         
     | 
| 
       181 
     | 
    
         
            -
                  obj.class. 
     | 
| 
      
 181 
     | 
    
         
            +
                  obj.class.proxied_savable_template_attributes.reject do |attr|
         
     | 
| 
       182 
182 
     | 
    
         
             
                    ref = obj.send(attr)
         
     | 
| 
       183 
183 
     | 
    
         
             
                    case ref
         
     | 
| 
       184 
184 
     | 
    
         
             
                      when Enumerable then ref.any? { |dep| not dep.identifier }
         
     | 
| 
         @@ -189,18 +189,18 @@ module CaRuby 
     | 
|
| 
       189 
189 
     | 
    
         | 
| 
       190 
190 
     | 
    
         
             
                # Copies proxied references as needed.
         
     | 
| 
       191 
191 
     | 
    
         
             
                #
         
     | 
| 
       192 
     | 
    
         
            -
                # caTissue  
     | 
| 
       193 
     | 
    
         
            -
                # 
     | 
| 
       194 
     | 
    
         
            -
                # 
     | 
| 
       195 
     | 
    
         
            -
                # 
     | 
| 
       196 
     | 
    
         
            -
                # 
     | 
| 
       197 
     | 
    
         
            -
                # 
     | 
| 
       198 
     | 
    
         
            -
                # 
     | 
| 
       199 
     | 
    
         
            -
                # 
     | 
| 
       200 
     | 
    
         
            -
                # 
     | 
| 
      
 192 
     | 
    
         
            +
                # @quirk caTissue even though Specimen save cascades to SpecimenPosition,
         
     | 
| 
      
 193 
     | 
    
         
            +
                #   SpecimenPosition cannot be updated directly. Rather than simply not
         
     | 
| 
      
 194 
     | 
    
         
            +
                #   cascading to the SpecimenPosition, caTissue checks a Specimen save argument
         
     | 
| 
      
 195 
     | 
    
         
            +
                #   to ensure that the SpecimenPosition reflects the current database state
         
     | 
| 
      
 196 
     | 
    
         
            +
                #   rather than the desired cascaded state. Play along with this bizarre
         
     | 
| 
      
 197 
     | 
    
         
            +
                #   mechanism by adding our own bizarre work-around mechanism to copy a
         
     | 
| 
      
 198 
     | 
    
         
            +
                #   proxied reference only if it has an identifier. This works only because
         
     | 
| 
      
 199 
     | 
    
         
            +
                #   another work-around in the #{CaRuby::Database::Writer} updates proxied
         
     | 
| 
      
 200 
     | 
    
         
            +
                #   references via the proxy create before building the update template.
         
     | 
| 
       201 
201 
     | 
    
         
             
                def copy_proxied_save_references(obj, template)
         
     | 
| 
       202 
202 
     | 
    
         
             
                  return unless obj.identifier
         
     | 
| 
       203 
     | 
    
         
            -
                  obj.class. 
     | 
| 
      
 203 
     | 
    
         
            +
                  obj.class.proxied_savable_template_attributes.each do |attr|
         
     | 
| 
       204 
204 
     | 
    
         
             
                    # the proxy source
         
     | 
| 
       205 
205 
     | 
    
         
             
                    ref = obj.send(attr)
         
     | 
| 
       206 
206 
     | 
    
         
             
                    case ref
         
     | 
| 
         @@ -19,7 +19,7 @@ module CaRuby 
     | 
|
| 
       19 
19 
     | 
    
         
             
                    # the save (result, argument) synchronization visitor
         
     | 
| 
       20 
20 
     | 
    
         
             
                    @svd_sync_vstr = MatchVisitor.new(:matcher => svd_mtchr) { |ref| ref.class.dependent_attributes }
         
     | 
| 
       21 
21 
     | 
    
         
             
                    # the attributes to merge from the save result
         
     | 
| 
       22 
     | 
    
         
            -
                    mgbl = Proc.new { |ref| ref.class.domain_attributes } 
     | 
| 
      
 22 
     | 
    
         
            +
                    mgbl = Proc.new { |ref| ref.class.domain_attributes }
         
     | 
| 
       23 
23 
     | 
    
         
             
                    # the save result => argument merge visitor
         
     | 
| 
       24 
24 
     | 
    
         
             
                    @svd_mrg_vstr = MergeVisitor.new(:matcher => svd_mtchr, :mergeable => mgbl) { |ref| ref.class.dependent_attributes }
         
     | 
| 
       25 
25 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -125,7 +125,7 @@ module CaRuby 
     | 
|
| 
       125 
125 
     | 
    
         
             
                  # Note that some applications restrict or forbid delete operations. Check the specific application
         
     | 
| 
       126 
126 
     | 
    
         
             
                  # documentation to determine whether deletion is supported.
         
     | 
| 
       127 
127 
     | 
    
         
             
                  #
         
     | 
| 
       128 
     | 
    
         
            -
                   
     | 
| 
      
 128 
     | 
    
         
            +
                  # @param [Resource] obj the domain object to delete
         
     | 
| 
       129 
129 
     | 
    
         
             
                  # @raise [DatabaseError] if the database operation fails
         
     | 
| 
       130 
130 
     | 
    
         
             
                  def delete(obj)
         
     | 
| 
       131 
131 
     | 
    
         
             
                    perform(:delete, obj) { delete_object(obj) }
         
     | 
| 
         @@ -133,9 +133,9 @@ module CaRuby 
     | 
|
| 
       133 
133 
     | 
    
         | 
| 
       134 
134 
     | 
    
         
             
                  # Creates the domain object obj, if necessary.
         
     | 
| 
       135 
135 
     | 
    
         
             
                  #
         
     | 
| 
       136 
     | 
    
         
            -
                  #  
     | 
| 
       137 
     | 
    
         
            -
                  #  
     | 
| 
       138 
     | 
    
         
            -
                  #  
     | 
| 
      
 136 
     | 
    
         
            +
                  # @param [Resource, <Resource>] obj the domain object or collection of domain objects to find or create 
         
     | 
| 
      
 137 
     | 
    
         
            +
                  # @raise [ArgumentError] if obj is nil or empty
         
     | 
| 
      
 138 
     | 
    
         
            +
                  # @raise [DatabaseError] if obj could not be created
         
     | 
| 
       139 
139 
     | 
    
         
             
                  def ensure_exists(obj)
         
     | 
| 
       140 
140 
     | 
    
         
             
                    raise ArgumentError.new("Database ensure_exists is missing a domain object argument") if obj.nil_or_empty?
         
     | 
| 
       141 
141 
     | 
    
         
             
                    obj.enumerate { |ref| find(ref, :create) unless ref.identifier }
         
     | 
| 
         @@ -237,50 +237,49 @@ module CaRuby 
     | 
|
| 
       237 
237 
     | 
    
         
             
                  # Creates obj by submitting a template to the persistence service. Ensures that the domain
         
     | 
| 
       238 
238 
     | 
    
         
             
                  # objects referenced by the created obj exist and are correctly stored.
         
     | 
| 
       239 
239 
     | 
    
         
             
                  #
         
     | 
| 
       240 
     | 
    
         
            -
                  # caCORE  
     | 
| 
       241 
     | 
    
         
            -
                  # 
     | 
| 
       242 
     | 
    
         
            -
                  # 
     | 
| 
       243 
     | 
    
         
            -
                  # 
     | 
| 
       244 
     | 
    
         
            -
                  # 
     | 
| 
       245 
     | 
    
         
            -
                  # 
     | 
| 
       246 
     | 
    
         
            -
                  # 
     | 
| 
       247 
     | 
    
         
            -
                  # 
     | 
| 
       248 
     | 
    
         
            -
                  #
         
     | 
| 
       249 
     | 
    
         
            -
                  # caCORE  
     | 
| 
       250 
     | 
    
         
            -
                  # 
     | 
| 
       251 
     | 
    
         
            -
                  # 
     | 
| 
       252 
     | 
    
         
            -
                  # 
     | 
| 
       253 
     | 
    
         
            -
                  # 
     | 
| 
       254 
     | 
    
         
            -
                  # 
     | 
| 
       255 
     | 
    
         
            -
                  # 
     | 
| 
       256 
     | 
    
         
            -
                  #
         
     | 
| 
       257 
     | 
    
         
            -
                  # 
     | 
| 
       258 
     | 
    
         
            -
                  # 
     | 
| 
       259 
     | 
    
         
            -
                  # 
     | 
| 
       260 
     | 
    
         
            -
                  # 
     | 
| 
       261 
     | 
    
         
            -
                  # 
     | 
| 
       262 
     | 
    
         
            -
                  # 
     | 
| 
       263 
     | 
    
         
            -
                  # 
     | 
| 
       264 
     | 
    
         
            -
                  #
         
     | 
| 
       265 
     | 
    
         
            -
                  # 
     | 
| 
       266 
     | 
    
         
            -
                  # 
     | 
| 
       267 
     | 
    
         
            -
                  # 
     | 
| 
       268 
     | 
    
         
            -
                  # 
     | 
| 
       269 
     | 
    
         
            -
                  # 
     | 
| 
       270 
     | 
    
         
            -
                  # 
     | 
| 
       271 
     | 
    
         
            -
                  #
         
     | 
| 
       272 
     | 
    
         
            -
                  # 
     | 
| 
       273 
     | 
    
         
            -
                  # 
     | 
| 
       274 
     | 
    
         
            -
                  # 
     | 
| 
       275 
     | 
    
         
            -
                  # 
     | 
| 
      
 240 
     | 
    
         
            +
                  # @quirk caCORE submitting the object directly for create runs into various caTissue bizlogic
         
     | 
| 
      
 241 
     | 
    
         
            +
                  #   traps, e.g. Participant CPR is not cascaded but Participant bizlogic checks that each CPR
         
     | 
| 
      
 242 
     | 
    
         
            +
                  #   referenced by Participant is ready to be created. It is treacherous to make assumptions
         
     | 
| 
      
 243 
     | 
    
         
            +
                  #   about what caTissue bizlogic will or will not check. Therefore, the safer strategy is to
         
     | 
| 
      
 244 
     | 
    
         
            +
                  #   build a template for submission that includes only the object cascaded and direct
         
     | 
| 
      
 245 
     | 
    
         
            +
                  #   non-cascaded independent references. The independent references are created if necessary.
         
     | 
| 
      
 246 
     | 
    
         
            +
                  #   The template thus includes only as much content as can safely pass through the caTissue
         
     | 
| 
      
 247 
     | 
    
         
            +
                  #   bizlogic minefield.
         
     | 
| 
      
 248 
     | 
    
         
            +
                  #
         
     | 
| 
      
 249 
     | 
    
         
            +
                  # @quirk caCORE caCORE create does not update the submitted object to reflect the created
         
     | 
| 
      
 250 
     | 
    
         
            +
                  #   content. The create result is a separate object, which in turn does not always reflect
         
     | 
| 
      
 251 
     | 
    
         
            +
                  #   the created content, e.g. caTissue ignores auto-generated attributes such as Container
         
     | 
| 
      
 252 
     | 
    
         
            +
                  #   name. Work-around is to merge the create result into the object being created, being
         
     | 
| 
      
 253 
     | 
    
         
            +
                  #   careful to merge only the fetched content in order to avoid the dreaded out-of-session
         
     | 
| 
      
 254 
     | 
    
         
            +
                  #   error message. The post-create cascaded dependent hierarchy is traversed to capture
         
     | 
| 
      
 255 
     | 
    
         
            +
                  #   the created state for each created object.
         
     | 
| 
      
 256 
     | 
    
         
            +
                  #
         
     | 
| 
      
 257 
     | 
    
         
            +
                  #   The ignored content is handled separately by fetching the ignored content from
         
     | 
| 
      
 258 
     | 
    
         
            +
                  #   the database, comparing it to the desired content as reflected in the submitted
         
     | 
| 
      
 259 
     | 
    
         
            +
                  #   create argument object, and submitting a post-create caCORE update as necessary to
         
     | 
| 
      
 260 
     | 
    
         
            +
                  #   force caCORE to reflect the desired content. This is complicated by the various
         
     | 
| 
      
 261 
     | 
    
         
            +
                  #   auto-generation schemes, e.g. in caTissue, that require a careful fetch, match and
         
     | 
| 
      
 262 
     | 
    
         
            +
                  #   merge logic to make sense of how what was actually created corresponds to the desired
         
     | 
| 
      
 263 
     | 
    
         
            +
                  #   content expressed in the create argument object graph.
         
     | 
| 
      
 264 
     | 
    
         
            +
                  #
         
     | 
| 
      
 265 
     | 
    
         
            +
                  #   There are thus several objects involved in the create process:
         
     | 
| 
      
 266 
     | 
    
         
            +
                  #   * the object to create
         
     | 
| 
      
 267 
     | 
    
         
            +
                  #   * the template for caCORE createObject submission
         
     | 
| 
      
 268 
     | 
    
         
            +
                  #   * the caCORE createObject result
         
     | 
| 
      
 269 
     | 
    
         
            +
                  #   * the post-create fetched object that reflects the persistent content
         
     | 
| 
      
 270 
     | 
    
         
            +
                  #   * the template for post-create caCORE updateObject submission
         
     | 
| 
      
 271 
     | 
    
         
            +
                  #
         
     | 
| 
      
 272 
     | 
    
         
            +
                  #   This object menagerie is unfortunate but unavoidable if we are to navigate the treacherous
         
     | 
| 
      
 273 
     | 
    
         
            +
                  #   caCORE create process and ensure that:
         
     | 
| 
      
 274 
     | 
    
         
            +
                  #   1. the database reflects the create argument.
         
     | 
| 
      
 275 
     | 
    
         
            +
                  #   2. the created object reflects the database content.
         
     | 
| 
       276 
276 
     | 
    
         
             
                  #
         
     | 
| 
       277 
277 
     | 
    
         
             
                  # @param (see #create)
         
     | 
| 
       278 
278 
     | 
    
         
             
                  # @return obj
         
     | 
| 
       279 
279 
     | 
    
         
             
                  def create_from_template(obj)
         
     | 
| 
       280 
280 
     | 
    
         
             
                    # The create template. Independent saved references are created as necessary.
         
     | 
| 
       281 
     | 
    
         
            -
                    tmpl = build_create_template(obj) 
     | 
| 
      
 281 
     | 
    
         
            +
                    tmpl = build_create_template(obj)
         
     | 
| 
       282 
282 
     | 
    
         
             
                    save_with_template(obj, tmpl) { |svc| svc.create(tmpl) }
         
     | 
| 
       283 
     | 
    
         
            -
             
     | 
| 
       284 
283 
     | 
    
         
             
                    # If obj is a top-level create, then ensure that remaining references exist.
         
     | 
| 
       285 
284 
     | 
    
         
             
                    if @operations.first.subject == obj then
         
     | 
| 
       286 
285 
     | 
    
         
             
                      refs = obj.references.reject { |ref| ref.identifier }
         
     | 
| 
         @@ -297,7 +296,7 @@ module CaRuby 
     | 
|
| 
       297 
296 
     | 
    
         
             
                    build_save_template(obj, @cr_tmpl_bldr)
         
     | 
| 
       298 
297 
     | 
    
         
             
                  end
         
     | 
| 
       299 
298 
     | 
    
         
             
            #
         
     | 
| 
       300 
     | 
    
         
            -
                  # caCORE  
     | 
| 
      
 299 
     | 
    
         
            +
                  # @quirk caCORE application create logic might ignore a non-domain attribute value,
         
     | 
| 
       301 
300 
     | 
    
         
             
                  # e.g. the caTissue StorageContainer auto-generated name attribute. In other cases, the application
         
     | 
| 
       302 
301 
     | 
    
         
             
                  # always ignores a non-domain attribute value, so the object should not be saved even if it differs
         
     | 
| 
       303 
302 
     | 
    
         
             
                  # from the stored result, e.g. the caTissue CollectionProtocolRegistration unsaved
         
     | 
| 
         @@ -306,7 +305,7 @@ module CaRuby 
     | 
|
| 
       306 
305 
     | 
    
         
             
                  # to update the saved object.
         
     | 
| 
       307 
306 
     | 
    
         
             
                  #
         
     | 
| 
       308 
307 
     | 
    
         
             
                  # This method returns whether the saved obj differs from the stored source for any
         
     | 
| 
       309 
     | 
    
         
            -
                  # { 
     | 
| 
      
 308 
     | 
    
         
            +
                  # {Attributes#autogenerated_nondomain_attributes}.
         
     | 
| 
       310 
309 
     | 
    
         
             
                  #
         
     | 
| 
       311 
310 
     | 
    
         
             
                  # @param [Resource] the created domain object
         
     | 
| 
       312 
311 
     | 
    
         
             
                  # @param [Resource] the stored database content source domain object
         
     | 
| 
         @@ -344,14 +343,14 @@ module CaRuby 
     | 
|
| 
       344 
343 
     | 
    
         | 
| 
       345 
344 
     | 
    
         
             
                  # Returns whether the given creatable domain attribute with value obj satisfies
         
     | 
| 
       346 
345 
     | 
    
         
             
                  # each of the following conditions:
         
     | 
| 
       347 
     | 
    
         
            -
                  # * the attribute is { 
     | 
| 
       348 
     | 
    
         
            -
                  # * the attribute is not an { 
     | 
| 
      
 346 
     | 
    
         
            +
                  # * the attribute is {Attribute#independent?}
         
     | 
| 
      
 347 
     | 
    
         
            +
                  # * the attribute is not an {Attribute#owner?}
         
     | 
| 
       349 
348 
     | 
    
         
             
                  # * the obj value is unsaved
         
     | 
| 
       350 
349 
     | 
    
         
             
                  # * the attribute is not mandatory
         
     | 
| 
       351 
350 
     | 
    
         
             
                  # * the attribute references a {#pending_create?} save context.
         
     | 
| 
       352 
351 
     | 
    
         
             
                  #
         
     | 
| 
       353 
352 
     | 
    
         
             
                  # @param obj (see #create)
         
     | 
| 
       354 
     | 
    
         
            -
                  # @param [ 
     | 
| 
      
 353 
     | 
    
         
            +
                  # @param [Attribute] attr_md candidate attribute metadata
         
     | 
| 
       355 
354 
     | 
    
         
             
                  # @return [Boolean] whether the attribute should not be included in the create template
         
     | 
| 
       356 
355 
     | 
    
         
             
                  def exclude_pending_create_attribute?(obj, attr_md)
         
     | 
| 
       357 
356 
     | 
    
         
             
                    attr_md.independent? and
         
     | 
| 
         @@ -413,13 +412,10 @@ module CaRuby 
     | 
|
| 
       413 
412 
     | 
    
         | 
| 
       414 
413 
     | 
    
         
             
                    # update a cascaded dependent by updating the owner
         
     | 
| 
       415 
414 
     | 
    
         
             
                    owner = cascaded_owner(obj)
         
     | 
| 
       416 
     | 
    
         
            -
                     
     | 
| 
       417 
     | 
    
         
            -
                    # if not cascaded, then update directly with a template
         
     | 
| 
       418 
     | 
    
         
            -
                    result ||= create_from_template(obj)
         
     | 
| 
      
 415 
     | 
    
         
            +
                    if owner then return update_cascaded_dependent(owner, obj) end
         
     | 
| 
       419 
416 
     | 
    
         | 
| 
       420 
     | 
    
         
            -
                    # update using a template
         
     | 
| 
      
 417 
     | 
    
         
            +
                    # Not cascaded dependent; update using a template,
         
     | 
| 
       421 
418 
     | 
    
         
             
                    tmpl = build_update_template(obj)
         
     | 
| 
       422 
     | 
    
         
            -
                    
         
     | 
| 
       423 
419 
     | 
    
         
             
                    # call the caCORE service with an obj update template
         
     | 
| 
       424 
420 
     | 
    
         
             
                    save_with_template(obj, tmpl) { |svc| svc.update(tmpl) }
         
     | 
| 
       425 
421 
     | 
    
         
             
                    # take a snapshot of the updated content
         
     | 
| 
         @@ -427,9 +423,9 @@ module CaRuby 
     | 
|
| 
       427 
423 
     | 
    
         
             
                  end
         
     | 
| 
       428 
424 
     | 
    
         | 
| 
       429 
425 
     | 
    
         
             
                  # Returns the owner that can cascade update to the given object.
         
     | 
| 
       430 
     | 
    
         
            -
                  # The owner is the #{Resource# 
     | 
| 
       431 
     | 
    
         
            -
                  # for which the owner attribute { 
     | 
| 
       432 
     | 
    
         
            -
                  # is { 
     | 
| 
      
 426 
     | 
    
         
            +
                  # The owner is the #{Resource#effective_owner_attribute} value
         
     | 
| 
      
 427 
     | 
    
         
            +
                  # for which the owner attribute {Attribute#inverse_metadata}
         
     | 
| 
      
 428 
     | 
    
         
            +
                  # is {Attribute#cascaded?}.
         
     | 
| 
       433 
429 
     | 
    
         
             
                  # 
         
     | 
| 
       434 
430 
     | 
    
         
             
                  # @param [Resource] obj the domain object to update
         
     | 
| 
       435 
431 
     | 
    
         
             
                  # @return [Resource, nil] the owner which can cascade an update to the object, or nil if none
         
     | 
| 
         @@ -439,14 +435,14 @@ module CaRuby 
     | 
|
| 
       439 
435 
     | 
    
         
             
                    # the owner attribute
         
     | 
| 
       440 
436 
     | 
    
         
             
                    oattr = obj.effective_owner_attribute
         
     | 
| 
       441 
437 
     | 
    
         
             
                    if oattr.nil? then raise DatabaseError.new("Dependent #{obj} does not have an owner") end
         
     | 
| 
       442 
     | 
    
         
            -
                    dep_md = obj.class.attribute_metadata(oattr). 
     | 
| 
      
 438 
     | 
    
         
            +
                    dep_md = obj.class.attribute_metadata(oattr).inverse_metadata
         
     | 
| 
       443 
439 
     | 
    
         
             
                    if dep_md and dep_md.cascaded? then
         
     | 
| 
       444 
440 
     | 
    
         
             
                      obj.send(oattr)
         
     | 
| 
       445 
441 
     | 
    
         
             
                    end
         
     | 
| 
       446 
442 
     | 
    
         
             
                  end
         
     | 
| 
       447 
443 
     | 
    
         | 
| 
       448 
     | 
    
         
            -
                  def  
     | 
| 
       449 
     | 
    
         
            -
                    logger.debug { "Updating #{obj} by  
     | 
| 
      
 444 
     | 
    
         
            +
                  def update_cascaded_dependent(owner, obj)
         
     | 
| 
      
 445 
     | 
    
         
            +
                    logger.debug { "Updating cascaded dependent #{obj} by updating the owner #{owner}..." }
         
     | 
| 
       450 
446 
     | 
    
         
             
                    update(owner)
         
     | 
| 
       451 
447 
     | 
    
         
             
                  end
         
     | 
| 
       452 
448 
     | 
    
         | 
| 
         @@ -458,33 +454,33 @@ module CaRuby 
     | 
|
| 
       458 
454 
     | 
    
         
             
                    obj.take_snapshot
         
     | 
| 
       459 
455 
     | 
    
         
             
                  end
         
     | 
| 
       460 
456 
     | 
    
         | 
| 
       461 
     | 
    
         
            -
                  # caTissue  
     | 
| 
       462 
     | 
    
         
            -
                  # 
     | 
| 
       463 
     | 
    
         
            -
                  # 
     | 
| 
       464 
     | 
    
         
            -
                  # 
     | 
| 
       465 
     | 
    
         
            -
                  # 
     | 
| 
       466 
     | 
    
         
            -
                  # 
     | 
| 
       467 
     | 
    
         
            -
                  # 
     | 
| 
       468 
     | 
    
         
            -
                  # 
     | 
| 
       469 
     | 
    
         
            -
                  # 
     | 
| 
       470 
     | 
    
         
            -
                  # 
     | 
| 
       471 
     | 
    
         
            -
                  # 
     | 
| 
       472 
     | 
    
         
            -
                  # 
     | 
| 
       473 
     | 
    
         
            -
                  # 
     | 
| 
       474 
     | 
    
         
            -
                  # 
     | 
| 
       475 
     | 
    
         
            -
                  # 
     | 
| 
       476 
     | 
    
         
            -
                  # 
     | 
| 
       477 
     | 
    
         
            -
                  # 
     | 
| 
       478 
     | 
    
         
            -
                  # 
     | 
| 
       479 
     | 
    
         
            -
                  # 
     | 
| 
       480 
     | 
    
         
            -
                  # 
     | 
| 
       481 
     | 
    
         
            -
                  # 
     | 
| 
      
 457 
     | 
    
         
            +
                  # @quirk caTissue the conditions for when and how to include a proxied dependent are
         
     | 
| 
      
 458 
     | 
    
         
            +
                  #   are intricate and treacherous. So far as can be determined, in the case of a
         
     | 
| 
      
 459 
     | 
    
         
            +
                  #   SpecimenPosition proxied by a TransferEventParameters, the sequence is as follows:
         
     | 
| 
      
 460 
     | 
    
         
            +
                  #   * If a Specimen without a previous position is updated with a position, then
         
     | 
| 
      
 461 
     | 
    
         
            +
                  #     the update template should not include the target position. Subsequent to the
         
     | 
| 
      
 462 
     | 
    
         
            +
                  #     Specimen update, the TransferEventParameters proxy is created.
         
     | 
| 
      
 463 
     | 
    
         
            +
                  #     This creates a new position in the database as a server side-effect. caRuby
         
     | 
| 
      
 464 
     | 
    
         
            +
                  #     then fetches the new position and merges it into the target position.
         
     | 
| 
      
 465 
     | 
    
         
            +
                  #   * If a Specimen with a previous position is updated, then the update template
         
     | 
| 
      
 466 
     | 
    
         
            +
                  #     must reflect the current datbase position state. Therefore, caRuby first
         
     | 
| 
      
 467 
     | 
    
         
            +
                  #     creates the proxy to update the database state.
         
     | 
| 
      
 468 
     | 
    
         
            +
                  #   * The TransferEventParameters create must reference a Specimen with the current
         
     | 
| 
      
 469 
     | 
    
         
            +
                  #     database position state, not the new position state.
         
     | 
| 
      
 470 
     | 
    
         
            +
                  #   * Update of a Specimen with a current database position must reference a
         
     | 
| 
      
 471 
     | 
    
         
            +
                  #     position which reflects that database state. This is true even if the position
         
     | 
| 
      
 472 
     | 
    
         
            +
                  #     has not changed. The position must be complete and consistent with the database
         
     | 
| 
      
 473 
     | 
    
         
            +
                  #     state. E.g. omitting the position storage container is accepted by caTissue
         
     | 
| 
      
 474 
     | 
    
         
            +
                  #     but corrupts the database side and has adverse delayed effects.
         
     | 
| 
      
 475 
     | 
    
         
            +
                  #   * Specimen create (but not auto-generated update) cannot include a position
         
     | 
| 
      
 476 
     | 
    
         
            +
                  #     (although that might have changed in the 1.1.2 release). The target position
         
     | 
| 
      
 477 
     | 
    
         
            +
                  #     must be created via the proxy after the Specimen is created.
         
     | 
| 
       482 
478 
     | 
    
         
             
                  #
         
     | 
| 
       483 
479 
     | 
    
         
             
                  # @param (see #update)
         
     | 
| 
       484 
     | 
    
         
            -
                  # @return [<Resource>] the #{ 
     | 
| 
      
 480 
     | 
    
         
            +
                  # @return [<Resource>] the #{Attributes#proxied_savable_template_attributes} dependents
         
     | 
| 
       485 
481 
     | 
    
         
             
                  #   which are #{Persistable#changed?}
         
     | 
| 
       486 
482 
     | 
    
         
             
                  def updatable_proxied_dependents(obj)
         
     | 
| 
       487 
     | 
    
         
            -
                    attrs = obj.class. 
     | 
| 
      
 483 
     | 
    
         
            +
                    attrs = obj.class.proxied_savable_template_attributes
         
     | 
| 
       488 
484 
     | 
    
         
             
                    return Array::EMPTY_ARRAY if attrs.empty?
         
     | 
| 
       489 
485 
     | 
    
         
             
                    deps = []
         
     | 
| 
       490 
486 
     | 
    
         
             
                    attrs.each do |attr|
         
     | 
| 
         @@ -537,7 +533,7 @@ module CaRuby 
     | 
|
| 
       537 
533 
     | 
    
         
             
                  def submit_save_template(obj, template)
         
     | 
| 
       538 
534 
     | 
    
         
             
                    svc = persistence_service(template.class)
         
     | 
| 
       539 
535 
     | 
    
         
             
                    result = template.identifier ? svc.update(template) : svc.create(template)
         
     | 
| 
       540 
     | 
    
         
            -
                    logger.debug { " 
     | 
| 
      
 536 
     | 
    
         
            +
                    logger.debug { "Save #{obj.qp} with template #{template.qp} produced caCORE result: #{result}." }
         
     | 
| 
       541 
537 
     | 
    
         
             
                    result
         
     | 
| 
       542 
538 
     | 
    
         
             
                  end
         
     | 
| 
       543 
539 
     | 
    
         | 
| 
         @@ -557,7 +553,6 @@ module CaRuby 
     | 
|
| 
       557 
553 
     | 
    
         
             
                    sync_saved_result_with_database(source, target)
         
     | 
| 
       558 
554 
     | 
    
         
             
                    # merge the source into the target
         
     | 
| 
       559 
555 
     | 
    
         
             
                    merge_saved(target, source)
         
     | 
| 
       560 
     | 
    
         
            -
             
     | 
| 
       561 
556 
     | 
    
         
             
                    # If saved must be updated, then update recursively.
         
     | 
| 
       562 
557 
     | 
    
         
             
                    # Otherwise, save dependents as needed.
         
     | 
| 
       563 
558 
     | 
    
         
             
                    if update_saved?(target, source) then
         
     | 
| 
         @@ -581,26 +576,30 @@ module CaRuby 
     | 
|
| 
       581 
576 
     | 
    
         
             
                  # Merges the database content into the given saved domain object.
         
     | 
| 
       582 
577 
     | 
    
         
             
                  # Dependents are merged recursively.
         
     | 
| 
       583 
578 
     | 
    
         
             
                  #
         
     | 
| 
       584 
     | 
    
         
            -
                  # caTissue  
     | 
| 
       585 
     | 
    
         
            -
                  # 
     | 
| 
       586 
     | 
    
         
            -
                  # 
     | 
| 
       587 
     | 
    
         
            -
                  # 
     | 
| 
       588 
     | 
    
         
            -
                  # 
     | 
| 
       589 
     | 
    
         
            -
                  # 
     | 
| 
      
 579 
     | 
    
         
            +
                  # @quirk caTissue the auto-generated references are not necessarily valid, e.g. the auto-generated
         
     | 
| 
      
 580 
     | 
    
         
            +
                  #   SpecimenRequirement characteristics tissue site is nil rather than the default 'Not Specified'.
         
     | 
| 
      
 581 
     | 
    
         
            +
                  #   This results in an obscure downstream error when creating an CPR which auto-generates a SCG
         
     | 
| 
      
 582 
     | 
    
         
            +
                  #   which auto-generates a Specimen which copies the invalid characteristics. The work-around for
         
     | 
| 
      
 583 
     | 
    
         
            +
                  #   this bug is to add defaults to auto-generated references. Then, if the content differs from
         
     | 
| 
      
 584 
     | 
    
         
            +
                  #   the database, the difference induces an update of the reference.
         
     | 
| 
       590 
585 
     | 
    
         
             
                  #
         
     | 
| 
       591 
586 
     | 
    
         
             
                  # @param (see #sync_saved)
         
     | 
| 
       592 
587 
     | 
    
         
             
                  # @return [Resource] the merged target object
         
     | 
| 
       593 
588 
     | 
    
         
             
                  def merge_saved(target, source)
         
     | 
| 
       594 
589 
     | 
    
         
             
                    logger.debug { "Merging saved result #{source} into saved #{target.qp}..." }
         
     | 
| 
       595 
     | 
    
         
            -
                    # Update each saved reference snapshot to reflect the database state and add a lazy loader 
     | 
| 
      
 590 
     | 
    
         
            +
                    # Update each saved reference snapshot to reflect the database state and add a lazy loader
         
     | 
| 
      
 591 
     | 
    
         
            +
                    # if necessary.
         
     | 
| 
       596 
592 
     | 
    
         
             
                    @svd_mrg_vstr.visit(source, target) do |src, tgt|
         
     | 
| 
      
 593 
     | 
    
         
            +
                      # Skip unsaved source objects. This occurs, e.g., if a transient source reference is generated
         
     | 
| 
      
 594 
     | 
    
         
            +
                      # on demand.
         
     | 
| 
      
 595 
     | 
    
         
            +
                      next if src.identifier.nil?
         
     | 
| 
       597 
596 
     | 
    
         
             
                      # capture the id
         
     | 
| 
       598 
597 
     | 
    
         
             
                      prev_id = tgt.identifier
         
     | 
| 
      
 598 
     | 
    
         
            +
                      # The target dependents will be visited, so persistify the target non-recursively.
         
     | 
| 
       599 
599 
     | 
    
         
             
                      persistify_object(tgt, src)
         
     | 
| 
       600 
     | 
    
         
            -
                      #  
     | 
| 
      
 600 
     | 
    
         
            +
                      # If tgt is an auto-generated reference, then add defaults.
         
     | 
| 
       601 
601 
     | 
    
         
             
                      if target != tgt and prev_id.nil? then tgt.add_defaults end
         
     | 
| 
       602 
602 
     | 
    
         
             
                    end
         
     | 
| 
       603 
     | 
    
         
            -
                    logger.debug { "Merged saved result #{source} into saved #{target.qp}." }
         
     | 
| 
       604 
603 
     | 
    
         
             
                  end
         
     | 
| 
       605 
604 
     | 
    
         | 
| 
       606 
605 
     | 
    
         
             
                  # Synchronizes the given save result source object to reflect the database content, as follows:
         
     | 
| 
         @@ -621,7 +620,7 @@ module CaRuby 
     | 
|
| 
       621 
620 
     | 
    
         
             
                    set_inverses(source)
         
     | 
| 
       622 
621 
     | 
    
         
             
                  end
         
     | 
| 
       623 
622 
     | 
    
         | 
| 
       624 
     | 
    
         
            -
                  # Refetches the given create result source if there are any { 
     | 
| 
      
 623 
     | 
    
         
            +
                  # Refetches the given create result source if there are any {Attributes#autogenerated_nondomain_attributes}
         
     | 
| 
       625 
624 
     | 
    
         
             
                  # which must be fetched to reflect the database state.
         
     | 
| 
       626 
625 
     | 
    
         
             
                  #
         
     | 
| 
       627 
626 
     | 
    
         
             
                  # @param source (see #sync_saved)
         
     | 
| 
         @@ -654,7 +653,7 @@ module CaRuby 
     | 
|
| 
       654 
653 
     | 
    
         
             
                  # Returns the saved target attributes which must be fetched to reflect the database content, consisting
         
     | 
| 
       655 
654 
     | 
    
         
             
                  # of the following:
         
     | 
| 
       656 
655 
     | 
    
         
             
                  # * {Persistable#saved_fetch_attributes}
         
     | 
| 
       657 
     | 
    
         
            -
                  # * { 
     | 
| 
      
 656 
     | 
    
         
            +
                  # * {Attributes#domain_attributes} which include a source reference without an identifier
         
     | 
| 
       658 
657 
     | 
    
         
             
                  #
         
     | 
| 
       659 
658 
     | 
    
         
             
                  # @param (see #sync_saved)
         
     | 
| 
       660 
659 
     | 
    
         
             
                  # @return [<Symbol>] the attributes which must be fetched
         
     | 
| 
         @@ -687,7 +686,7 @@ module CaRuby 
     | 
|
| 
       687 
686 
     | 
    
         
             
                  def save_changed_dependents(obj)
         
     | 
| 
       688 
687 
     | 
    
         
             
                    obj.class.dependent_attributes.each do |attr|
         
     | 
| 
       689 
688 
     | 
    
         
             
                      deps = obj.send(attr).to_enum
         
     | 
| 
       690 
     | 
    
         
            -
                      logger.debug { "Saving the #{obj} #{attr} dependents #{deps.qp} which have changed..." } unless deps.empty?
         
     | 
| 
      
 689 
     | 
    
         
            +
                      logger.debug { "Saving the #{obj} #{attr} dependents #{deps.qp(:single_line)} which have changed..." } unless deps.empty?
         
     | 
| 
       691 
690 
     | 
    
         
             
                      deps.each { |dep| save_dependent_if_changed(obj, attr, dep) }
         
     | 
| 
       692 
691 
     | 
    
         
             
                    end
         
     | 
| 
       693 
692 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -704,6 +703,7 @@ module CaRuby 
     | 
|
| 
       704 
703 
     | 
    
         
             
                      return create(dependent)
         
     | 
| 
       705 
704 
     | 
    
         
             
                    end
         
     | 
| 
       706 
705 
     | 
    
         
             
                    changes = dependent.changed_attributes
         
     | 
| 
      
 706 
     | 
    
         
            +
                    # If the dependent identifier is missing, then the identifier was probably created on demand. 
         
     | 
| 
       707 
707 
     | 
    
         
             
                    logger.debug { "#{owner.qp} #{attribute} dependent #{dependent.qp} changed for attributes #{changes.to_series}." } unless changes.empty?
         
     | 
| 
       708 
708 
     | 
    
         
             
                    if changes.any? { |attr| not dependent.class.attribute_metadata(attr).dependent? } then
         
     | 
| 
       709 
709 
     | 
    
         
             
                      # the owner save operation
         
     | 
    
        data/lib/caruby/database.rb
    CHANGED
    
    | 
         @@ -77,23 +77,23 @@ module CaRuby 
     | 
|
| 
       77 
77 
     | 
    
         | 
| 
       78 
78 
     | 
    
         
             
                # Creates a new Database with the specified service name and options.
         
     | 
| 
       79 
79 
     | 
    
         
             
                #
         
     | 
| 
       80 
     | 
    
         
            -
                # caCORE  
     | 
| 
       81 
     | 
    
         
            -
                # 
     | 
| 
       82 
     | 
    
         
            -
                # 
     | 
| 
       83 
     | 
    
         
            -
                # 
     | 
| 
       84 
     | 
    
         
            -
                # 
     | 
| 
      
 80 
     | 
    
         
            +
                # @quirk caCORE obtaining a caCORE session instance mysteriously depends on referencing the
         
     | 
| 
      
 81 
     | 
    
         
            +
                #   application service first. Therefore, the default persistence service appService method must
         
     | 
| 
      
 82 
     | 
    
         
            +
                #   be called after it is instantiated and before the session is instantiated. However, when
         
     | 
| 
      
 83 
     | 
    
         
            +
                #   the appService method is called just before a session is acquired, then this call corrupts
         
     | 
| 
      
 84 
     | 
    
         
            +
                #   the object state of existing objects.
         
     | 
| 
       85 
85 
     | 
    
         
             
                #
         
     | 
| 
       86 
     | 
    
         
            -
                # 
     | 
| 
       87 
     | 
    
         
            -
                # 
     | 
| 
       88 
     | 
    
         
            -
                # 
     | 
| 
       89 
     | 
    
         
            -
                # 
     | 
| 
       90 
     | 
    
         
            -
                # 
     | 
| 
       91 
     | 
    
         
            -
                # 
     | 
| 
      
 86 
     | 
    
         
            +
                #   Specifically, when a CaTissue::CollectionProtocol is created which references a
         
     | 
| 
      
 87 
     | 
    
         
            +
                #   CaTissue::CollectionProtocolRegistration which in turn references a CaTissue::Participant,
         
     | 
| 
      
 88 
     | 
    
         
            +
                #   then the call to PersistenceService.appService replaces the CaTissue::Participant
         
     | 
| 
      
 89 
     | 
    
         
            +
                #   reference with a difference CaTissue::Participant instance. The work-around for
         
     | 
| 
      
 90 
     | 
    
         
            +
                #   this extremely bizarre bug is to call appService immediately after instantiating
         
     | 
| 
      
 91 
     | 
    
         
            +
                #   the default persistence service.
         
     | 
| 
       92 
92 
     | 
    
         
             
                #
         
     | 
| 
       93 
     | 
    
         
            -
                # 
     | 
| 
       94 
     | 
    
         
            -
                # 
     | 
| 
       95 
     | 
    
         
            -
                # 
     | 
| 
       96 
     | 
    
         
            -
                # 
     | 
| 
      
 93 
     | 
    
         
            +
                #   This bug might be a low-level JRuby-Java-caCORE-Hibernate confusion where something in
         
     | 
| 
      
 94 
     | 
    
         
            +
                #   caCORE stomps on an existing JRuby object graph. To reproduce, move the appService call
         
     | 
| 
      
 95 
     | 
    
         
            +
                #   to the start_session method and run {PCBIN::MigrationTest#test_save} with all but the
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   verify_save(:biopsy, BIOPSY_OPTS) line commented out.
         
     | 
| 
       97 
97 
     | 
    
         
             
                #
         
     | 
| 
       98 
98 
     | 
    
         
             
                # @param [String] service_name the name of the default {PersistenceService}
         
     | 
| 
       99 
99 
     | 
    
         
             
                # @param [{Symbol => String}] opts access options
         
     | 
| 
         @@ -251,10 +251,10 @@ module CaRuby 
     | 
|
| 
       251 
251 
     | 
    
         | 
| 
       252 
252 
     | 
    
         
             
                # @return [Cache] a new object cache.
         
     | 
| 
       253 
253 
     | 
    
         
             
                def create_cache
         
     | 
| 
       254 
     | 
    
         
            -
                  # JRuby  
     | 
| 
       255 
     | 
    
         
            -
                  # 
     | 
| 
       256 
     | 
    
         
            -
                  # 
     | 
| 
       257 
     | 
    
         
            -
                  # 
     | 
| 
      
 254 
     | 
    
         
            +
                  # @quirk JRuby identifier is not a stable object when fetched from the database, i.e.:
         
     | 
| 
      
 255 
     | 
    
         
            +
                  #     obj.identifier.equal?(obj.identifier) #=> false
         
     | 
| 
      
 256 
     | 
    
         
            +
                  #   This is probably an artifact of jRuby Numeric - Java Long conversion interaction
         
     | 
| 
      
 257 
     | 
    
         
            +
                  #   combined with hash access use of the eql? method. Work-around is to make a Ruby Integer.
         
     | 
| 
       258 
258 
     | 
    
         
             
                  Cache.new do |obj|
         
     | 
| 
       259 
259 
     | 
    
         
             
                    raise ArgumentError.new("Can't cache object without identifier: #{obj}") unless obj.identifier
         
     | 
| 
       260 
260 
     | 
    
         
             
                    obj.identifier.to_s.to_i
         
     |