composite_primary_keys 13.0.7 → 14.0.9
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.
- checksums.yaml +4 -4
 - data/History.rdoc +50 -1
 - data/README.rdoc +182 -181
 - data/Rakefile +1 -1
 - data/lib/composite_primary_keys/associations/association.rb +2 -2
 - data/lib/composite_primary_keys/associations/association_scope.rb +1 -1
 - data/lib/composite_primary_keys/associations/collection_association.rb +38 -31
 - data/lib/composite_primary_keys/associations/has_many_through_association.rb +19 -0
 - data/lib/composite_primary_keys/associations/preloader/association.rb +52 -61
 - data/lib/composite_primary_keys/autosave_association.rb +60 -60
 - data/lib/composite_primary_keys/composite_arrays.rb +88 -86
 - data/lib/composite_primary_keys/composite_predicates.rb +121 -120
 - data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +1 -2
 - data/lib/composite_primary_keys/nested_attributes.rb +2 -2
 - data/lib/composite_primary_keys/persistence.rb +96 -83
 - data/lib/composite_primary_keys/relation/calculations.rb +110 -104
 - data/lib/composite_primary_keys/relation/query_methods.rb +14 -16
 - data/lib/composite_primary_keys/relation.rb +2 -0
 - data/lib/composite_primary_keys/validations/uniqueness.rb +40 -32
 - data/lib/composite_primary_keys/version.rb +2 -2
 - data/lib/composite_primary_keys.rb +117 -119
 - data/scripts/console.rb +2 -2
 - data/tasks/databases/trilogy.rake +23 -0
 - data/test/abstract_unit.rb +124 -114
 - data/test/connections/databases.ci.yml +10 -0
 - data/test/fixtures/admin.rb +4 -0
 - data/test/fixtures/comments.yml +6 -0
 - data/test/fixtures/db_definitions/db2-create-tables.sql +34 -0
 - data/test/fixtures/db_definitions/db2-drop-tables.sql +7 -1
 - data/test/fixtures/db_definitions/mysql.sql +23 -0
 - data/test/fixtures/db_definitions/oracle.drop.sql +4 -0
 - data/test/fixtures/db_definitions/oracle.sql +21 -0
 - data/test/fixtures/db_definitions/postgresql.sql +23 -0
 - data/test/fixtures/db_definitions/sqlite.sql +21 -0
 - data/test/fixtures/db_definitions/sqlserver.sql +23 -0
 - data/test/fixtures/department.rb +20 -16
 - data/test/fixtures/moderator.rb +4 -0
 - data/test/fixtures/room.rb +4 -1
 - data/test/fixtures/room_assignment.rb +18 -14
 - data/test/fixtures/staff_room.rb +6 -0
 - data/test/fixtures/staff_room_key.rb +6 -0
 - data/test/fixtures/user.rb +3 -0
 - data/test/fixtures/user_with_polymorphic_name.rb +9 -0
 - data/test/test_associations.rb +403 -372
 - data/test/test_composite_arrays.rb +44 -38
 - data/test/test_has_one_through.rb +30 -0
 - data/test/test_nested_attributes.rb +23 -0
 - data/test/test_polymorphic.rb +6 -0
 - metadata +14 -7
 - data/lib/composite_primary_keys/associations/through_association.rb +0 -24
 
| 
         @@ -61,12 +61,12 @@ module ActiveRecord 
     | 
|
| 
       61 
61 
     | 
    
         
             
                      unless reject_new_record?(association_name, attributes)
         
     | 
| 
       62 
62 
     | 
    
         
             
                        association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
         
     | 
| 
       63 
63 
     | 
    
         
             
                      end
         
     | 
| 
       64 
     | 
    
         
            -
                    elsif existing_record =  
     | 
| 
      
 64 
     | 
    
         
            +
                    elsif existing_record = cpk_detect_record(attributes['id'], existing_records)
         
     | 
| 
       65 
65 
     | 
    
         
             
                      unless call_reject_if(association_name, attributes)
         
     | 
| 
       66 
66 
     | 
    
         
             
                        # Make sure we are operating on the actual object which is in the association's
         
     | 
| 
       67 
67 
     | 
    
         
             
                        # proxy_target array (either by finding it, or adding it if not found)
         
     | 
| 
       68 
68 
     | 
    
         
             
                        # Take into account that the proxy_target may have changed due to callbacks
         
     | 
| 
       69 
     | 
    
         
            -
                        target_record =  
     | 
| 
      
 69 
     | 
    
         
            +
                        target_record = cpk_detect_record(attributes['id'], association.target)
         
     | 
| 
       70 
70 
     | 
    
         
             
                        if target_record
         
     | 
| 
       71 
71 
     | 
    
         
             
                          existing_record = target_record
         
     | 
| 
       72 
72 
     | 
    
         
             
                        else
         
     | 
| 
         @@ -1,83 +1,96 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module ActiveRecord
         
     | 
| 
       2 
     | 
    
         
            -
              module Persistence
         
     | 
| 
       3 
     | 
    
         
            -
                module ClassMethods
         
     | 
| 
       4 
     | 
    
         
            -
                  def delete(id_or_array)
         
     | 
| 
       5 
     | 
    
         
            -
                    # CPK
         
     | 
| 
       6 
     | 
    
         
            -
                    if self.composite?
         
     | 
| 
       7 
     | 
    
         
            -
                      id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
         
     | 
| 
       8 
     | 
    
         
            -
                                      [id_or_array]
         
     | 
| 
       9 
     | 
    
         
            -
                                    else
         
     | 
| 
       10 
     | 
    
         
            -
                                      Array(id_or_array)
         
     | 
| 
       11 
     | 
    
         
            -
                                    end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                      # Delete should return the number of deleted records
         
     | 
| 
       14 
     | 
    
         
            -
                      id_or_array.map do |id|
         
     | 
| 
       15 
     | 
    
         
            -
                        # Is the passed in id actually a record?
         
     | 
| 
       16 
     | 
    
         
            -
                        id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
         
     | 
| 
       17 
     | 
    
         
            -
                        delete_by(cpk_id_predicate(self.arel_table, self.primary_key, id))
         
     | 
| 
       18 
     | 
    
         
            -
                      end.sum
         
     | 
| 
       19 
     | 
    
         
            -
                    else
         
     | 
| 
       20 
     | 
    
         
            -
                      delete_by(primary_key => id_or_array)
         
     | 
| 
       21 
     | 
    
         
            -
                    end
         
     | 
| 
       22 
     | 
    
         
            -
                  end
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                  def _update_record(values, constraints) # :nodoc:
         
     | 
| 
       25 
     | 
    
         
            -
                    # CPK
         
     | 
| 
       26 
     | 
    
         
            -
                    if self.composite? && constraints[primary_key]
         
     | 
| 
       27 
     | 
    
         
            -
                      primary_key_values = constraints.delete(primary_key)
         
     | 
| 
       28 
     | 
    
         
            -
                      primary_key.each_with_index do |key, i|
         
     | 
| 
       29 
     | 
    
         
            -
                       constraints[key] = primary_key_values[i]
         
     | 
| 
       30 
     | 
    
         
            -
                      end
         
     | 
| 
       31 
     | 
    
         
            -
                    end
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                    constraints =  
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
       44 
     | 
    
         
            -
                     
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                     
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                     
     | 
| 
       71 
     | 
    
         
            -
                   
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                   
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            module ActiveRecord
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Persistence
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def delete(id_or_array)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    # CPK
         
     | 
| 
      
 6 
     | 
    
         
            +
                    if self.composite?
         
     | 
| 
      
 7 
     | 
    
         
            +
                      id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
         
     | 
| 
      
 8 
     | 
    
         
            +
                                      [id_or_array]
         
     | 
| 
      
 9 
     | 
    
         
            +
                                    else
         
     | 
| 
      
 10 
     | 
    
         
            +
                                      Array(id_or_array)
         
     | 
| 
      
 11 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                      # Delete should return the number of deleted records
         
     | 
| 
      
 14 
     | 
    
         
            +
                      id_or_array.map do |id|
         
     | 
| 
      
 15 
     | 
    
         
            +
                        # Is the passed in id actually a record?
         
     | 
| 
      
 16 
     | 
    
         
            +
                        id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
         
     | 
| 
      
 17 
     | 
    
         
            +
                        delete_by(cpk_id_predicate(self.arel_table, self.primary_key, id))
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end.sum
         
     | 
| 
      
 19 
     | 
    
         
            +
                    else
         
     | 
| 
      
 20 
     | 
    
         
            +
                      delete_by(primary_key => id_or_array)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def _update_record(values, constraints) # :nodoc:
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # CPK
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if self.composite? && constraints[primary_key]
         
     | 
| 
      
 27 
     | 
    
         
            +
                      primary_key_values = constraints.delete(primary_key)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      primary_key.each_with_index do |key, i|
         
     | 
| 
      
 29 
     | 
    
         
            +
                       constraints[key] = primary_key_values[i]
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    constraints = constraints.map { |name, value| predicate_builder[name, value] }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    default_constraint = build_default_constraint
         
     | 
| 
      
 36 
     | 
    
         
            +
                    constraints << default_constraint if default_constraint
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    if current_scope = self.global_current_scope
         
     | 
| 
      
 39 
     | 
    
         
            +
                      constraints << current_scope.where_clause.ast
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    um = Arel::UpdateManager.new(arel_table)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    um.set(values.transform_keys { |name| arel_table[name] })
         
     | 
| 
      
 44 
     | 
    
         
            +
                    um.wheres = constraints
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    connection.update(um, "#{self} Update")
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def _delete_record(constraints) # :nodoc:
         
     | 
| 
      
 50 
     | 
    
         
            +
                    # CPK
         
     | 
| 
      
 51 
     | 
    
         
            +
                    if self.composite? && constraints[primary_key]
         
     | 
| 
      
 52 
     | 
    
         
            +
                      primary_key_values = constraints.delete(primary_key)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      primary_key.each_with_index do |key, i|
         
     | 
| 
      
 54 
     | 
    
         
            +
                        constraints[key] = primary_key_values[i]
         
     | 
| 
      
 55 
     | 
    
         
            +
                      end
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    constraints = constraints.map { |name, value| predicate_builder[name, value] }
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    default_constraint = build_default_constraint
         
     | 
| 
      
 61 
     | 
    
         
            +
                    constraints << default_constraint if default_constraint
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    if current_scope = self.global_current_scope
         
     | 
| 
      
 64 
     | 
    
         
            +
                      constraints << current_scope.where_clause.ast
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    dm = Arel::DeleteManager.new(arel_table)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    dm.wheres = constraints
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    connection.delete(dm, "#{self} Destroy")
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def _create_record(attribute_names = self.attribute_names)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  attribute_names = attributes_for_create(attribute_names)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  new_id = self.class._insert_record(
         
     | 
| 
      
 78 
     | 
    
         
            +
                      attributes_with_values(attribute_names)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  )
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  # CPK
         
     | 
| 
      
 82 
     | 
    
         
            +
                  if self.composite?
         
     | 
| 
      
 83 
     | 
    
         
            +
                    self.id = self.id.zip(Array(new_id)).map {|id1, id2| id2.nil? ? id1 : id2}
         
     | 
| 
      
 84 
     | 
    
         
            +
                  else
         
     | 
| 
      
 85 
     | 
    
         
            +
                    self.id ||= new_id if self.class.primary_key
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  @new_record = false
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @previously_new_record = true
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  yield(self) if block_given?
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  id
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,104 +1,110 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module CompositePrimaryKeys
         
     | 
| 
       2 
     | 
    
         
            -
              module ActiveRecord
         
     | 
| 
       3 
     | 
    
         
            -
                module Calculations
         
     | 
| 
       4 
     | 
    
         
            -
                  def aggregate_column(column_name)
         
     | 
| 
       5 
     | 
    
         
            -
                    # CPK
         
     | 
| 
       6 
     | 
    
         
            -
                    if column_name.kind_of?(Array)
         
     | 
| 
       7 
     | 
    
         
            -
                      # Note: Test don't seem to run this code?
         
     | 
| 
       8 
     | 
    
         
            -
                      column_name.map do |column|
         
     | 
| 
       9 
     | 
    
         
            -
                        @klass.arel_table[column]
         
     | 
| 
       10 
     | 
    
         
            -
                      end
         
     | 
| 
       11 
     | 
    
         
            -
                    elsif @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
         
     | 
| 
       12 
     | 
    
         
            -
                      @klass.arel_table[column_name]
         
     | 
| 
       13 
     | 
    
         
            -
                    else
         
     | 
| 
       14 
     | 
    
         
            -
                      Arel.sql(column_name == :all ? "*" : column_name.to_s)
         
     | 
| 
       15 
     | 
    
         
            -
                    end
         
     | 
| 
       16 
     | 
    
         
            -
                  end
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
         
     | 
| 
       19 
     | 
    
         
            -
                    column_alias = column_name
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                    # CPK
         
     | 
| 
       22 
     | 
    
         
            -
                    # if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
         
     | 
| 
       23 
     | 
    
         
            -
                    #   # Shortcut when limit is zero.
         
     | 
| 
       24 
     | 
    
         
            -
                    #   return 0 if limit_value == 0
         
     | 
| 
       25 
     | 
    
         
            -
                    #
         
     | 
| 
       26 
     | 
    
         
            -
                    #   query_builder = build_count_subquery(spawn, column_name, distinct)
         
     | 
| 
       27 
     | 
    
         
            -
                    if operation == "count"
         
     | 
| 
       28 
     | 
    
         
            -
                      relation = unscope(:order)
         
     | 
| 
       29 
     | 
    
         
            -
                      query_builder = build_count_subquery(spawn, column_name, distinct)
         
     | 
| 
       30 
     | 
    
         
            -
                    else
         
     | 
| 
       31 
     | 
    
         
            -
                      # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
         
     | 
| 
       32 
     | 
    
         
            -
                      relation = unscope(:order).distinct!(false)
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                      column = aggregate_column(column_name)
         
     | 
| 
       35 
     | 
    
         
            -
                      select_value = operation_over_aggregate_column(column, operation, distinct)
         
     | 
| 
       36 
     | 
    
         
            -
                      select_value.distinct = true if operation == "sum" && distinct
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                      relation.select_values = [select_value]
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                      query_builder = relation.arel
         
     | 
| 
       41 
     | 
    
         
            -
                    end
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                    result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                     
     | 
| 
       46 
     | 
    
         
            -
                      type = column.try(:type_caster) ||
         
     | 
| 
       47 
     | 
    
         
            -
                         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                     
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
                       
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                       
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                       
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
                       
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                     
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
                     
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                           
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
                           
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            module CompositePrimaryKeys
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ActiveRecord
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Calculations
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def aggregate_column(column_name)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    # CPK
         
     | 
| 
      
 6 
     | 
    
         
            +
                    if column_name.kind_of?(Array)
         
     | 
| 
      
 7 
     | 
    
         
            +
                      # Note: Test don't seem to run this code?
         
     | 
| 
      
 8 
     | 
    
         
            +
                      column_name.map do |column|
         
     | 
| 
      
 9 
     | 
    
         
            +
                        @klass.arel_table[column]
         
     | 
| 
      
 10 
     | 
    
         
            +
                      end
         
     | 
| 
      
 11 
     | 
    
         
            +
                    elsif @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      @klass.arel_table[column_name]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    else
         
     | 
| 
      
 14 
     | 
    
         
            +
                      Arel.sql(column_name == :all ? "*" : column_name.to_s)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
         
     | 
| 
      
 19 
     | 
    
         
            +
                    column_alias = column_name
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    # CPK
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    #   # Shortcut when limit is zero.
         
     | 
| 
      
 24 
     | 
    
         
            +
                    #   return 0 if limit_value == 0
         
     | 
| 
      
 25 
     | 
    
         
            +
                    #
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #   query_builder = build_count_subquery(spawn, column_name, distinct)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    if operation == "count"
         
     | 
| 
      
 28 
     | 
    
         
            +
                      relation = unscope(:order)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      query_builder = build_count_subquery(spawn, column_name, distinct)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    else
         
     | 
| 
      
 31 
     | 
    
         
            +
                      # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
         
     | 
| 
      
 32 
     | 
    
         
            +
                      relation = unscope(:order).distinct!(false)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      column = aggregate_column(column_name)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      select_value = operation_over_aggregate_column(column, operation, distinct)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      select_value.distinct = true if operation == "sum" && distinct
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      relation.select_values = [select_value]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      query_builder = relation.arel
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    if operation != "count"
         
     | 
| 
      
 46 
     | 
    
         
            +
                      type = column.try(:type_caster) ||
         
     | 
| 
      
 47 
     | 
    
         
            +
                        lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
         
     | 
| 
      
 48 
     | 
    
         
            +
                      type = type.subtype if ::ActiveRecord::Enum::EnumType === type
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    type_cast_calculated_value(result.cast_values.first, operation, type) do |value|
         
     | 
| 
      
 52 
     | 
    
         
            +
                      type = column.try(:type_caster) ||
         
     | 
| 
      
 53 
     | 
    
         
            +
                        # CPK
         
     | 
| 
      
 54 
     | 
    
         
            +
                        # lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
         
     | 
| 
      
 55 
     | 
    
         
            +
                        lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
         
     | 
| 
      
 56 
     | 
    
         
            +
                      type.deserialize(value)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def build_count_subquery(relation, column_name, distinct)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    if column_name == :all
         
     | 
| 
      
 62 
     | 
    
         
            +
                      column_alias = Arel.star
         
     | 
| 
      
 63 
     | 
    
         
            +
                      # CPK
         
     | 
| 
      
 64 
     | 
    
         
            +
                      # relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
         
     | 
| 
      
 65 
     | 
    
         
            +
                      relation.select_values = [ Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE) ] unless distinct
         
     | 
| 
      
 66 
     | 
    
         
            +
                    elsif column_name.is_a?(Array)
         
     | 
| 
      
 67 
     | 
    
         
            +
                      column_alias = Arel.star
         
     | 
| 
      
 68 
     | 
    
         
            +
                      relation.select_values = column_name.map do |column|
         
     | 
| 
      
 69 
     | 
    
         
            +
                        Arel::Attribute.new(@klass.unscoped.table, column)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                    else
         
     | 
| 
      
 72 
     | 
    
         
            +
                      column_alias = Arel.sql("count_column")
         
     | 
| 
      
 73 
     | 
    
         
            +
                      relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
         
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    subquery_alias = Arel.sql("subquery_for_count")
         
     | 
| 
      
 77 
     | 
    
         
            +
                    select_value = operation_over_aggregate_column(column_alias, "count", false)
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    relation.build_subquery(subquery_alias, select_value)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def calculate(operation, column_name)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    if has_include?(column_name)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      relation = apply_join_dependency
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                      if operation.to_s.downcase == "count"
         
     | 
| 
      
 87 
     | 
    
         
            +
                        unless distinct_value || distinct_select?(column_name || select_for_count)
         
     | 
| 
      
 88 
     | 
    
         
            +
                          relation.distinct!
         
     | 
| 
      
 89 
     | 
    
         
            +
                          # CPK
         
     | 
| 
      
 90 
     | 
    
         
            +
                          # relation.select_values = [ klass.primary_key || table[Arel.star] ]
         
     | 
| 
      
 91 
     | 
    
         
            +
                          if klass.primary_key.present? && klass.primary_key.is_a?(Array)
         
     | 
| 
      
 92 
     | 
    
         
            +
                            relation.select_values = klass.primary_key.map do |k|
         
     | 
| 
      
 93 
     | 
    
         
            +
                              "#{connection.quote_table_name(klass.table_name)}.#{connection.quote_column_name(k)}"
         
     | 
| 
      
 94 
     | 
    
         
            +
                            end
         
     | 
| 
      
 95 
     | 
    
         
            +
                          else
         
     | 
| 
      
 96 
     | 
    
         
            +
                            relation.select_values = [ klass.primary_key || table[Arel.star] ]
         
     | 
| 
      
 97 
     | 
    
         
            +
                          end
         
     | 
| 
      
 98 
     | 
    
         
            +
                        end
         
     | 
| 
      
 99 
     | 
    
         
            +
                        # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
         
     | 
| 
      
 100 
     | 
    
         
            +
                        relation.order_values = [] if group_values.empty?
         
     | 
| 
      
 101 
     | 
    
         
            +
                      end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                      relation.calculate(operation, column_name)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    else
         
     | 
| 
      
 105 
     | 
    
         
            +
                      perform_calculation(operation, column_name)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
              end
         
     | 
| 
      
 110 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -18,23 +18,21 @@ module CompositePrimaryKeys 
     | 
|
| 
       18 
18 
     | 
    
         
             
                    end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                    order_query.flat_map do |o|
         
     | 
| 
       21 
     | 
    
         
            -
                       
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
                         
     | 
| 
       28 
     | 
    
         
            -
                           
     | 
| 
       29 
     | 
    
         
            -
                            raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
         
     | 
| 
       30 
     | 
    
         
            -
                          end
         
     | 
| 
       31 
     | 
    
         
            -
                          o.split(",").map! do |s|
         
     | 
| 
       32 
     | 
    
         
            -
                            s.strip!
         
     | 
| 
       33 
     | 
    
         
            -
                            s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
         
     | 
| 
       34 
     | 
    
         
            -
                          end
         
     | 
| 
       35 
     | 
    
         
            -
                        else
         
     | 
| 
       36 
     | 
    
         
            -
                          o
         
     | 
| 
      
 21 
     | 
    
         
            +
                      case o
         
     | 
| 
      
 22 
     | 
    
         
            +
                      when Arel::Attribute
         
     | 
| 
      
 23 
     | 
    
         
            +
                        o.desc
         
     | 
| 
      
 24 
     | 
    
         
            +
                      when Arel::Nodes::Ordering
         
     | 
| 
      
 25 
     | 
    
         
            +
                        o.reverse
         
     | 
| 
      
 26 
     | 
    
         
            +
                      when String
         
     | 
| 
      
 27 
     | 
    
         
            +
                        if does_not_support_reverse?(o)
         
     | 
| 
      
 28 
     | 
    
         
            +
                          raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
         
     | 
| 
       37 
29 
     | 
    
         
             
                        end
         
     | 
| 
      
 30 
     | 
    
         
            +
                        o.split(",").map! do |s|
         
     | 
| 
      
 31 
     | 
    
         
            +
                          s.strip!
         
     | 
| 
      
 32 
     | 
    
         
            +
                          s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
         
     | 
| 
      
 33 
     | 
    
         
            +
                        end
         
     | 
| 
      
 34 
     | 
    
         
            +
                      else
         
     | 
| 
      
 35 
     | 
    
         
            +
                        o
         
     | 
| 
       38 
36 
     | 
    
         
             
                      end
         
     | 
| 
       39 
37 
     | 
    
         
             
                    end
         
     | 
| 
       40 
38 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -102,6 +102,8 @@ module ActiveRecord 
     | 
|
| 
       102 
102 
     | 
    
         
             
                  # database adapter to decide how to proceed.
         
     | 
| 
       103 
103 
     | 
    
         
             
                  if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && connection.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
         
     | 
| 
       104 
104 
     | 
    
         
             
                    cpk_mysql_subquery(stmt)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  elsif defined?(ActiveRecord::ConnectionAdapters::TrilogyAdapter) && connection.is_a?(ActiveRecord::ConnectionAdapters::TrilogyAdapter)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    cpk_mysql_subquery(stmt)
         
     | 
| 
       105 
107 
     | 
    
         
             
                  elsif defined?(ActiveRecord::ConnectionAdapters::SQLServerAdapter) && connection.is_a?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
         
     | 
| 
       106 
108 
     | 
    
         
             
                    cpk_exists_subquery(stmt)
         
     | 
| 
       107 
109 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -1,32 +1,40 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module ActiveRecord
         
     | 
| 
       2 
     | 
    
         
            -
              module Validations
         
     | 
| 
       3 
     | 
    
         
            -
                class UniquenessValidator
         
     | 
| 
       4 
     | 
    
         
            -
                  def validate_each(record, attribute, value)
         
     | 
| 
       5 
     | 
    
         
            -
                    finder_class = find_finder_class_for(record)
         
     | 
| 
       6 
     | 
    
         
            -
                    value = map_enum_attribute(finder_class, attribute, value)
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                    relation = build_relation(finder_class, attribute, value)
         
     | 
| 
       9 
     | 
    
         
            -
                    if record.persisted?
         
     | 
| 
       10 
     | 
    
         
            -
                      # CPK
         
     | 
| 
       11 
     | 
    
         
            -
                      if finder_class.primary_key.is_a?(Array)
         
     | 
| 
       12 
     | 
    
         
            -
                        predicate = finder_class.cpk_id_predicate(finder_class.arel_table, finder_class.primary_key, record.id_in_database || record.id)
         
     | 
| 
       13 
     | 
    
         
            -
                        relation = relation.where.not(predicate)
         
     | 
| 
       14 
     | 
    
         
            -
                      elsif finder_class.primary_key
         
     | 
| 
       15 
     | 
    
         
            -
                        relation = relation.where.not(finder_class.primary_key => record.id_in_database)
         
     | 
| 
       16 
     | 
    
         
            -
                      else
         
     | 
| 
       17 
     | 
    
         
            -
                        raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
         
     | 
| 
       18 
     | 
    
         
            -
                      end
         
     | 
| 
       19 
     | 
    
         
            -
                    end
         
     | 
| 
       20 
     | 
    
         
            -
                    relation = scope_relation(record, relation)
         
     | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                       
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            module ActiveRecord
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validations
         
     | 
| 
      
 3 
     | 
    
         
            +
                class UniquenessValidator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def validate_each(record, attribute, value)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    finder_class = find_finder_class_for(record)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    value = map_enum_attribute(finder_class, attribute, value)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                    relation = build_relation(finder_class, attribute, value)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    if record.persisted?
         
     | 
| 
      
 10 
     | 
    
         
            +
                      # CPK
         
     | 
| 
      
 11 
     | 
    
         
            +
                      if finder_class.primary_key.is_a?(Array)
         
     | 
| 
      
 12 
     | 
    
         
            +
                        predicate = finder_class.cpk_id_predicate(finder_class.arel_table, finder_class.primary_key, record.id_in_database || record.id)
         
     | 
| 
      
 13 
     | 
    
         
            +
                        relation = relation.where.not(predicate)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      elsif finder_class.primary_key
         
     | 
| 
      
 15 
     | 
    
         
            +
                        relation = relation.where.not(finder_class.primary_key => record.id_in_database)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      else
         
     | 
| 
      
 17 
     | 
    
         
            +
                        raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                    relation = scope_relation(record, relation)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    if options[:conditions]
         
     | 
| 
      
 22 
     | 
    
         
            +
                      conditions = options[:conditions]
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                      relation = if conditions.arity.zero?
         
     | 
| 
      
 25 
     | 
    
         
            +
                        relation.instance_exec(&conditions)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      else
         
     | 
| 
      
 27 
     | 
    
         
            +
                        relation.instance_exec(record, &conditions)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    if relation.exists?
         
     | 
| 
      
 32 
     | 
    
         
            +
                      error_options = options.except(:case_sensitive, :scope, :conditions)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      error_options[:value] = value
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      record.errors.add(attribute, :taken, **error_options)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     |