activerecord 3.2.22.4 → 4.0.13
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +2799 -617
 - data/MIT-LICENSE +1 -1
 - data/README.rdoc +23 -32
 - data/examples/performance.rb +1 -1
 - data/lib/active_record/aggregations.rb +40 -34
 - data/lib/active_record/association_relation.rb +22 -0
 - data/lib/active_record/associations/alias_tracker.rb +4 -2
 - data/lib/active_record/associations/association.rb +60 -46
 - data/lib/active_record/associations/association_scope.rb +46 -40
 - data/lib/active_record/associations/belongs_to_association.rb +17 -4
 - data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
 - data/lib/active_record/associations/builder/association.rb +81 -28
 - data/lib/active_record/associations/builder/belongs_to.rb +73 -56
 - data/lib/active_record/associations/builder/collection_association.rb +54 -40
 - data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
 - data/lib/active_record/associations/builder/has_many.rb +8 -64
 - data/lib/active_record/associations/builder/has_one.rb +13 -50
 - data/lib/active_record/associations/builder/singular_association.rb +13 -13
 - data/lib/active_record/associations/collection_association.rb +130 -96
 - data/lib/active_record/associations/collection_proxy.rb +916 -63
 - data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
 - data/lib/active_record/associations/has_many_association.rb +35 -8
 - data/lib/active_record/associations/has_many_through_association.rb +37 -17
 - data/lib/active_record/associations/has_one_association.rb +42 -19
 - data/lib/active_record/associations/has_one_through_association.rb +1 -1
 - data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
 - data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
 - data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
 - data/lib/active_record/associations/join_dependency.rb +30 -9
 - data/lib/active_record/associations/join_helper.rb +1 -11
 - data/lib/active_record/associations/preloader/association.rb +29 -33
 - data/lib/active_record/associations/preloader/collection_association.rb +1 -1
 - data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
 - data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
 - data/lib/active_record/associations/preloader/has_one.rb +1 -1
 - data/lib/active_record/associations/preloader/through_association.rb +13 -17
 - data/lib/active_record/associations/preloader.rb +20 -43
 - data/lib/active_record/associations/singular_association.rb +11 -11
 - data/lib/active_record/associations/through_association.rb +3 -3
 - data/lib/active_record/associations.rb +223 -282
 - data/lib/active_record/attribute_assignment.rb +134 -154
 - data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
 - data/lib/active_record/attribute_methods/dirty.rb +36 -29
 - data/lib/active_record/attribute_methods/primary_key.rb +45 -31
 - data/lib/active_record/attribute_methods/query.rb +5 -4
 - data/lib/active_record/attribute_methods/read.rb +67 -90
 - data/lib/active_record/attribute_methods/serialization.rb +133 -70
 - data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
 - data/lib/active_record/attribute_methods/write.rb +34 -39
 - data/lib/active_record/attribute_methods.rb +268 -108
 - data/lib/active_record/autosave_association.rb +80 -73
 - data/lib/active_record/base.rb +54 -451
 - data/lib/active_record/callbacks.rb +60 -22
 - data/lib/active_record/coders/yaml_column.rb +18 -21
 - data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
 - data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
 - data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
 - data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
 - data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
 - data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
 - data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
 - data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
 - data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
 - data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
 - data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
 - data/lib/active_record/connection_adapters/column.rb +67 -36
 - data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
 - data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
 - data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
 - data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
 - data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
 - data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
 - data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
 - data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
 - data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
 - data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
 - data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
 - data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
 - data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
 - data/lib/active_record/connection_handling.rb +98 -0
 - data/lib/active_record/core.rb +472 -0
 - data/lib/active_record/counter_cache.rb +107 -108
 - data/lib/active_record/dynamic_matchers.rb +115 -63
 - data/lib/active_record/errors.rb +36 -18
 - data/lib/active_record/explain.rb +15 -63
 - data/lib/active_record/explain_registry.rb +30 -0
 - data/lib/active_record/explain_subscriber.rb +8 -4
 - data/lib/active_record/fixture_set/file.rb +55 -0
 - data/lib/active_record/fixtures.rb +159 -155
 - data/lib/active_record/inheritance.rb +93 -59
 - data/lib/active_record/integration.rb +8 -8
 - data/lib/active_record/locale/en.yml +8 -1
 - data/lib/active_record/locking/optimistic.rb +39 -43
 - data/lib/active_record/locking/pessimistic.rb +4 -4
 - data/lib/active_record/log_subscriber.rb +19 -9
 - data/lib/active_record/migration/command_recorder.rb +102 -33
 - data/lib/active_record/migration/join_table.rb +15 -0
 - data/lib/active_record/migration.rb +411 -173
 - data/lib/active_record/model_schema.rb +81 -94
 - data/lib/active_record/nested_attributes.rb +173 -131
 - data/lib/active_record/null_relation.rb +67 -0
 - data/lib/active_record/persistence.rb +254 -106
 - data/lib/active_record/query_cache.rb +18 -36
 - data/lib/active_record/querying.rb +19 -15
 - data/lib/active_record/railtie.rb +113 -38
 - data/lib/active_record/railties/console_sandbox.rb +3 -4
 - data/lib/active_record/railties/controller_runtime.rb +4 -3
 - data/lib/active_record/railties/databases.rake +115 -368
 - data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
 - data/lib/active_record/readonly_attributes.rb +7 -3
 - data/lib/active_record/reflection.rb +110 -61
 - data/lib/active_record/relation/batches.rb +29 -29
 - data/lib/active_record/relation/calculations.rb +155 -125
 - data/lib/active_record/relation/delegation.rb +94 -18
 - data/lib/active_record/relation/finder_methods.rb +151 -203
 - data/lib/active_record/relation/merger.rb +188 -0
 - data/lib/active_record/relation/predicate_builder.rb +85 -42
 - data/lib/active_record/relation/query_methods.rb +793 -146
 - data/lib/active_record/relation/spawn_methods.rb +43 -150
 - data/lib/active_record/relation.rb +293 -173
 - data/lib/active_record/result.rb +48 -7
 - data/lib/active_record/runtime_registry.rb +17 -0
 - data/lib/active_record/sanitization.rb +41 -54
 - data/lib/active_record/schema.rb +19 -12
 - data/lib/active_record/schema_dumper.rb +41 -41
 - data/lib/active_record/schema_migration.rb +46 -0
 - data/lib/active_record/scoping/default.rb +56 -52
 - data/lib/active_record/scoping/named.rb +78 -103
 - data/lib/active_record/scoping.rb +54 -124
 - data/lib/active_record/serialization.rb +6 -2
 - data/lib/active_record/serializers/xml_serializer.rb +9 -15
 - data/lib/active_record/statement_cache.rb +26 -0
 - data/lib/active_record/store.rb +131 -15
 - data/lib/active_record/tasks/database_tasks.rb +204 -0
 - data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
 - data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
 - data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
 - data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
 - data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
 - data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
 - data/lib/active_record/test_case.rb +67 -38
 - data/lib/active_record/timestamp.rb +16 -11
 - data/lib/active_record/transactions.rb +73 -51
 - data/lib/active_record/validations/associated.rb +19 -13
 - data/lib/active_record/validations/presence.rb +65 -0
 - data/lib/active_record/validations/uniqueness.rb +110 -57
 - data/lib/active_record/validations.rb +18 -17
 - data/lib/active_record/version.rb +7 -6
 - data/lib/active_record.rb +63 -45
 - data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
 - data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
 - data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
 - data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
 - data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
 - data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
 - data/lib/rails/generators/active_record.rb +3 -5
 - metadata +43 -29
 - data/examples/associations.png +0 -0
 - data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
 - data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
 - data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
 - data/lib/active_record/dynamic_finder_match.rb +0 -68
 - data/lib/active_record/dynamic_scope_match.rb +0 -23
 - data/lib/active_record/fixtures/file.rb +0 -65
 - data/lib/active_record/identity_map.rb +0 -162
 - data/lib/active_record/observer.rb +0 -121
 - data/lib/active_record/session_store.rb +0 -360
 - data/lib/rails/generators/active_record/migration.rb +0 -15
 - data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
 - data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
 - data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
 - data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
 
| 
         @@ -1,12 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'active_support/concern'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'active_support/core_ext/class/attribute'
         
     | 
| 
       3 
1 
     | 
    
         | 
| 
       4 
2 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       5 
3 
     | 
    
         
             
              module ReadonlyAttributes
         
     | 
| 
       6 
4 
     | 
    
         
             
                extend ActiveSupport::Concern
         
     | 
| 
       7 
5 
     | 
    
         | 
| 
       8 
6 
     | 
    
         
             
                included do
         
     | 
| 
       9 
     | 
    
         
            -
                  class_attribute :_attr_readonly, : 
     | 
| 
      
 7 
     | 
    
         
            +
                  class_attribute :_attr_readonly, instance_accessor: false
         
     | 
| 
       10 
8 
     | 
    
         
             
                  self._attr_readonly = []
         
     | 
| 
       11 
9 
     | 
    
         
             
                end
         
     | 
| 
       12 
10 
     | 
    
         | 
| 
         @@ -22,5 +20,11 @@ module ActiveRecord 
     | 
|
| 
       22 
20 
     | 
    
         
             
                    self._attr_readonly
         
     | 
| 
       23 
21 
     | 
    
         
             
                  end
         
     | 
| 
       24 
22 
     | 
    
         
             
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def _attr_readonly
         
     | 
| 
      
 25 
     | 
    
         
            +
                  message = "Instance level _attr_readonly method is deprecated, please use class level method."
         
     | 
| 
      
 26 
     | 
    
         
            +
                  ActiveSupport::Deprecation.warn message
         
     | 
| 
      
 27 
     | 
    
         
            +
                  defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
       25 
29 
     | 
    
         
             
              end
         
     | 
| 
       26 
30 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,6 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'active_support/core_ext/class/attribute'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'active_support/core_ext/object/inclusion'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
1 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       5 
2 
     | 
    
         
             
              # = Active Record Reflection
         
     | 
| 
       6 
3 
     | 
    
         
             
              module Reflection # :nodoc:
         
     | 
| 
         @@ -20,13 +17,13 @@ module ActiveRecord 
     | 
|
| 
       20 
17 
     | 
    
         
             
                # MacroReflection class has info for AggregateReflection and AssociationReflection
         
     | 
| 
       21 
18 
     | 
    
         
             
                # classes.
         
     | 
| 
       22 
19 
     | 
    
         
             
                module ClassMethods
         
     | 
| 
       23 
     | 
    
         
            -
                  def create_reflection(macro, name, options, active_record)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def create_reflection(macro, name, scope, options, active_record)
         
     | 
| 
       24 
21 
     | 
    
         
             
                    case macro
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
                    when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
         
     | 
| 
      
 23 
     | 
    
         
            +
                      klass = options[:through] ? ThroughReflection : AssociationReflection
         
     | 
| 
      
 24 
     | 
    
         
            +
                      reflection = klass.new(macro, name, scope, options, active_record)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    when :composed_of
         
     | 
| 
      
 26 
     | 
    
         
            +
                      reflection = AggregateReflection.new(macro, name, scope, options, active_record)
         
     | 
| 
       30 
27 
     | 
    
         
             
                    end
         
     | 
| 
       31 
28 
     | 
    
         | 
| 
       32 
29 
     | 
    
         
             
                    self.reflections = self.reflections.merge(name => reflection)
         
     | 
| 
         @@ -43,7 +40,8 @@ module ActiveRecord 
     | 
|
| 
       43 
40 
     | 
    
         
             
                  #   Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
         
     | 
| 
       44 
41 
     | 
    
         
             
                  #
         
     | 
| 
       45 
42 
     | 
    
         
             
                  def reflect_on_aggregation(aggregation)
         
     | 
| 
       46 
     | 
    
         
            -
                     
     | 
| 
      
 43 
     | 
    
         
            +
                    reflection = reflections[aggregation]
         
     | 
| 
      
 44 
     | 
    
         
            +
                    reflection if reflection.is_a?(AggregateReflection)
         
     | 
| 
       47 
45 
     | 
    
         
             
                  end
         
     | 
| 
       48 
46 
     | 
    
         | 
| 
       49 
47 
     | 
    
         
             
                  # Returns an array of AssociationReflection objects for all the
         
     | 
| 
         @@ -67,7 +65,8 @@ module ActiveRecord 
     | 
|
| 
       67 
65 
     | 
    
         
             
                  #   Invoice.reflect_on_association(:line_items).macro  # returns :has_many
         
     | 
| 
       68 
66 
     | 
    
         
             
                  #
         
     | 
| 
       69 
67 
     | 
    
         
             
                  def reflect_on_association(association)
         
     | 
| 
       70 
     | 
    
         
            -
                     
     | 
| 
      
 68 
     | 
    
         
            +
                    reflection = reflections[association]
         
     | 
| 
      
 69 
     | 
    
         
            +
                    reflection if reflection.is_a?(AssociationReflection)
         
     | 
| 
       71 
70 
     | 
    
         
             
                  end
         
     | 
| 
       72 
71 
     | 
    
         | 
| 
       73 
72 
     | 
    
         
             
                  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
         
     | 
| 
         @@ -76,25 +75,31 @@ module ActiveRecord 
     | 
|
| 
       76 
75 
     | 
    
         
             
                  end
         
     | 
| 
       77 
76 
     | 
    
         
             
                end
         
     | 
| 
       78 
77 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                # Abstract base class for AggregateReflection and AssociationReflection. Objects of
         
     | 
| 
      
 78 
     | 
    
         
            +
                # Base class for AggregateReflection and AssociationReflection. Objects of
         
     | 
| 
       81 
79 
     | 
    
         
             
                # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                #   MacroReflection
         
     | 
| 
      
 82 
     | 
    
         
            +
                #     AggregateReflection
         
     | 
| 
      
 83 
     | 
    
         
            +
                #     AssociationReflection
         
     | 
| 
      
 84 
     | 
    
         
            +
                #       ThroughReflection
         
     | 
| 
       82 
85 
     | 
    
         
             
                class MacroReflection
         
     | 
| 
       83 
86 
     | 
    
         
             
                  # Returns the name of the macro.
         
     | 
| 
       84 
87 
     | 
    
         
             
                  #
         
     | 
| 
       85 
     | 
    
         
            -
                  # <tt>composed_of :balance, : 
     | 
| 
      
 88 
     | 
    
         
            +
                  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
         
     | 
| 
       86 
89 
     | 
    
         
             
                  # <tt>has_many :clients</tt> returns <tt>:clients</tt>
         
     | 
| 
       87 
90 
     | 
    
         
             
                  attr_reader :name
         
     | 
| 
       88 
91 
     | 
    
         | 
| 
       89 
92 
     | 
    
         
             
                  # Returns the macro type.
         
     | 
| 
       90 
93 
     | 
    
         
             
                  #
         
     | 
| 
       91 
     | 
    
         
            -
                  # <tt>composed_of :balance, : 
     | 
| 
      
 94 
     | 
    
         
            +
                  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
         
     | 
| 
       92 
95 
     | 
    
         
             
                  # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
         
     | 
| 
       93 
96 
     | 
    
         
             
                  attr_reader :macro
         
     | 
| 
       94 
97 
     | 
    
         | 
| 
      
 98 
     | 
    
         
            +
                  attr_reader :scope
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
       95 
100 
     | 
    
         
             
                  # Returns the hash of options used for the macro.
         
     | 
| 
       96 
101 
     | 
    
         
             
                  #
         
     | 
| 
       97 
     | 
    
         
            -
                  # <tt>composed_of :balance, : 
     | 
| 
      
 102 
     | 
    
         
            +
                  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
         
     | 
| 
       98 
103 
     | 
    
         
             
                  # <tt>has_many :clients</tt> returns +{}+
         
     | 
| 
       99 
104 
     | 
    
         
             
                  attr_reader :options
         
     | 
| 
       100 
105 
     | 
    
         | 
| 
         @@ -102,9 +107,10 @@ module ActiveRecord 
     | 
|
| 
       102 
107 
     | 
    
         | 
| 
       103 
108 
     | 
    
         
             
                  attr_reader :plural_name # :nodoc:
         
     | 
| 
       104 
109 
     | 
    
         | 
| 
       105 
     | 
    
         
            -
                  def initialize(macro, name, options, active_record)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  def initialize(macro, name, scope, options, active_record)
         
     | 
| 
       106 
111 
     | 
    
         
             
                    @macro         = macro
         
     | 
| 
       107 
112 
     | 
    
         
             
                    @name          = name
         
     | 
| 
      
 113 
     | 
    
         
            +
                    @scope         = scope
         
     | 
| 
       108 
114 
     | 
    
         
             
                    @options       = options
         
     | 
| 
       109 
115 
     | 
    
         
             
                    @active_record = active_record
         
     | 
| 
       110 
116 
     | 
    
         
             
                    @plural_name   = active_record.pluralize_table_names ?
         
     | 
| 
         @@ -113,7 +119,7 @@ module ActiveRecord 
     | 
|
| 
       113 
119 
     | 
    
         | 
| 
       114 
120 
     | 
    
         
             
                  # Returns the class for the macro.
         
     | 
| 
       115 
121 
     | 
    
         
             
                  #
         
     | 
| 
       116 
     | 
    
         
            -
                  # <tt>composed_of :balance, : 
     | 
| 
      
 122 
     | 
    
         
            +
                  # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
         
     | 
| 
       117 
123 
     | 
    
         
             
                  # <tt>has_many :clients</tt> returns the Client class
         
     | 
| 
       118 
124 
     | 
    
         
             
                  def klass
         
     | 
| 
       119 
125 
     | 
    
         
             
                    @klass ||= class_name.constantize
         
     | 
| 
         @@ -121,7 +127,7 @@ module ActiveRecord 
     | 
|
| 
       121 
127 
     | 
    
         | 
| 
       122 
128 
     | 
    
         
             
                  # Returns the class name for the macro.
         
     | 
| 
       123 
129 
     | 
    
         
             
                  #
         
     | 
| 
       124 
     | 
    
         
            -
                  # <tt>composed_of :balance, : 
     | 
| 
      
 130 
     | 
    
         
            +
                  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
         
     | 
| 
       125 
131 
     | 
    
         
             
                  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
         
     | 
| 
       126 
132 
     | 
    
         
             
                  def class_name
         
     | 
| 
       127 
133 
     | 
    
         
             
                    @class_name ||= (options[:class_name] || derive_class_name).to_s
         
     | 
| 
         @@ -137,10 +143,6 @@ module ActiveRecord 
     | 
|
| 
       137 
143 
     | 
    
         
             
                      active_record == other_aggregation.active_record
         
     | 
| 
       138 
144 
     | 
    
         
             
                  end
         
     | 
| 
       139 
145 
     | 
    
         | 
| 
       140 
     | 
    
         
            -
                  def sanitized_conditions #:nodoc:
         
     | 
| 
       141 
     | 
    
         
            -
                    @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
         
     | 
| 
       142 
     | 
    
         
            -
                  end
         
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
146 
     | 
    
         
             
                  private
         
     | 
| 
       145 
147 
     | 
    
         
             
                    def derive_class_name
         
     | 
| 
       146 
148 
     | 
    
         
             
                      name.to_s.camelize
         
     | 
| 
         @@ -151,6 +153,10 @@ module ActiveRecord 
     | 
|
| 
       151 
153 
     | 
    
         
             
                # Holds all the meta-data about an aggregation as it was specified in the
         
     | 
| 
       152 
154 
     | 
    
         
             
                # Active Record class.
         
     | 
| 
       153 
155 
     | 
    
         
             
                class AggregateReflection < MacroReflection #:nodoc:
         
     | 
| 
      
 156 
     | 
    
         
            +
                  def mapping
         
     | 
| 
      
 157 
     | 
    
         
            +
                    mapping = options[:mapping] || [name, name]
         
     | 
| 
      
 158 
     | 
    
         
            +
                    mapping.first.is_a?(Array) ? mapping : [mapping]
         
     | 
| 
      
 159 
     | 
    
         
            +
                  end
         
     | 
| 
       154 
160 
     | 
    
         
             
                end
         
     | 
| 
       155 
161 
     | 
    
         | 
| 
       156 
162 
     | 
    
         
             
                # Holds all the meta-data about an association as it was specified in the
         
     | 
| 
         @@ -172,15 +178,15 @@ module ActiveRecord 
     | 
|
| 
       172 
178 
     | 
    
         
             
                    @klass ||= active_record.send(:compute_type, class_name)
         
     | 
| 
       173 
179 
     | 
    
         
             
                  end
         
     | 
| 
       174 
180 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
                  def initialize( 
     | 
| 
      
 181 
     | 
    
         
            +
                  def initialize(*args)
         
     | 
| 
       176 
182 
     | 
    
         
             
                    super
         
     | 
| 
       177 
     | 
    
         
            -
                    @collection =  
     | 
| 
      
 183 
     | 
    
         
            +
                    @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
         
     | 
| 
       178 
184 
     | 
    
         
             
                  end
         
     | 
| 
       179 
185 
     | 
    
         | 
| 
       180 
     | 
    
         
            -
                  # Returns a new, unsaved instance of the associated class. + 
     | 
| 
      
 186 
     | 
    
         
            +
                  # Returns a new, unsaved instance of the associated class. +attributes+ will
         
     | 
| 
       181 
187 
     | 
    
         
             
                  # be passed to the class's constructor.
         
     | 
| 
       182 
     | 
    
         
            -
                  def build_association( 
     | 
| 
       183 
     | 
    
         
            -
                    klass.new( 
     | 
| 
      
 188 
     | 
    
         
            +
                  def build_association(attributes, &block)
         
     | 
| 
      
 189 
     | 
    
         
            +
                    klass.new(attributes, &block)
         
     | 
| 
       184 
190 
     | 
    
         
             
                  end
         
     | 
| 
       185 
191 
     | 
    
         | 
| 
       186 
192 
     | 
    
         
             
                  def table_name
         
     | 
| 
         @@ -191,6 +197,10 @@ module ActiveRecord 
     | 
|
| 
       191 
197 
     | 
    
         
             
                    @quoted_table_name ||= klass.quoted_table_name
         
     | 
| 
       192 
198 
     | 
    
         
             
                  end
         
     | 
| 
       193 
199 
     | 
    
         | 
| 
      
 200 
     | 
    
         
            +
                  def join_table
         
     | 
| 
      
 201 
     | 
    
         
            +
                    @join_table ||= options[:join_table] || derive_join_table
         
     | 
| 
      
 202 
     | 
    
         
            +
                  end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
       194 
204 
     | 
    
         
             
                  def foreign_key
         
     | 
| 
       195 
205 
     | 
    
         
             
                    @foreign_key ||= options[:foreign_key] || derive_foreign_key
         
     | 
| 
       196 
206 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -228,8 +238,8 @@ module ActiveRecord 
     | 
|
| 
       228 
238 
     | 
    
         
             
                    end
         
     | 
| 
       229 
239 
     | 
    
         
             
                  end
         
     | 
| 
       230 
240 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
                  def columns(tbl_name 
     | 
| 
       232 
     | 
    
         
            -
                    @columns ||= klass.connection.columns(tbl_name 
     | 
| 
      
 241 
     | 
    
         
            +
                  def columns(tbl_name)
         
     | 
| 
      
 242 
     | 
    
         
            +
                    @columns ||= klass.connection.columns(tbl_name)
         
     | 
| 
       233 
243 
     | 
    
         
             
                  end
         
     | 
| 
       234 
244 
     | 
    
         | 
| 
       235 
245 
     | 
    
         
             
                  def reset_column_information
         
     | 
| 
         @@ -238,6 +248,10 @@ module ActiveRecord 
     | 
|
| 
       238 
248 
     | 
    
         | 
| 
       239 
249 
     | 
    
         
             
                  def check_validity!
         
     | 
| 
       240 
250 
     | 
    
         
             
                    check_validity_of_inverse!
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
                    if has_and_belongs_to_many? && association_foreign_key == foreign_key
         
     | 
| 
      
 253 
     | 
    
         
            +
                      raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(self)
         
     | 
| 
      
 254 
     | 
    
         
            +
                    end
         
     | 
| 
       241 
255 
     | 
    
         
             
                  end
         
     | 
| 
       242 
256 
     | 
    
         | 
| 
       243 
257 
     | 
    
         
             
                  def check_validity_of_inverse!
         
     | 
| 
         @@ -266,11 +280,10 @@ module ActiveRecord 
     | 
|
| 
       266 
280 
     | 
    
         
             
                    false
         
     | 
| 
       267 
281 
     | 
    
         
             
                  end
         
     | 
| 
       268 
282 
     | 
    
         | 
| 
       269 
     | 
    
         
            -
                  # An array of arrays of  
     | 
| 
       270 
     | 
    
         
            -
                  # in the #chain. 
     | 
| 
       271 
     | 
    
         
            -
                   
     | 
| 
       272 
     | 
    
         
            -
             
     | 
| 
       273 
     | 
    
         
            -
                    [[options[:conditions]].compact]
         
     | 
| 
      
 283 
     | 
    
         
            +
                  # An array of arrays of scopes. Each item in the outside array corresponds to a reflection
         
     | 
| 
      
 284 
     | 
    
         
            +
                  # in the #chain.
         
     | 
| 
      
 285 
     | 
    
         
            +
                  def scope_chain
         
     | 
| 
      
 286 
     | 
    
         
            +
                    scope ? [[scope]] : [[]]
         
     | 
| 
       274 
287 
     | 
    
         
             
                  end
         
     | 
| 
       275 
288 
     | 
    
         | 
| 
       276 
289 
     | 
    
         
             
                  alias :source_macro :macro
         
     | 
| 
         @@ -306,10 +319,10 @@ module ActiveRecord 
     | 
|
| 
       306 
319 
     | 
    
         
             
                  # the parent's validation.
         
     | 
| 
       307 
320 
     | 
    
         
             
                  #
         
     | 
| 
       308 
321 
     | 
    
         
             
                  # Unless you explicitly disable validation with
         
     | 
| 
       309 
     | 
    
         
            -
                  # <tt 
     | 
| 
      
 322 
     | 
    
         
            +
                  # <tt>validate: false</tt>, validation will take place when:
         
     | 
| 
       310 
323 
     | 
    
         
             
                  #
         
     | 
| 
       311 
     | 
    
         
            -
                  # * you explicitly enable validation; <tt 
     | 
| 
       312 
     | 
    
         
            -
                  # * you use autosave; <tt 
     | 
| 
      
 324 
     | 
    
         
            +
                  # * you explicitly enable validation; <tt>validate: true</tt>
         
     | 
| 
      
 325 
     | 
    
         
            +
                  # * you use autosave; <tt>autosave: true</tt>
         
     | 
| 
       313 
326 
     | 
    
         
             
                  # * the association is a +has_many+ association
         
     | 
| 
       314 
327 
     | 
    
         
             
                  def validate?
         
     | 
| 
       315 
328 
     | 
    
         
             
                    !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
         
     | 
| 
         @@ -320,6 +333,10 @@ module ActiveRecord 
     | 
|
| 
       320 
333 
     | 
    
         
             
                    macro == :belongs_to
         
     | 
| 
       321 
334 
     | 
    
         
             
                  end
         
     | 
| 
       322 
335 
     | 
    
         | 
| 
      
 336 
     | 
    
         
            +
                  def has_and_belongs_to_many?
         
     | 
| 
      
 337 
     | 
    
         
            +
                    macro == :has_and_belongs_to_many
         
     | 
| 
      
 338 
     | 
    
         
            +
                  end
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
       323 
340 
     | 
    
         
             
                  def association_class
         
     | 
| 
       324 
341 
     | 
    
         
             
                    case macro
         
     | 
| 
       325 
342 
     | 
    
         
             
                    when :belongs_to
         
     | 
| 
         @@ -345,11 +362,15 @@ module ActiveRecord 
     | 
|
| 
       345 
362 
     | 
    
         
             
                    end
         
     | 
| 
       346 
363 
     | 
    
         
             
                  end
         
     | 
| 
       347 
364 
     | 
    
         | 
| 
      
 365 
     | 
    
         
            +
                  def polymorphic?
         
     | 
| 
      
 366 
     | 
    
         
            +
                    options.key? :polymorphic
         
     | 
| 
      
 367 
     | 
    
         
            +
                  end
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
       348 
369 
     | 
    
         
             
                  private
         
     | 
| 
       349 
370 
     | 
    
         
             
                    def derive_class_name
         
     | 
| 
       350 
     | 
    
         
            -
                      class_name = name.to_s 
     | 
| 
      
 371 
     | 
    
         
            +
                      class_name = name.to_s
         
     | 
| 
       351 
372 
     | 
    
         
             
                      class_name = class_name.singularize if collection?
         
     | 
| 
       352 
     | 
    
         
            -
                      class_name
         
     | 
| 
      
 373 
     | 
    
         
            +
                      class_name.camelize
         
     | 
| 
       353 
374 
     | 
    
         
             
                    end
         
     | 
| 
       354 
375 
     | 
    
         | 
| 
       355 
376 
     | 
    
         
             
                    def derive_foreign_key
         
     | 
| 
         @@ -362,6 +383,10 @@ module ActiveRecord 
     | 
|
| 
       362 
383 
     | 
    
         
             
                      end
         
     | 
| 
       363 
384 
     | 
    
         
             
                    end
         
     | 
| 
       364 
385 
     | 
    
         | 
| 
      
 386 
     | 
    
         
            +
                    def derive_join_table
         
     | 
| 
      
 387 
     | 
    
         
            +
                      [active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
         
     | 
| 
      
 388 
     | 
    
         
            +
                    end
         
     | 
| 
      
 389 
     | 
    
         
            +
             
     | 
| 
       365 
390 
     | 
    
         
             
                    def primary_key(klass)
         
     | 
| 
       366 
391 
     | 
    
         
             
                      klass.primary_key || raise(UnknownPrimaryKey.new(klass))
         
     | 
| 
       367 
392 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -378,9 +403,19 @@ module ActiveRecord 
     | 
|
| 
       378 
403 
     | 
    
         
             
                  #
         
     | 
| 
       379 
404 
     | 
    
         
             
                  #   class Post < ActiveRecord::Base
         
     | 
| 
       380 
405 
     | 
    
         
             
                  #     has_many :taggings
         
     | 
| 
       381 
     | 
    
         
            -
                  #     has_many :tags, : 
     | 
| 
      
 406 
     | 
    
         
            +
                  #     has_many :tags, through: :taggings
         
     | 
| 
       382 
407 
     | 
    
         
             
                  #   end
         
     | 
| 
       383 
408 
     | 
    
         
             
                  #
         
     | 
| 
      
 409 
     | 
    
         
            +
                  #   class Tagging < ActiveRecord::Base
         
     | 
| 
      
 410 
     | 
    
         
            +
                  #     belongs_to :post
         
     | 
| 
      
 411 
     | 
    
         
            +
                  #     belongs_to :tag
         
     | 
| 
      
 412 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 413 
     | 
    
         
            +
                  #
         
     | 
| 
      
 414 
     | 
    
         
            +
                  #   tags_reflection = Post.reflect_on_association(:tags)
         
     | 
| 
      
 415 
     | 
    
         
            +
                  #
         
     | 
| 
      
 416 
     | 
    
         
            +
                  #   taggings_reflection = tags_reflection.source_reflection
         
     | 
| 
      
 417 
     | 
    
         
            +
                  #   # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
         
     | 
| 
      
 418 
     | 
    
         
            +
                  #
         
     | 
| 
       384 
419 
     | 
    
         
             
                  def source_reflection
         
     | 
| 
       385 
420 
     | 
    
         
             
                    @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
         
     | 
| 
       386 
421 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -390,7 +425,7 @@ module ActiveRecord 
     | 
|
| 
       390 
425 
     | 
    
         
             
                  #
         
     | 
| 
       391 
426 
     | 
    
         
             
                  #   class Post < ActiveRecord::Base
         
     | 
| 
       392 
427 
     | 
    
         
             
                  #     has_many :taggings
         
     | 
| 
       393 
     | 
    
         
            -
                  #     has_many :tags, : 
     | 
| 
      
 428 
     | 
    
         
            +
                  #     has_many :tags, through: :taggings
         
     | 
| 
       394 
429 
     | 
    
         
             
                  #   end
         
     | 
| 
       395 
430 
     | 
    
         
             
                  #
         
     | 
| 
       396 
431 
     | 
    
         
             
                  #   tags_reflection = Post.reflect_on_association(:tags)
         
     | 
| 
         @@ -406,6 +441,17 @@ module ActiveRecord 
     | 
|
| 
       406 
441 
     | 
    
         
             
                  # The chain is built by recursively calling #chain on the source reflection and the through
         
     | 
| 
       407 
442 
     | 
    
         
             
                  # reflection. The base case for the recursion is a normal association, which just returns
         
     | 
| 
       408 
443 
     | 
    
         
             
                  # [self] as its #chain.
         
     | 
| 
      
 444 
     | 
    
         
            +
                  #
         
     | 
| 
      
 445 
     | 
    
         
            +
                  #   class Post < ActiveRecord::Base
         
     | 
| 
      
 446 
     | 
    
         
            +
                  #     has_many :taggings
         
     | 
| 
      
 447 
     | 
    
         
            +
                  #     has_many :tags, through: :taggings
         
     | 
| 
      
 448 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 449 
     | 
    
         
            +
                  #
         
     | 
| 
      
 450 
     | 
    
         
            +
                  #   tags_reflection = Post.reflect_on_association(:tags)
         
     | 
| 
      
 451 
     | 
    
         
            +
                  #   tags_reflection.chain
         
     | 
| 
      
 452 
     | 
    
         
            +
                  #   # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
         
     | 
| 
      
 453 
     | 
    
         
            +
                  #         <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
         
     | 
| 
      
 454 
     | 
    
         
            +
                  #
         
     | 
| 
       409 
455 
     | 
    
         
             
                  def chain
         
     | 
| 
       410 
456 
     | 
    
         
             
                    @chain ||= begin
         
     | 
| 
       411 
457 
     | 
    
         
             
                      chain = source_reflection.chain + through_reflection.chain
         
     | 
| 
         @@ -418,41 +464,37 @@ module ActiveRecord 
     | 
|
| 
       418 
464 
     | 
    
         
             
                  #
         
     | 
| 
       419 
465 
     | 
    
         
             
                  #   class Person
         
     | 
| 
       420 
466 
     | 
    
         
             
                  #     has_many :articles
         
     | 
| 
       421 
     | 
    
         
            -
                  #     has_many :comment_tags, : 
     | 
| 
      
 467 
     | 
    
         
            +
                  #     has_many :comment_tags, through: :articles
         
     | 
| 
       422 
468 
     | 
    
         
             
                  #   end
         
     | 
| 
       423 
469 
     | 
    
         
             
                  #
         
     | 
| 
       424 
470 
     | 
    
         
             
                  #   class Article
         
     | 
| 
       425 
471 
     | 
    
         
             
                  #     has_many :comments
         
     | 
| 
       426 
     | 
    
         
            -
                  #     has_many :comment_tags, : 
     | 
| 
      
 472 
     | 
    
         
            +
                  #     has_many :comment_tags, through: :comments, source: :tags
         
     | 
| 
       427 
473 
     | 
    
         
             
                  #   end
         
     | 
| 
       428 
474 
     | 
    
         
             
                  #
         
     | 
| 
       429 
475 
     | 
    
         
             
                  #   class Comment
         
     | 
| 
       430 
476 
     | 
    
         
             
                  #     has_many :tags
         
     | 
| 
       431 
477 
     | 
    
         
             
                  #   end
         
     | 
| 
       432 
478 
     | 
    
         
             
                  #
         
     | 
| 
       433 
     | 
    
         
            -
                  # There may be  
     | 
| 
      
 479 
     | 
    
         
            +
                  # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
         
     | 
| 
       434 
480 
     | 
    
         
             
                  # but only Comment.tags will be represented in the #chain. So this method creates an array
         
     | 
| 
       435 
     | 
    
         
            -
                  # of  
     | 
| 
       436 
     | 
    
         
            -
                   
     | 
| 
       437 
     | 
    
         
            -
             
     | 
| 
       438 
     | 
    
         
            -
             
     | 
| 
       439 
     | 
    
         
            -
                    @conditions ||= begin
         
     | 
| 
       440 
     | 
    
         
            -
                      conditions = source_reflection.conditions.map { |c| c.dup }
         
     | 
| 
      
 481 
     | 
    
         
            +
                  # of scopes corresponding to the chain.
         
     | 
| 
      
 482 
     | 
    
         
            +
                  def scope_chain
         
     | 
| 
      
 483 
     | 
    
         
            +
                    @scope_chain ||= begin
         
     | 
| 
      
 484 
     | 
    
         
            +
                      scope_chain = source_reflection.scope_chain.map(&:dup)
         
     | 
| 
       441 
485 
     | 
    
         | 
| 
       442 
     | 
    
         
            -
                      # Add to it the  
     | 
| 
       443 
     | 
    
         
            -
                       
     | 
| 
      
 486 
     | 
    
         
            +
                      # Add to it the scope from this reflection (if any)
         
     | 
| 
      
 487 
     | 
    
         
            +
                      scope_chain.first << scope if scope
         
     | 
| 
       444 
488 
     | 
    
         | 
| 
       445 
     | 
    
         
            -
                       
     | 
| 
      
 489 
     | 
    
         
            +
                      through_scope_chain = through_reflection.scope_chain.map(&:dup)
         
     | 
| 
       446 
490 
     | 
    
         | 
| 
       447 
491 
     | 
    
         
             
                      if options[:source_type]
         
     | 
| 
       448 
     | 
    
         
            -
                         
     | 
| 
      
 492 
     | 
    
         
            +
                        through_scope_chain.first <<
         
     | 
| 
      
 493 
     | 
    
         
            +
                          through_reflection.klass.where(foreign_type => options[:source_type])
         
     | 
| 
       449 
494 
     | 
    
         
             
                      end
         
     | 
| 
       450 
495 
     | 
    
         | 
| 
       451 
496 
     | 
    
         
             
                      # Recursively fill out the rest of the array from the through reflection
         
     | 
| 
       452 
     | 
    
         
            -
                       
     | 
| 
       453 
     | 
    
         
            -
             
     | 
| 
       454 
     | 
    
         
            -
                      # And return
         
     | 
| 
       455 
     | 
    
         
            -
                      conditions
         
     | 
| 
      
 497 
     | 
    
         
            +
                      scope_chain + through_scope_chain
         
     | 
| 
       456 
498 
     | 
    
         
             
                    end
         
     | 
| 
       457 
499 
     | 
    
         
             
                  end
         
     | 
| 
       458 
500 
     | 
    
         | 
| 
         @@ -480,9 +522,16 @@ module ActiveRecord 
     | 
|
| 
       480 
522 
     | 
    
         
             
                    source_reflection.options[:primary_key] || primary_key(klass || self.klass)
         
     | 
| 
       481 
523 
     | 
    
         
             
                  end
         
     | 
| 
       482 
524 
     | 
    
         | 
| 
       483 
     | 
    
         
            -
                  # Gets an array of possible <tt>:through</tt> source reflection names 
     | 
| 
      
 525 
     | 
    
         
            +
                  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
         
     | 
| 
      
 526 
     | 
    
         
            +
                  #
         
     | 
| 
      
 527 
     | 
    
         
            +
                  #   class Post < ActiveRecord::Base
         
     | 
| 
      
 528 
     | 
    
         
            +
                  #     has_many :taggings
         
     | 
| 
      
 529 
     | 
    
         
            +
                  #     has_many :tags, through: :taggings
         
     | 
| 
      
 530 
     | 
    
         
            +
                  #   end
         
     | 
| 
       484 
531 
     | 
    
         
             
                  #
         
     | 
| 
       485 
     | 
    
         
            -
                  #    
     | 
| 
      
 532 
     | 
    
         
            +
                  #   tags_reflection = Post.reflect_on_association(:tags)
         
     | 
| 
      
 533 
     | 
    
         
            +
                  #   tags_reflection.source_reflection_names
         
     | 
| 
      
 534 
     | 
    
         
            +
                  #   # => [:tag, :tags]
         
     | 
| 
       486 
535 
     | 
    
         
             
                  #
         
     | 
| 
       487 
536 
     | 
    
         
             
                  def source_reflection_names
         
     | 
| 
       488 
537 
     | 
    
         
             
                    @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
         
     | 
| 
         @@ -1,21 +1,26 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'active_support/core_ext/object/blank'
         
     | 
| 
       2 
1 
     | 
    
         | 
| 
       3 
2 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       4 
3 
     | 
    
         
             
              module Batches
         
     | 
| 
       5 
     | 
    
         
            -
                #  
     | 
| 
       6 
     | 
    
         
            -
                #  
     | 
| 
       7 
     | 
    
         
            -
                #  
     | 
| 
      
 4 
     | 
    
         
            +
                # Looping through a collection of records from the database
         
     | 
| 
      
 5 
     | 
    
         
            +
                # (using the +all+ method, for example) is very inefficient
         
     | 
| 
      
 6 
     | 
    
         
            +
                # since it will try to instantiate all the objects at once.
         
     | 
| 
       8 
7 
     | 
    
         
             
                #
         
     | 
| 
       9 
     | 
    
         
            -
                #  
     | 
| 
      
 8 
     | 
    
         
            +
                # In that case, batch processing methods allow you to work
         
     | 
| 
      
 9 
     | 
    
         
            +
                # with the records in batches, thereby greatly reducing memory consumption.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # The #find_each method uses #find_in_batches with a batch size of 1000 (or as
         
     | 
| 
      
 12 
     | 
    
         
            +
                # specified by the +:batch_size+ option).
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   Person.all.find_each do |person|
         
     | 
| 
      
 15 
     | 
    
         
            +
                #     person.do_awesome_stuff
         
     | 
| 
      
 16 
     | 
    
         
            +
                #   end
         
     | 
| 
       10 
17 
     | 
    
         
             
                #
         
     | 
| 
       11 
18 
     | 
    
         
             
                #   Person.where("age > 21").find_each do |person|
         
     | 
| 
       12 
19 
     | 
    
         
             
                #     person.party_all_night!
         
     | 
| 
       13 
20 
     | 
    
         
             
                #   end
         
     | 
| 
       14 
21 
     | 
    
         
             
                #
         
     | 
| 
       15 
     | 
    
         
            -
                #  
     | 
| 
       16 
     | 
    
         
            -
                #  
     | 
| 
       17 
     | 
    
         
            -
                # you just need to loop over less than 1000 records, it's probably
         
     | 
| 
       18 
     | 
    
         
            -
                # better just to use the regular find methods.
         
     | 
| 
      
 22 
     | 
    
         
            +
                #  You can also pass the +:start+ option to specify
         
     | 
| 
      
 23 
     | 
    
         
            +
                #  an offset to control the starting point.
         
     | 
| 
       19 
24 
     | 
    
         
             
                def find_each(options = {})
         
     | 
| 
       20 
25 
     | 
    
         
             
                  find_in_batches(options) do |records|
         
     | 
| 
       21 
26 
     | 
    
         
             
                    records.each { |record| yield record }
         
     | 
| 
         @@ -23,40 +28,38 @@ module ActiveRecord 
     | 
|
| 
       23 
28 
     | 
    
         
             
                end
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
                # Yields each batch of records that was found by the find +options+ as
         
     | 
| 
       26 
     | 
    
         
            -
                # an array. The size of each batch is set by the  
     | 
| 
      
 31 
     | 
    
         
            +
                # an array. The size of each batch is set by the +:batch_size+
         
     | 
| 
       27 
32 
     | 
    
         
             
                # option; the default is 1000.
         
     | 
| 
       28 
33 
     | 
    
         
             
                #
         
     | 
| 
       29 
34 
     | 
    
         
             
                # You can control the starting point for the batch processing by
         
     | 
| 
       30 
     | 
    
         
            -
                # supplying the  
     | 
| 
      
 35 
     | 
    
         
            +
                # supplying the +:start+ option. This is especially useful if you
         
     | 
| 
       31 
36 
     | 
    
         
             
                # want multiple workers dealing with the same processing queue. You can
         
     | 
| 
       32 
37 
     | 
    
         
             
                # make worker 1 handle all the records between id 0 and 10,000 and
         
     | 
| 
       33 
     | 
    
         
            -
                # worker 2 handle from 10,000 and beyond (by setting the  
     | 
| 
      
 38 
     | 
    
         
            +
                # worker 2 handle from 10,000 and beyond (by setting the +:start+
         
     | 
| 
       34 
39 
     | 
    
         
             
                # option on that worker).
         
     | 
| 
       35 
40 
     | 
    
         
             
                #
         
     | 
| 
       36 
41 
     | 
    
         
             
                # It's not possible to set the order. That is automatically set to
         
     | 
| 
       37 
42 
     | 
    
         
             
                # ascending on the primary key ("id ASC") to make the batch ordering
         
     | 
| 
       38 
     | 
    
         
            -
                # work. This also  
     | 
| 
      
 43 
     | 
    
         
            +
                # work. This also means that this method only works with integer-based
         
     | 
| 
       39 
44 
     | 
    
         
             
                # primary keys. You can't set the limit either, that's used to control
         
     | 
| 
       40 
45 
     | 
    
         
             
                # the batch sizes.
         
     | 
| 
       41 
46 
     | 
    
         
             
                #
         
     | 
| 
       42 
     | 
    
         
            -
                # Example:
         
     | 
| 
       43 
     | 
    
         
            -
                #
         
     | 
| 
       44 
47 
     | 
    
         
             
                #   Person.where("age > 21").find_in_batches do |group|
         
     | 
| 
       45 
48 
     | 
    
         
             
                #     sleep(50) # Make sure it doesn't get too crowded in there!
         
     | 
| 
       46 
49 
     | 
    
         
             
                #     group.each { |person| person.party_all_night! }
         
     | 
| 
       47 
50 
     | 
    
         
             
                #   end
         
     | 
| 
      
 51 
     | 
    
         
            +
                #
         
     | 
| 
      
 52 
     | 
    
         
            +
                #   # Let's process the next 2000 records
         
     | 
| 
      
 53 
     | 
    
         
            +
                #   Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
         
     | 
| 
      
 54 
     | 
    
         
            +
                #     group.each { |person| person.party_all_night! }
         
     | 
| 
      
 55 
     | 
    
         
            +
                #   end
         
     | 
| 
       48 
56 
     | 
    
         
             
                def find_in_batches(options = {})
         
     | 
| 
       49 
     | 
    
         
            -
                   
     | 
| 
      
 57 
     | 
    
         
            +
                  options.assert_valid_keys(:start, :batch_size)
         
     | 
| 
       50 
58 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                   
     | 
| 
       52 
     | 
    
         
            -
                    ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
         
     | 
| 
       53 
     | 
    
         
            -
                  end
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                  if (finder_options = options.except(:start, :batch_size)).present?
         
     | 
| 
       56 
     | 
    
         
            -
                    raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
         
     | 
| 
       57 
     | 
    
         
            -
                    raise "You can't specify a limit, it's forced to be the batch_size"  if options[:limit].present?
         
     | 
| 
      
 59 
     | 
    
         
            +
                  relation = self
         
     | 
| 
       58 
60 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
      
 61 
     | 
    
         
            +
                  if logger && (arel.orders.present? || arel.taken.present?)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
         
     | 
| 
       60 
63 
     | 
    
         
             
                  end
         
     | 
| 
       61 
64 
     | 
    
         | 
| 
       62 
65 
     | 
    
         
             
                  start = options.delete(:start)
         
     | 
| 
         @@ -68,16 +71,13 @@ module ActiveRecord 
     | 
|
| 
       68 
71 
     | 
    
         
             
                  while records.any?
         
     | 
| 
       69 
72 
     | 
    
         
             
                    records_size = records.size
         
     | 
| 
       70 
73 
     | 
    
         
             
                    primary_key_offset = records.last.id
         
     | 
| 
      
 74 
     | 
    
         
            +
                    raise "Primary key not included in the custom select clause" unless primary_key_offset
         
     | 
| 
       71 
75 
     | 
    
         | 
| 
       72 
76 
     | 
    
         
             
                    yield records
         
     | 
| 
       73 
77 
     | 
    
         | 
| 
       74 
78 
     | 
    
         
             
                    break if records_size < batch_size
         
     | 
| 
       75 
79 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                     
     | 
| 
       77 
     | 
    
         
            -
                      records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
         
     | 
| 
       78 
     | 
    
         
            -
                    else
         
     | 
| 
       79 
     | 
    
         
            -
                      raise "Primary key not included in the custom select clause"
         
     | 
| 
       80 
     | 
    
         
            -
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                    records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
         
     | 
| 
       81 
81 
     | 
    
         
             
                  end
         
     | 
| 
       82 
82 
     | 
    
         
             
                end
         
     | 
| 
       83 
83 
     | 
    
         |