activerecord 3.0.0 → 4.0.0
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 +7 -0
 - data/CHANGELOG.md +2102 -0
 - data/MIT-LICENSE +20 -0
 - data/README.rdoc +35 -44
 - data/examples/performance.rb +110 -100
 - data/lib/active_record/aggregations.rb +59 -75
 - data/lib/active_record/associations/alias_tracker.rb +76 -0
 - data/lib/active_record/associations/association.rb +248 -0
 - data/lib/active_record/associations/association_scope.rb +135 -0
 - data/lib/active_record/associations/belongs_to_association.rb +60 -59
 - data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
 - data/lib/active_record/associations/builder/association.rb +108 -0
 - data/lib/active_record/associations/builder/belongs_to.rb +98 -0
 - data/lib/active_record/associations/builder/collection_association.rb +89 -0
 - data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
 - data/lib/active_record/associations/builder/has_many.rb +15 -0
 - data/lib/active_record/associations/builder/has_one.rb +25 -0
 - data/lib/active_record/associations/builder/singular_association.rb +32 -0
 - data/lib/active_record/associations/collection_association.rb +608 -0
 - data/lib/active_record/associations/collection_proxy.rb +986 -0
 - data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
 - data/lib/active_record/associations/has_many_association.rb +83 -76
 - data/lib/active_record/associations/has_many_through_association.rb +147 -66
 - data/lib/active_record/associations/has_one_association.rb +67 -108
 - data/lib/active_record/associations/has_one_through_association.rb +21 -25
 - data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
 - data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
 - data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
 - data/lib/active_record/associations/join_dependency.rb +235 -0
 - data/lib/active_record/associations/join_helper.rb +45 -0
 - data/lib/active_record/associations/preloader/association.rb +121 -0
 - data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
 - data/lib/active_record/associations/preloader/collection_association.rb +24 -0
 - data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
 - data/lib/active_record/associations/preloader/has_many.rb +17 -0
 - data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
 - data/lib/active_record/associations/preloader/has_one.rb +23 -0
 - data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
 - data/lib/active_record/associations/preloader/singular_association.rb +21 -0
 - data/lib/active_record/associations/preloader/through_association.rb +63 -0
 - data/lib/active_record/associations/preloader.rb +178 -0
 - data/lib/active_record/associations/singular_association.rb +64 -0
 - data/lib/active_record/associations/through_association.rb +87 -0
 - data/lib/active_record/associations.rb +512 -1224
 - data/lib/active_record/attribute_assignment.rb +201 -0
 - data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
 - data/lib/active_record/attribute_methods/dirty.rb +51 -28
 - data/lib/active_record/attribute_methods/primary_key.rb +94 -22
 - data/lib/active_record/attribute_methods/query.rb +5 -4
 - data/lib/active_record/attribute_methods/read.rb +63 -72
 - data/lib/active_record/attribute_methods/serialization.rb +162 -0
 - data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
 - data/lib/active_record/attribute_methods/write.rb +39 -13
 - data/lib/active_record/attribute_methods.rb +362 -29
 - data/lib/active_record/autosave_association.rb +132 -75
 - data/lib/active_record/base.rb +83 -1627
 - data/lib/active_record/callbacks.rb +69 -47
 - data/lib/active_record/coders/yaml_column.rb +38 -0
 - data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
 - data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
 - data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
 - data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
 - data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
 - data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
 - data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
 - data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
 - data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
 - data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
 - data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
 - data/lib/active_record/connection_adapters/column.rb +318 -0
 - data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
 - data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
 - data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
 - data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
 - data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
 - data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
 - data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
 - data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
 - data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
 - data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
 - data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
 - data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
 - data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
 - data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
 - data/lib/active_record/connection_handling.rb +98 -0
 - data/lib/active_record/core.rb +463 -0
 - data/lib/active_record/counter_cache.rb +108 -101
 - data/lib/active_record/dynamic_matchers.rb +131 -0
 - data/lib/active_record/errors.rb +54 -13
 - data/lib/active_record/explain.rb +38 -0
 - data/lib/active_record/explain_registry.rb +30 -0
 - data/lib/active_record/explain_subscriber.rb +29 -0
 - data/lib/active_record/fixture_set/file.rb +55 -0
 - data/lib/active_record/fixtures.rb +703 -785
 - data/lib/active_record/inheritance.rb +200 -0
 - data/lib/active_record/integration.rb +60 -0
 - data/lib/active_record/locale/en.yml +8 -1
 - data/lib/active_record/locking/optimistic.rb +69 -60
 - data/lib/active_record/locking/pessimistic.rb +34 -12
 - data/lib/active_record/log_subscriber.rb +40 -6
 - data/lib/active_record/migration/command_recorder.rb +164 -0
 - data/lib/active_record/migration/join_table.rb +15 -0
 - data/lib/active_record/migration.rb +614 -216
 - data/lib/active_record/model_schema.rb +345 -0
 - data/lib/active_record/nested_attributes.rb +248 -119
 - data/lib/active_record/null_relation.rb +65 -0
 - data/lib/active_record/persistence.rb +275 -57
 - data/lib/active_record/query_cache.rb +29 -9
 - data/lib/active_record/querying.rb +62 -0
 - data/lib/active_record/railtie.rb +135 -21
 - data/lib/active_record/railties/console_sandbox.rb +5 -0
 - data/lib/active_record/railties/controller_runtime.rb +17 -5
 - data/lib/active_record/railties/databases.rake +249 -359
 - data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
 - data/lib/active_record/readonly_attributes.rb +30 -0
 - data/lib/active_record/reflection.rb +283 -103
 - data/lib/active_record/relation/batches.rb +38 -34
 - data/lib/active_record/relation/calculations.rb +252 -139
 - data/lib/active_record/relation/delegation.rb +125 -0
 - data/lib/active_record/relation/finder_methods.rb +182 -188
 - data/lib/active_record/relation/merger.rb +161 -0
 - data/lib/active_record/relation/predicate_builder.rb +86 -21
 - data/lib/active_record/relation/query_methods.rb +917 -134
 - data/lib/active_record/relation/spawn_methods.rb +53 -92
 - data/lib/active_record/relation.rb +405 -143
 - data/lib/active_record/result.rb +67 -0
 - data/lib/active_record/runtime_registry.rb +17 -0
 - data/lib/active_record/sanitization.rb +168 -0
 - data/lib/active_record/schema.rb +20 -14
 - data/lib/active_record/schema_dumper.rb +55 -46
 - data/lib/active_record/schema_migration.rb +39 -0
 - data/lib/active_record/scoping/default.rb +146 -0
 - data/lib/active_record/scoping/named.rb +175 -0
 - data/lib/active_record/scoping.rb +82 -0
 - data/lib/active_record/serialization.rb +8 -46
 - data/lib/active_record/serializers/xml_serializer.rb +21 -68
 - data/lib/active_record/statement_cache.rb +26 -0
 - data/lib/active_record/store.rb +156 -0
 - data/lib/active_record/tasks/database_tasks.rb +203 -0
 - data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
 - data/lib/active_record/tasks/mysql_database_tasks.rb +143 -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 +57 -28
 - data/lib/active_record/timestamp.rb +49 -18
 - data/lib/active_record/transactions.rb +106 -63
 - data/lib/active_record/translation.rb +22 -0
 - data/lib/active_record/validations/associated.rb +25 -24
 - data/lib/active_record/validations/presence.rb +65 -0
 - data/lib/active_record/validations/uniqueness.rb +123 -83
 - data/lib/active_record/validations.rb +29 -29
 - data/lib/active_record/version.rb +7 -5
 - data/lib/active_record.rb +83 -34
 - data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
 - data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
 - data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
 - data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
 - data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
 - data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
 - data/lib/rails/generators/active_record.rb +4 -8
 - metadata +163 -121
 - data/CHANGELOG +0 -6023
 - data/examples/associations.png +0 -0
 - data/lib/active_record/association_preload.rb +0 -403
 - data/lib/active_record/associations/association_collection.rb +0 -562
 - data/lib/active_record/associations/association_proxy.rb +0 -295
 - data/lib/active_record/associations/through_association_scope.rb +0 -154
 - data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
 - data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
 - data/lib/active_record/dynamic_finder_match.rb +0 -53
 - data/lib/active_record/dynamic_scope_match.rb +0 -32
 - data/lib/active_record/named_scope.rb +0 -138
 - data/lib/active_record/observer.rb +0 -140
 - data/lib/active_record/session_store.rb +0 -340
 - data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
 - data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
 - data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
 - data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
 - data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
 
| 
         @@ -1,562 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'set'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'active_support/core_ext/array/wrap'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            module ActiveRecord
         
     | 
| 
       5 
     | 
    
         
            -
              module Associations
         
     | 
| 
       6 
     | 
    
         
            -
                # = Active Record Association Collection
         
     | 
| 
       7 
     | 
    
         
            -
                #
         
     | 
| 
       8 
     | 
    
         
            -
                # AssociationCollection is an abstract class that provides common stuff to
         
     | 
| 
       9 
     | 
    
         
            -
                # ease the implementation of association proxies that represent
         
     | 
| 
       10 
     | 
    
         
            -
                # collections. See the class hierarchy in AssociationProxy.
         
     | 
| 
       11 
     | 
    
         
            -
                #
         
     | 
| 
       12 
     | 
    
         
            -
                # You need to be careful with assumptions regarding the target: The proxy
         
     | 
| 
       13 
     | 
    
         
            -
                # does not fetch records from the database until it needs them, but new
         
     | 
| 
       14 
     | 
    
         
            -
                # ones created with +build+ are added to the target. So, the target may be
         
     | 
| 
       15 
     | 
    
         
            -
                # non-empty and still lack children waiting to be read from the database.
         
     | 
| 
       16 
     | 
    
         
            -
                # If you look directly to the database you cannot assume that's the entire
         
     | 
| 
       17 
     | 
    
         
            -
                # collection because new records may have been added to the target, etc.
         
     | 
| 
       18 
     | 
    
         
            -
                #
         
     | 
| 
       19 
     | 
    
         
            -
                # If you need to work on all current children, new and existing records,
         
     | 
| 
       20 
     | 
    
         
            -
                # +load_target+ and the +loaded+ flag are your friends.
         
     | 
| 
       21 
     | 
    
         
            -
                class AssociationCollection < AssociationProxy #:nodoc:
         
     | 
| 
       22 
     | 
    
         
            -
                  def initialize(owner, reflection)
         
     | 
| 
       23 
     | 
    
         
            -
                    super
         
     | 
| 
       24 
     | 
    
         
            -
                    construct_sql
         
     | 
| 
       25 
     | 
    
         
            -
                  end
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
                  delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                  def select(select = nil)
         
     | 
| 
       30 
     | 
    
         
            -
                    if block_given?
         
     | 
| 
       31 
     | 
    
         
            -
                      load_target
         
     | 
| 
       32 
     | 
    
         
            -
                      @target.select.each { |e| yield e }
         
     | 
| 
       33 
     | 
    
         
            -
                    else
         
     | 
| 
       34 
     | 
    
         
            -
                      scoped.select(select)
         
     | 
| 
       35 
     | 
    
         
            -
                    end
         
     | 
| 
       36 
     | 
    
         
            -
                  end
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                  def scoped
         
     | 
| 
       39 
     | 
    
         
            -
                    with_scope(construct_scope) { @reflection.klass.scoped }
         
     | 
| 
       40 
     | 
    
         
            -
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  def find(*args)
         
     | 
| 
       43 
     | 
    
         
            -
                    options = args.extract_options!
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                    # If using a custom finder_sql, scan the entire collection.
         
     | 
| 
       46 
     | 
    
         
            -
                    if @reflection.options[:finder_sql]
         
     | 
| 
       47 
     | 
    
         
            -
                      expects_array = args.first.kind_of?(Array)
         
     | 
| 
       48 
     | 
    
         
            -
                      ids           = args.flatten.compact.uniq.map { |arg| arg.to_i }
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                      if ids.size == 1
         
     | 
| 
       51 
     | 
    
         
            -
                        id = ids.first
         
     | 
| 
       52 
     | 
    
         
            -
                        record = load_target.detect { |r| id == r.id }
         
     | 
| 
       53 
     | 
    
         
            -
                        expects_array ? [ record ] : record
         
     | 
| 
       54 
     | 
    
         
            -
                      else
         
     | 
| 
       55 
     | 
    
         
            -
                        load_target.select { |r| ids.include?(r.id) }
         
     | 
| 
       56 
     | 
    
         
            -
                      end
         
     | 
| 
       57 
     | 
    
         
            -
                    else
         
     | 
| 
       58 
     | 
    
         
            -
                      merge_options_from_reflection!(options)
         
     | 
| 
       59 
     | 
    
         
            -
                      construct_find_options!(options)
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                      find_scope = construct_scope[:find].slice(:conditions, :order)
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
                      with_scope(:find => find_scope) do
         
     | 
| 
       64 
     | 
    
         
            -
                        relation = @reflection.klass.send(:construct_finder_arel, options, @reflection.klass.send(:current_scoped_methods))
         
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
                        case args.first
         
     | 
| 
       67 
     | 
    
         
            -
                        when :first, :last
         
     | 
| 
       68 
     | 
    
         
            -
                          relation.send(args.first)
         
     | 
| 
       69 
     | 
    
         
            -
                        when :all
         
     | 
| 
       70 
     | 
    
         
            -
                          records = relation.all
         
     | 
| 
       71 
     | 
    
         
            -
                          @reflection.options[:uniq] ? uniq(records) : records
         
     | 
| 
       72 
     | 
    
         
            -
                        else
         
     | 
| 
       73 
     | 
    
         
            -
                          relation.find(*args)
         
     | 
| 
       74 
     | 
    
         
            -
                        end
         
     | 
| 
       75 
     | 
    
         
            -
                      end
         
     | 
| 
       76 
     | 
    
         
            -
                    end
         
     | 
| 
       77 
     | 
    
         
            -
                  end
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                  # Fetches the first one using SQL if possible.
         
     | 
| 
       80 
     | 
    
         
            -
                  def first(*args)
         
     | 
| 
       81 
     | 
    
         
            -
                    if fetch_first_or_last_using_find?(args)
         
     | 
| 
       82 
     | 
    
         
            -
                      find(:first, *args)
         
     | 
| 
       83 
     | 
    
         
            -
                    else
         
     | 
| 
       84 
     | 
    
         
            -
                      load_target unless loaded?
         
     | 
| 
       85 
     | 
    
         
            -
                      @target.first(*args)
         
     | 
| 
       86 
     | 
    
         
            -
                    end
         
     | 
| 
       87 
     | 
    
         
            -
                  end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                  # Fetches the last one using SQL if possible.
         
     | 
| 
       90 
     | 
    
         
            -
                  def last(*args)
         
     | 
| 
       91 
     | 
    
         
            -
                    if fetch_first_or_last_using_find?(args)
         
     | 
| 
       92 
     | 
    
         
            -
                      find(:last, *args)
         
     | 
| 
       93 
     | 
    
         
            -
                    else
         
     | 
| 
       94 
     | 
    
         
            -
                      load_target unless loaded?
         
     | 
| 
       95 
     | 
    
         
            -
                      @target.last(*args)
         
     | 
| 
       96 
     | 
    
         
            -
                    end
         
     | 
| 
       97 
     | 
    
         
            -
                  end
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                  def to_ary
         
     | 
| 
       100 
     | 
    
         
            -
                    load_target
         
     | 
| 
       101 
     | 
    
         
            -
                    if @target.is_a?(Array)
         
     | 
| 
       102 
     | 
    
         
            -
                      @target.to_ary
         
     | 
| 
       103 
     | 
    
         
            -
                    else
         
     | 
| 
       104 
     | 
    
         
            -
                      Array.wrap(@target)
         
     | 
| 
       105 
     | 
    
         
            -
                    end
         
     | 
| 
       106 
     | 
    
         
            -
                  end
         
     | 
| 
       107 
     | 
    
         
            -
                  alias_method :to_a, :to_ary
         
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
                  def reset
         
     | 
| 
       110 
     | 
    
         
            -
                    reset_target!
         
     | 
| 
       111 
     | 
    
         
            -
                    reset_named_scopes_cache!
         
     | 
| 
       112 
     | 
    
         
            -
                    @loaded = false
         
     | 
| 
       113 
     | 
    
         
            -
                  end
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                  def build(attributes = {}, &block)
         
     | 
| 
       116 
     | 
    
         
            -
                    if attributes.is_a?(Array)
         
     | 
| 
       117 
     | 
    
         
            -
                      attributes.collect { |attr| build(attr, &block) }
         
     | 
| 
       118 
     | 
    
         
            -
                    else
         
     | 
| 
       119 
     | 
    
         
            -
                      build_record(attributes) do |record|
         
     | 
| 
       120 
     | 
    
         
            -
                        block.call(record) if block_given?
         
     | 
| 
       121 
     | 
    
         
            -
                        set_belongs_to_association_for(record)
         
     | 
| 
       122 
     | 
    
         
            -
                      end
         
     | 
| 
       123 
     | 
    
         
            -
                    end
         
     | 
| 
       124 
     | 
    
         
            -
                  end
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                  # Add +records+ to this association.  Returns +self+ so method calls may be chained.
         
     | 
| 
       127 
     | 
    
         
            -
                  # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
         
     | 
| 
       128 
     | 
    
         
            -
                  def <<(*records)
         
     | 
| 
       129 
     | 
    
         
            -
                    result = true
         
     | 
| 
       130 
     | 
    
         
            -
                    load_target if @owner.new_record?
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                    transaction do
         
     | 
| 
       133 
     | 
    
         
            -
                      flatten_deeper(records).each do |record|
         
     | 
| 
       134 
     | 
    
         
            -
                        raise_on_type_mismatch(record)
         
     | 
| 
       135 
     | 
    
         
            -
                        add_record_to_target_with_callbacks(record) do |r|
         
     | 
| 
       136 
     | 
    
         
            -
                          result &&= insert_record(record) unless @owner.new_record?
         
     | 
| 
       137 
     | 
    
         
            -
                        end
         
     | 
| 
       138 
     | 
    
         
            -
                      end
         
     | 
| 
       139 
     | 
    
         
            -
                    end
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                    result && self
         
     | 
| 
       142 
     | 
    
         
            -
                  end
         
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
                  alias_method :push, :<<
         
     | 
| 
       145 
     | 
    
         
            -
                  alias_method :concat, :<<
         
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                  # Starts a transaction in the association class's database connection.
         
     | 
| 
       148 
     | 
    
         
            -
                  #
         
     | 
| 
       149 
     | 
    
         
            -
                  #   class Author < ActiveRecord::Base
         
     | 
| 
       150 
     | 
    
         
            -
                  #     has_many :books
         
     | 
| 
       151 
     | 
    
         
            -
                  #   end
         
     | 
| 
       152 
     | 
    
         
            -
                  #
         
     | 
| 
       153 
     | 
    
         
            -
                  #   Author.first.books.transaction do
         
     | 
| 
       154 
     | 
    
         
            -
                  #     # same effect as calling Book.transaction
         
     | 
| 
       155 
     | 
    
         
            -
                  #   end
         
     | 
| 
       156 
     | 
    
         
            -
                  def transaction(*args)
         
     | 
| 
       157 
     | 
    
         
            -
                    @reflection.klass.transaction(*args) do
         
     | 
| 
       158 
     | 
    
         
            -
                      yield
         
     | 
| 
       159 
     | 
    
         
            -
                    end
         
     | 
| 
       160 
     | 
    
         
            -
                  end
         
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
                  # Remove all records from this association
         
     | 
| 
       163 
     | 
    
         
            -
                  #
         
     | 
| 
       164 
     | 
    
         
            -
                  # See delete for more info.
         
     | 
| 
       165 
     | 
    
         
            -
                  def delete_all
         
     | 
| 
       166 
     | 
    
         
            -
                    load_target
         
     | 
| 
       167 
     | 
    
         
            -
                    delete(@target)
         
     | 
| 
       168 
     | 
    
         
            -
                    reset_target!
         
     | 
| 
       169 
     | 
    
         
            -
                    reset_named_scopes_cache!
         
     | 
| 
       170 
     | 
    
         
            -
                  end
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
                  # Calculate sum using SQL, not Enumerable
         
     | 
| 
       173 
     | 
    
         
            -
                  def sum(*args)
         
     | 
| 
       174 
     | 
    
         
            -
                    if block_given?
         
     | 
| 
       175 
     | 
    
         
            -
                      calculate(:sum, *args) { |*block_args| yield(*block_args) }
         
     | 
| 
       176 
     | 
    
         
            -
                    else
         
     | 
| 
       177 
     | 
    
         
            -
                      calculate(:sum, *args)
         
     | 
| 
       178 
     | 
    
         
            -
                    end
         
     | 
| 
       179 
     | 
    
         
            -
                  end
         
     | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
       181 
     | 
    
         
            -
                  # Count all records using SQL. If the +:counter_sql+ option is set for the association, it will
         
     | 
| 
       182 
     | 
    
         
            -
                  # be used for the query. If no +:counter_sql+ was supplied, but +:finder_sql+ was set, the
         
     | 
| 
       183 
     | 
    
         
            -
                  # descendant's +construct_sql+ method will have set :counter_sql automatically.
         
     | 
| 
       184 
     | 
    
         
            -
                  # Otherwise, construct options and pass them with scope to the target class's +count+.
         
     | 
| 
       185 
     | 
    
         
            -
                  def count(column_name = nil, options = {})
         
     | 
| 
       186 
     | 
    
         
            -
                    column_name, options = nil, column_name if column_name.is_a?(Hash)
         
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
                    if @reflection.options[:counter_sql] && !options.blank?
         
     | 
| 
       189 
     | 
    
         
            -
                      raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
         
     | 
| 
       190 
     | 
    
         
            -
                    elsif @reflection.options[:counter_sql]
         
     | 
| 
       191 
     | 
    
         
            -
                      @reflection.klass.count_by_sql(@counter_sql)
         
     | 
| 
       192 
     | 
    
         
            -
                    else
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
                      if @reflection.options[:uniq]
         
     | 
| 
       195 
     | 
    
         
            -
                        # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
         
     | 
| 
       196 
     | 
    
         
            -
                        column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" unless column_name
         
     | 
| 
       197 
     | 
    
         
            -
                        options.merge!(:distinct => true)
         
     | 
| 
       198 
     | 
    
         
            -
                      end
         
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
                      value = @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.count(column_name, options) }
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
                      limit  = @reflection.options[:limit]
         
     | 
| 
       203 
     | 
    
         
            -
                      offset = @reflection.options[:offset]
         
     | 
| 
       204 
     | 
    
         
            -
             
     | 
| 
       205 
     | 
    
         
            -
                      if limit || offset
         
     | 
| 
       206 
     | 
    
         
            -
                        [ [value - offset.to_i, 0].max, limit.to_i ].min
         
     | 
| 
       207 
     | 
    
         
            -
                      else
         
     | 
| 
       208 
     | 
    
         
            -
                        value
         
     | 
| 
       209 
     | 
    
         
            -
                      end
         
     | 
| 
       210 
     | 
    
         
            -
                    end
         
     | 
| 
       211 
     | 
    
         
            -
                  end
         
     | 
| 
       212 
     | 
    
         
            -
             
     | 
| 
       213 
     | 
    
         
            -
                  # Removes +records+ from this association calling +before_remove+ and
         
     | 
| 
       214 
     | 
    
         
            -
                  # +after_remove+ callbacks.
         
     | 
| 
       215 
     | 
    
         
            -
                  #
         
     | 
| 
       216 
     | 
    
         
            -
                  # This method is abstract in the sense that +delete_records+ has to be
         
     | 
| 
       217 
     | 
    
         
            -
                  # provided by descendants. Note this method does not imply the records
         
     | 
| 
       218 
     | 
    
         
            -
                  # are actually removed from the database, that depends precisely on
         
     | 
| 
       219 
     | 
    
         
            -
                  # +delete_records+. They are in any case removed from the collection.
         
     | 
| 
       220 
     | 
    
         
            -
                  def delete(*records)
         
     | 
| 
       221 
     | 
    
         
            -
                    remove_records(records) do |_records, old_records|
         
     | 
| 
       222 
     | 
    
         
            -
                      delete_records(old_records) if old_records.any?
         
     | 
| 
       223 
     | 
    
         
            -
                      _records.each { |record| @target.delete(record) }
         
     | 
| 
       224 
     | 
    
         
            -
                    end
         
     | 
| 
       225 
     | 
    
         
            -
                  end
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
                  # Destroy +records+ and remove them from this association calling
         
     | 
| 
       228 
     | 
    
         
            -
                  # +before_remove+ and +after_remove+ callbacks.
         
     | 
| 
       229 
     | 
    
         
            -
                  #
         
     | 
| 
       230 
     | 
    
         
            -
                  # Note that this method will _always_ remove records from the database
         
     | 
| 
       231 
     | 
    
         
            -
                  # ignoring the +:dependent+ option.
         
     | 
| 
       232 
     | 
    
         
            -
                  def destroy(*records)
         
     | 
| 
       233 
     | 
    
         
            -
                    records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)}
         
     | 
| 
       234 
     | 
    
         
            -
                    remove_records(records) do |_records, old_records|
         
     | 
| 
       235 
     | 
    
         
            -
                      old_records.each { |record| record.destroy }
         
     | 
| 
       236 
     | 
    
         
            -
                    end
         
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
                    load_target
         
     | 
| 
       239 
     | 
    
         
            -
                  end
         
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
                  # Removes all records from this association.  Returns +self+ so method calls may be chained.
         
     | 
| 
       242 
     | 
    
         
            -
                  def clear
         
     | 
| 
       243 
     | 
    
         
            -
                    return self if length.zero? # forces load_target if it hasn't happened already
         
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
                    if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
         
     | 
| 
       246 
     | 
    
         
            -
                      destroy_all
         
     | 
| 
       247 
     | 
    
         
            -
                    else
         
     | 
| 
       248 
     | 
    
         
            -
                      delete_all
         
     | 
| 
       249 
     | 
    
         
            -
                    end
         
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
                    self
         
     | 
| 
       252 
     | 
    
         
            -
                  end
         
     | 
| 
       253 
     | 
    
         
            -
             
     | 
| 
       254 
     | 
    
         
            -
                  # Destroy all the records from this association.
         
     | 
| 
       255 
     | 
    
         
            -
                  #
         
     | 
| 
       256 
     | 
    
         
            -
                  # See destroy for more info.
         
     | 
| 
       257 
     | 
    
         
            -
                  def destroy_all
         
     | 
| 
       258 
     | 
    
         
            -
                    load_target
         
     | 
| 
       259 
     | 
    
         
            -
                    destroy(@target).tap do
         
     | 
| 
       260 
     | 
    
         
            -
                      reset_target!
         
     | 
| 
       261 
     | 
    
         
            -
                      reset_named_scopes_cache!
         
     | 
| 
       262 
     | 
    
         
            -
                    end
         
     | 
| 
       263 
     | 
    
         
            -
                  end
         
     | 
| 
       264 
     | 
    
         
            -
             
     | 
| 
       265 
     | 
    
         
            -
                  def create(attrs = {})
         
     | 
| 
       266 
     | 
    
         
            -
                    if attrs.is_a?(Array)
         
     | 
| 
       267 
     | 
    
         
            -
                      attrs.collect { |attr| create(attr) }
         
     | 
| 
       268 
     | 
    
         
            -
                    else
         
     | 
| 
       269 
     | 
    
         
            -
                      create_record(attrs) do |record|
         
     | 
| 
       270 
     | 
    
         
            -
                        yield(record) if block_given?
         
     | 
| 
       271 
     | 
    
         
            -
                        record.save
         
     | 
| 
       272 
     | 
    
         
            -
                      end
         
     | 
| 
       273 
     | 
    
         
            -
                    end
         
     | 
| 
       274 
     | 
    
         
            -
                  end
         
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
       276 
     | 
    
         
            -
                  def create!(attrs = {})
         
     | 
| 
       277 
     | 
    
         
            -
                    create_record(attrs) do |record|
         
     | 
| 
       278 
     | 
    
         
            -
                      yield(record) if block_given?
         
     | 
| 
       279 
     | 
    
         
            -
                      record.save!
         
     | 
| 
       280 
     | 
    
         
            -
                    end
         
     | 
| 
       281 
     | 
    
         
            -
                  end
         
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
                  # Returns the size of the collection by executing a SELECT COUNT(*)
         
     | 
| 
       284 
     | 
    
         
            -
                  # query if the collection hasn't been loaded, and calling
         
     | 
| 
       285 
     | 
    
         
            -
                  # <tt>collection.size</tt> if it has.
         
     | 
| 
       286 
     | 
    
         
            -
                  #
         
     | 
| 
       287 
     | 
    
         
            -
                  # If the collection has been already loaded +size+ and +length+ are
         
     | 
| 
       288 
     | 
    
         
            -
                  # equivalent. If not and you are going to need the records anyway
         
     | 
| 
       289 
     | 
    
         
            -
                  # +length+ will take one less query. Otherwise +size+ is more efficient.
         
     | 
| 
       290 
     | 
    
         
            -
                  #
         
     | 
| 
       291 
     | 
    
         
            -
                  # This method is abstract in the sense that it relies on
         
     | 
| 
       292 
     | 
    
         
            -
                  # +count_records+, which is a method descendants have to provide.
         
     | 
| 
       293 
     | 
    
         
            -
                  def size
         
     | 
| 
       294 
     | 
    
         
            -
                    if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
         
     | 
| 
       295 
     | 
    
         
            -
                      @target.size
         
     | 
| 
       296 
     | 
    
         
            -
                    elsif !loaded? && @reflection.options[:group]
         
     | 
| 
       297 
     | 
    
         
            -
                      load_target.size
         
     | 
| 
       298 
     | 
    
         
            -
                    elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
         
     | 
| 
       299 
     | 
    
         
            -
                      unsaved_records = @target.select { |r| r.new_record? }
         
     | 
| 
       300 
     | 
    
         
            -
                      unsaved_records.size + count_records
         
     | 
| 
       301 
     | 
    
         
            -
                    else
         
     | 
| 
       302 
     | 
    
         
            -
                      count_records
         
     | 
| 
       303 
     | 
    
         
            -
                    end
         
     | 
| 
       304 
     | 
    
         
            -
                  end
         
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
       306 
     | 
    
         
            -
                  # Returns the size of the collection calling +size+ on the target.
         
     | 
| 
       307 
     | 
    
         
            -
                  #
         
     | 
| 
       308 
     | 
    
         
            -
                  # If the collection has been already loaded +length+ and +size+ are
         
     | 
| 
       309 
     | 
    
         
            -
                  # equivalent. If not and you are going to need the records anyway this
         
     | 
| 
       310 
     | 
    
         
            -
                  # method will take one less query. Otherwise +size+ is more efficient.
         
     | 
| 
       311 
     | 
    
         
            -
                  def length
         
     | 
| 
       312 
     | 
    
         
            -
                    load_target.size
         
     | 
| 
       313 
     | 
    
         
            -
                  end
         
     | 
| 
       314 
     | 
    
         
            -
             
     | 
| 
       315 
     | 
    
         
            -
                  # Equivalent to <tt>collection.size.zero?</tt>. If the collection has
         
     | 
| 
       316 
     | 
    
         
            -
                  # not been already loaded and you are going to fetch the records anyway
         
     | 
| 
       317 
     | 
    
         
            -
                  # it is better to check <tt>collection.length.zero?</tt>.
         
     | 
| 
       318 
     | 
    
         
            -
                  def empty?
         
     | 
| 
       319 
     | 
    
         
            -
                    size.zero?
         
     | 
| 
       320 
     | 
    
         
            -
                  end
         
     | 
| 
       321 
     | 
    
         
            -
             
     | 
| 
       322 
     | 
    
         
            -
                  def any?
         
     | 
| 
       323 
     | 
    
         
            -
                    if block_given?
         
     | 
| 
       324 
     | 
    
         
            -
                      method_missing(:any?) { |*block_args| yield(*block_args) }
         
     | 
| 
       325 
     | 
    
         
            -
                    else
         
     | 
| 
       326 
     | 
    
         
            -
                      !empty?
         
     | 
| 
       327 
     | 
    
         
            -
                    end
         
     | 
| 
       328 
     | 
    
         
            -
                  end
         
     | 
| 
       329 
     | 
    
         
            -
             
     | 
| 
       330 
     | 
    
         
            -
                  # Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
         
     | 
| 
       331 
     | 
    
         
            -
                  def many?
         
     | 
| 
       332 
     | 
    
         
            -
                    if block_given?
         
     | 
| 
       333 
     | 
    
         
            -
                      method_missing(:many?) { |*block_args| yield(*block_args) }
         
     | 
| 
       334 
     | 
    
         
            -
                    else
         
     | 
| 
       335 
     | 
    
         
            -
                      size > 1
         
     | 
| 
       336 
     | 
    
         
            -
                    end
         
     | 
| 
       337 
     | 
    
         
            -
                  end
         
     | 
| 
       338 
     | 
    
         
            -
             
     | 
| 
       339 
     | 
    
         
            -
                  def uniq(collection = self)
         
     | 
| 
       340 
     | 
    
         
            -
                    seen = Set.new
         
     | 
| 
       341 
     | 
    
         
            -
                    collection.inject([]) do |kept, record|
         
     | 
| 
       342 
     | 
    
         
            -
                      unless seen.include?(record.id)
         
     | 
| 
       343 
     | 
    
         
            -
                        kept << record
         
     | 
| 
       344 
     | 
    
         
            -
                        seen << record.id
         
     | 
| 
       345 
     | 
    
         
            -
                      end
         
     | 
| 
       346 
     | 
    
         
            -
                      kept
         
     | 
| 
       347 
     | 
    
         
            -
                    end
         
     | 
| 
       348 
     | 
    
         
            -
                  end
         
     | 
| 
       349 
     | 
    
         
            -
             
     | 
| 
       350 
     | 
    
         
            -
                  # Replace this collection with +other_array+
         
     | 
| 
       351 
     | 
    
         
            -
                  # This will perform a diff and delete/add only records that have changed.
         
     | 
| 
       352 
     | 
    
         
            -
                  def replace(other_array)
         
     | 
| 
       353 
     | 
    
         
            -
                    other_array.each { |val| raise_on_type_mismatch(val) }
         
     | 
| 
       354 
     | 
    
         
            -
             
     | 
| 
       355 
     | 
    
         
            -
                    load_target
         
     | 
| 
       356 
     | 
    
         
            -
                    other   = other_array.size < 100 ? other_array : other_array.to_set
         
     | 
| 
       357 
     | 
    
         
            -
                    current = @target.size < 100 ? @target : @target.to_set
         
     | 
| 
       358 
     | 
    
         
            -
             
     | 
| 
       359 
     | 
    
         
            -
                    transaction do
         
     | 
| 
       360 
     | 
    
         
            -
                      delete(@target.select { |v| !other.include?(v) })
         
     | 
| 
       361 
     | 
    
         
            -
                      concat(other_array.select { |v| !current.include?(v) })
         
     | 
| 
       362 
     | 
    
         
            -
                    end
         
     | 
| 
       363 
     | 
    
         
            -
                  end
         
     | 
| 
       364 
     | 
    
         
            -
             
     | 
| 
       365 
     | 
    
         
            -
                  def include?(record)
         
     | 
| 
       366 
     | 
    
         
            -
                    return false unless record.is_a?(@reflection.klass)
         
     | 
| 
       367 
     | 
    
         
            -
                    load_target if @reflection.options[:finder_sql] && !loaded?
         
     | 
| 
       368 
     | 
    
         
            -
                    return @target.include?(record) if loaded?
         
     | 
| 
       369 
     | 
    
         
            -
                    exists?(record)
         
     | 
| 
       370 
     | 
    
         
            -
                  end
         
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
       372 
     | 
    
         
            -
                  def proxy_respond_to?(method, include_private = false)
         
     | 
| 
       373 
     | 
    
         
            -
                    super || @reflection.klass.respond_to?(method, include_private)
         
     | 
| 
       374 
     | 
    
         
            -
                  end
         
     | 
| 
       375 
     | 
    
         
            -
             
     | 
| 
       376 
     | 
    
         
            -
                  protected
         
     | 
| 
       377 
     | 
    
         
            -
                    def construct_find_options!(options)
         
     | 
| 
       378 
     | 
    
         
            -
                    end
         
     | 
| 
       379 
     | 
    
         
            -
             
     | 
| 
       380 
     | 
    
         
            -
                    def construct_counter_sql
         
     | 
| 
       381 
     | 
    
         
            -
                      if @reflection.options[:counter_sql]
         
     | 
| 
       382 
     | 
    
         
            -
                        @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
         
     | 
| 
       383 
     | 
    
         
            -
                      elsif @reflection.options[:finder_sql]
         
     | 
| 
       384 
     | 
    
         
            -
                        # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
         
     | 
| 
       385 
     | 
    
         
            -
                        @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
         
     | 
| 
       386 
     | 
    
         
            -
                        @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
         
     | 
| 
       387 
     | 
    
         
            -
                      else
         
     | 
| 
       388 
     | 
    
         
            -
                        @counter_sql = @finder_sql
         
     | 
| 
       389 
     | 
    
         
            -
                      end
         
     | 
| 
       390 
     | 
    
         
            -
                    end
         
     | 
| 
       391 
     | 
    
         
            -
             
     | 
| 
       392 
     | 
    
         
            -
                    def load_target
         
     | 
| 
       393 
     | 
    
         
            -
                      if !@owner.new_record? || foreign_key_present
         
     | 
| 
       394 
     | 
    
         
            -
                        begin
         
     | 
| 
       395 
     | 
    
         
            -
                          if !loaded?
         
     | 
| 
       396 
     | 
    
         
            -
                            if @target.is_a?(Array) && @target.any?
         
     | 
| 
       397 
     | 
    
         
            -
                              @target = find_target.map do |f|
         
     | 
| 
       398 
     | 
    
         
            -
                                i = @target.index(f)
         
     | 
| 
       399 
     | 
    
         
            -
                                if i
         
     | 
| 
       400 
     | 
    
         
            -
                                  @target.delete_at(i).tap do |t|
         
     | 
| 
       401 
     | 
    
         
            -
                                    keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
         
     | 
| 
       402 
     | 
    
         
            -
                                    t.attributes = f.attributes.except(*keys)
         
     | 
| 
       403 
     | 
    
         
            -
                                  end
         
     | 
| 
       404 
     | 
    
         
            -
                                else
         
     | 
| 
       405 
     | 
    
         
            -
                                  f
         
     | 
| 
       406 
     | 
    
         
            -
                                end
         
     | 
| 
       407 
     | 
    
         
            -
                              end + @target
         
     | 
| 
       408 
     | 
    
         
            -
                            else
         
     | 
| 
       409 
     | 
    
         
            -
                              @target = find_target
         
     | 
| 
       410 
     | 
    
         
            -
                            end
         
     | 
| 
       411 
     | 
    
         
            -
                          end
         
     | 
| 
       412 
     | 
    
         
            -
                        rescue ActiveRecord::RecordNotFound
         
     | 
| 
       413 
     | 
    
         
            -
                          reset
         
     | 
| 
       414 
     | 
    
         
            -
                        end
         
     | 
| 
       415 
     | 
    
         
            -
                      end
         
     | 
| 
       416 
     | 
    
         
            -
             
     | 
| 
       417 
     | 
    
         
            -
                      loaded if target
         
     | 
| 
       418 
     | 
    
         
            -
                      target
         
     | 
| 
       419 
     | 
    
         
            -
                    end
         
     | 
| 
       420 
     | 
    
         
            -
             
     | 
| 
       421 
     | 
    
         
            -
                    def method_missing(method, *args)
         
     | 
| 
       422 
     | 
    
         
            -
                      match = DynamicFinderMatch.match(method)
         
     | 
| 
       423 
     | 
    
         
            -
                      if match && match.creator?
         
     | 
| 
       424 
     | 
    
         
            -
                        attributes = match.attribute_names
         
     | 
| 
       425 
     | 
    
         
            -
                        return send(:"find_by_#{attributes.join('_and_')}", *args) || create(Hash[attributes.zip(args)])
         
     | 
| 
       426 
     | 
    
         
            -
                      end
         
     | 
| 
       427 
     | 
    
         
            -
             
     | 
| 
       428 
     | 
    
         
            -
                      if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
         
     | 
| 
       429 
     | 
    
         
            -
                        if block_given?
         
     | 
| 
       430 
     | 
    
         
            -
                          super { |*block_args| yield(*block_args) }
         
     | 
| 
       431 
     | 
    
         
            -
                        else
         
     | 
| 
       432 
     | 
    
         
            -
                          super
         
     | 
| 
       433 
     | 
    
         
            -
                        end
         
     | 
| 
       434 
     | 
    
         
            -
                      elsif @reflection.klass.scopes[method]
         
     | 
| 
       435 
     | 
    
         
            -
                        @_named_scopes_cache ||= {}
         
     | 
| 
       436 
     | 
    
         
            -
                        @_named_scopes_cache[method] ||= {}
         
     | 
| 
       437 
     | 
    
         
            -
                        @_named_scopes_cache[method][args] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
         
     | 
| 
       438 
     | 
    
         
            -
                      else
         
     | 
| 
       439 
     | 
    
         
            -
                        with_scope(construct_scope) do
         
     | 
| 
       440 
     | 
    
         
            -
                          if block_given?
         
     | 
| 
       441 
     | 
    
         
            -
                            @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) }
         
     | 
| 
       442 
     | 
    
         
            -
                          else
         
     | 
| 
       443 
     | 
    
         
            -
                            @reflection.klass.send(method, *args)
         
     | 
| 
       444 
     | 
    
         
            -
                          end
         
     | 
| 
       445 
     | 
    
         
            -
                        end
         
     | 
| 
       446 
     | 
    
         
            -
                      end
         
     | 
| 
       447 
     | 
    
         
            -
                    end
         
     | 
| 
       448 
     | 
    
         
            -
             
     | 
| 
       449 
     | 
    
         
            -
                    # overloaded in derived Association classes to provide useful scoping depending on association type.
         
     | 
| 
       450 
     | 
    
         
            -
                    def construct_scope
         
     | 
| 
       451 
     | 
    
         
            -
                      {}
         
     | 
| 
       452 
     | 
    
         
            -
                    end
         
     | 
| 
       453 
     | 
    
         
            -
             
     | 
| 
       454 
     | 
    
         
            -
                    def reset_target!
         
     | 
| 
       455 
     | 
    
         
            -
                      @target = Array.new
         
     | 
| 
       456 
     | 
    
         
            -
                    end
         
     | 
| 
       457 
     | 
    
         
            -
             
     | 
| 
       458 
     | 
    
         
            -
                    def reset_named_scopes_cache!
         
     | 
| 
       459 
     | 
    
         
            -
                      @_named_scopes_cache = {}
         
     | 
| 
       460 
     | 
    
         
            -
                    end
         
     | 
| 
       461 
     | 
    
         
            -
             
     | 
| 
       462 
     | 
    
         
            -
                    def find_target
         
     | 
| 
       463 
     | 
    
         
            -
                      records =
         
     | 
| 
       464 
     | 
    
         
            -
                        if @reflection.options[:finder_sql]
         
     | 
| 
       465 
     | 
    
         
            -
                          @reflection.klass.find_by_sql(@finder_sql)
         
     | 
| 
       466 
     | 
    
         
            -
                        else
         
     | 
| 
       467 
     | 
    
         
            -
                          find(:all)
         
     | 
| 
       468 
     | 
    
         
            -
                        end
         
     | 
| 
       469 
     | 
    
         
            -
             
     | 
| 
       470 
     | 
    
         
            -
                      records = @reflection.options[:uniq] ? uniq(records) : records
         
     | 
| 
       471 
     | 
    
         
            -
                      records.each do |record|
         
     | 
| 
       472 
     | 
    
         
            -
                        set_inverse_instance(record, @owner)
         
     | 
| 
       473 
     | 
    
         
            -
                      end
         
     | 
| 
       474 
     | 
    
         
            -
                      records
         
     | 
| 
       475 
     | 
    
         
            -
                    end
         
     | 
| 
       476 
     | 
    
         
            -
             
     | 
| 
       477 
     | 
    
         
            -
                    def add_record_to_target_with_callbacks(record)
         
     | 
| 
       478 
     | 
    
         
            -
                      callback(:before_add, record)
         
     | 
| 
       479 
     | 
    
         
            -
                      yield(record) if block_given?
         
     | 
| 
       480 
     | 
    
         
            -
                      @target ||= [] unless loaded?
         
     | 
| 
       481 
     | 
    
         
            -
                      if index = @target.index(record)
         
     | 
| 
       482 
     | 
    
         
            -
                        @target[index] = record
         
     | 
| 
       483 
     | 
    
         
            -
                      else
         
     | 
| 
       484 
     | 
    
         
            -
                         @target << record
         
     | 
| 
       485 
     | 
    
         
            -
                      end
         
     | 
| 
       486 
     | 
    
         
            -
                      callback(:after_add, record)
         
     | 
| 
       487 
     | 
    
         
            -
                      set_inverse_instance(record, @owner)
         
     | 
| 
       488 
     | 
    
         
            -
                      record
         
     | 
| 
       489 
     | 
    
         
            -
                    end
         
     | 
| 
       490 
     | 
    
         
            -
             
     | 
| 
       491 
     | 
    
         
            -
                  private
         
     | 
| 
       492 
     | 
    
         
            -
                    def create_record(attrs)
         
     | 
| 
       493 
     | 
    
         
            -
                      attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
         
     | 
| 
       494 
     | 
    
         
            -
                      ensure_owner_is_not_new
         
     | 
| 
       495 
     | 
    
         
            -
             
     | 
| 
       496 
     | 
    
         
            -
                      _scope = self.construct_scope[:create]
         
     | 
| 
       497 
     | 
    
         
            -
                      csm = @reflection.klass.send(:current_scoped_methods)
         
     | 
| 
       498 
     | 
    
         
            -
                      options = (csm.blank? || !_scope.is_a?(Hash)) ? _scope : _scope.merge(csm.where_values_hash)
         
     | 
| 
       499 
     | 
    
         
            -
             
     | 
| 
       500 
     | 
    
         
            -
                      record = @reflection.klass.send(:with_scope, :create => options) do
         
     | 
| 
       501 
     | 
    
         
            -
                        @reflection.build_association(attrs)
         
     | 
| 
       502 
     | 
    
         
            -
                      end
         
     | 
| 
       503 
     | 
    
         
            -
                      if block_given?
         
     | 
| 
       504 
     | 
    
         
            -
                        add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
         
     | 
| 
       505 
     | 
    
         
            -
                      else
         
     | 
| 
       506 
     | 
    
         
            -
                        add_record_to_target_with_callbacks(record)
         
     | 
| 
       507 
     | 
    
         
            -
                      end
         
     | 
| 
       508 
     | 
    
         
            -
                    end
         
     | 
| 
       509 
     | 
    
         
            -
             
     | 
| 
       510 
     | 
    
         
            -
                    def build_record(attrs)
         
     | 
| 
       511 
     | 
    
         
            -
                      attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
         
     | 
| 
       512 
     | 
    
         
            -
                      record = @reflection.build_association(attrs)
         
     | 
| 
       513 
     | 
    
         
            -
                      if block_given?
         
     | 
| 
       514 
     | 
    
         
            -
                        add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
         
     | 
| 
       515 
     | 
    
         
            -
                      else
         
     | 
| 
       516 
     | 
    
         
            -
                        add_record_to_target_with_callbacks(record)
         
     | 
| 
       517 
     | 
    
         
            -
                      end
         
     | 
| 
       518 
     | 
    
         
            -
                    end
         
     | 
| 
       519 
     | 
    
         
            -
             
     | 
| 
       520 
     | 
    
         
            -
                    def remove_records(*records)
         
     | 
| 
       521 
     | 
    
         
            -
                      records = flatten_deeper(records)
         
     | 
| 
       522 
     | 
    
         
            -
                      records.each { |record| raise_on_type_mismatch(record) }
         
     | 
| 
       523 
     | 
    
         
            -
             
     | 
| 
       524 
     | 
    
         
            -
                      transaction do
         
     | 
| 
       525 
     | 
    
         
            -
                        records.each { |record| callback(:before_remove, record) }
         
     | 
| 
       526 
     | 
    
         
            -
                        old_records = records.reject { |r| r.new_record? }
         
     | 
| 
       527 
     | 
    
         
            -
                        yield(records, old_records)
         
     | 
| 
       528 
     | 
    
         
            -
                        records.each { |record| callback(:after_remove, record) }
         
     | 
| 
       529 
     | 
    
         
            -
                      end
         
     | 
| 
       530 
     | 
    
         
            -
                    end
         
     | 
| 
       531 
     | 
    
         
            -
             
     | 
| 
       532 
     | 
    
         
            -
                    def callback(method, record)
         
     | 
| 
       533 
     | 
    
         
            -
                      callbacks_for(method).each do |callback|
         
     | 
| 
       534 
     | 
    
         
            -
                        case callback
         
     | 
| 
       535 
     | 
    
         
            -
                        when Symbol
         
     | 
| 
       536 
     | 
    
         
            -
                          @owner.send(callback, record)
         
     | 
| 
       537 
     | 
    
         
            -
                        when Proc
         
     | 
| 
       538 
     | 
    
         
            -
                          callback.call(@owner, record)
         
     | 
| 
       539 
     | 
    
         
            -
                        else
         
     | 
| 
       540 
     | 
    
         
            -
                          callback.send(method, @owner, record)
         
     | 
| 
       541 
     | 
    
         
            -
                        end
         
     | 
| 
       542 
     | 
    
         
            -
                      end
         
     | 
| 
       543 
     | 
    
         
            -
                    end
         
     | 
| 
       544 
     | 
    
         
            -
             
     | 
| 
       545 
     | 
    
         
            -
                    def callbacks_for(callback_name)
         
     | 
| 
       546 
     | 
    
         
            -
                      full_callback_name = "#{callback_name}_for_#{@reflection.name}"
         
     | 
| 
       547 
     | 
    
         
            -
                      @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
         
     | 
| 
       548 
     | 
    
         
            -
                    end
         
     | 
| 
       549 
     | 
    
         
            -
             
     | 
| 
       550 
     | 
    
         
            -
                    def ensure_owner_is_not_new
         
     | 
| 
       551 
     | 
    
         
            -
                      if @owner.new_record?
         
     | 
| 
       552 
     | 
    
         
            -
                        raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
         
     | 
| 
       553 
     | 
    
         
            -
                      end
         
     | 
| 
       554 
     | 
    
         
            -
                    end
         
     | 
| 
       555 
     | 
    
         
            -
             
     | 
| 
       556 
     | 
    
         
            -
                    def fetch_first_or_last_using_find?(args)
         
     | 
| 
       557 
     | 
    
         
            -
                      args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
         
     | 
| 
       558 
     | 
    
         
            -
                                                     @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
         
     | 
| 
       559 
     | 
    
         
            -
                    end
         
     | 
| 
       560 
     | 
    
         
            -
                end
         
     | 
| 
       561 
     | 
    
         
            -
              end
         
     | 
| 
       562 
     | 
    
         
            -
            end
         
     |