activerecord 2.2.3 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (120) hide show
  1. data/CHANGELOG +438 -396
  2. data/Rakefile +4 -2
  3. data/lib/active_record.rb +46 -43
  4. data/lib/active_record/association_preload.rb +34 -19
  5. data/lib/active_record/associations.rb +193 -251
  6. data/lib/active_record/associations/association_collection.rb +38 -21
  7. data/lib/active_record/associations/association_proxy.rb +11 -4
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
  9. data/lib/active_record/associations/has_many_association.rb +2 -2
  10. data/lib/active_record/associations/has_many_through_association.rb +8 -8
  11. data/lib/active_record/associations/has_one_association.rb +11 -2
  12. data/lib/active_record/attribute_methods.rb +1 -0
  13. data/lib/active_record/autosave_association.rb +349 -0
  14. data/lib/active_record/base.rb +292 -106
  15. data/lib/active_record/batches.rb +73 -0
  16. data/lib/active_record/calculations.rb +34 -16
  17. data/lib/active_record/callbacks.rb +37 -8
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +16 -0
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -0
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +103 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -6
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +28 -25
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -5
  24. data/lib/active_record/connection_adapters/mysql_adapter.rb +50 -21
  25. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -41
  26. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  27. data/lib/active_record/connection_adapters/sqlite_adapter.rb +41 -21
  28. data/lib/active_record/dirty.rb +1 -1
  29. data/lib/active_record/dynamic_scope_match.rb +25 -0
  30. data/lib/active_record/fixtures.rb +193 -198
  31. data/lib/active_record/locale/en.yml +1 -1
  32. data/lib/active_record/locking/optimistic.rb +33 -0
  33. data/lib/active_record/migration.rb +8 -2
  34. data/lib/active_record/named_scope.rb +13 -6
  35. data/lib/active_record/nested_attributes.rb +329 -0
  36. data/lib/active_record/query_cache.rb +25 -13
  37. data/lib/active_record/reflection.rb +6 -1
  38. data/lib/active_record/schema_dumper.rb +2 -0
  39. data/lib/active_record/serialization.rb +3 -1
  40. data/lib/active_record/serializers/json_serializer.rb +19 -0
  41. data/lib/active_record/serializers/xml_serializer.rb +28 -13
  42. data/lib/active_record/session_store.rb +318 -0
  43. data/lib/active_record/test_case.rb +15 -9
  44. data/lib/active_record/timestamp.rb +2 -2
  45. data/lib/active_record/transactions.rb +58 -8
  46. data/lib/active_record/validations.rb +29 -24
  47. data/lib/active_record/version.rb +2 -2
  48. data/test/cases/ar_schema_test.rb +0 -1
  49. data/test/cases/associations/belongs_to_associations_test.rb +35 -131
  50. data/test/cases/associations/cascaded_eager_loading_test.rb +8 -0
  51. data/test/cases/associations/eager_load_nested_include_test.rb +29 -0
  52. data/test/cases/associations/eager_test.rb +137 -7
  53. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +45 -7
  54. data/test/cases/associations/has_many_associations_test.rb +110 -149
  55. data/test/cases/associations/has_many_through_associations_test.rb +39 -7
  56. data/test/cases/associations/has_one_associations_test.rb +39 -92
  57. data/test/cases/associations/has_one_through_associations_test.rb +34 -3
  58. data/test/cases/associations/inner_join_association_test.rb +0 -5
  59. data/test/cases/associations/join_model_test.rb +5 -7
  60. data/test/cases/attribute_methods_test.rb +13 -1
  61. data/test/cases/autosave_association_test.rb +901 -0
  62. data/test/cases/base_test.rb +41 -21
  63. data/test/cases/batches_test.rb +61 -0
  64. data/test/cases/calculations_test.rb +37 -17
  65. data/test/cases/callbacks_test.rb +43 -5
  66. data/test/cases/connection_pool_test.rb +25 -0
  67. data/test/cases/copy_table_test_sqlite.rb +11 -0
  68. data/test/cases/datatype_test_postgresql.rb +1 -0
  69. data/test/cases/defaults_test.rb +37 -26
  70. data/test/cases/dirty_test.rb +26 -2
  71. data/test/cases/finder_test.rb +79 -44
  72. data/test/cases/fixtures_test.rb +15 -19
  73. data/test/cases/helper.rb +26 -19
  74. data/test/cases/inheritance_test.rb +2 -2
  75. data/test/cases/json_serialization_test.rb +1 -1
  76. data/test/cases/locking_test.rb +23 -5
  77. data/test/cases/method_scoping_test.rb +126 -3
  78. data/test/cases/migration_test.rb +253 -237
  79. data/test/cases/named_scope_test.rb +73 -3
  80. data/test/cases/nested_attributes_test.rb +509 -0
  81. data/test/cases/query_cache_test.rb +0 -4
  82. data/test/cases/reflection_test.rb +13 -3
  83. data/test/cases/reload_models_test.rb +3 -1
  84. data/test/cases/repair_helper.rb +50 -0
  85. data/test/cases/schema_dumper_test.rb +0 -1
  86. data/test/cases/transactions_test.rb +177 -12
  87. data/test/cases/validations_i18n_test.rb +288 -294
  88. data/test/cases/validations_test.rb +230 -180
  89. data/test/cases/xml_serialization_test.rb +19 -1
  90. data/test/fixtures/fixture_database.sqlite3 +0 -0
  91. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  92. data/test/fixtures/member_types.yml +6 -0
  93. data/test/fixtures/members.yml +3 -1
  94. data/test/fixtures/people.yml +10 -1
  95. data/test/fixtures/toys.yml +4 -0
  96. data/test/models/author.rb +1 -2
  97. data/test/models/bird.rb +3 -0
  98. data/test/models/category.rb +1 -0
  99. data/test/models/company.rb +3 -0
  100. data/test/models/developer.rb +12 -0
  101. data/test/models/event.rb +3 -0
  102. data/test/models/member.rb +1 -0
  103. data/test/models/member_detail.rb +1 -0
  104. data/test/models/member_type.rb +3 -0
  105. data/test/models/owner.rb +2 -1
  106. data/test/models/parrot.rb +2 -0
  107. data/test/models/person.rb +6 -0
  108. data/test/models/pet.rb +2 -1
  109. data/test/models/pirate.rb +55 -1
  110. data/test/models/post.rb +6 -0
  111. data/test/models/project.rb +1 -0
  112. data/test/models/reply.rb +6 -0
  113. data/test/models/ship.rb +8 -1
  114. data/test/models/ship_part.rb +5 -0
  115. data/test/models/topic.rb +13 -1
  116. data/test/models/toy.rb +4 -0
  117. data/test/schema/schema.rb +35 -2
  118. metadata +70 -9
  119. data/test/fixtures/fixture_database.sqlite +0 -0
  120. data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -327,7 +327,7 @@ module ActiveRecord #:nodoc:
327
327
  # User.find(user.id).preferences # => { "background" => "black", "display" => large }
328
328
  #
329
329
  # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a
330
- # descendent of a class not in the hierarchy. Example:
330
+ # descendant of a class not in the hierarchy. Example:
331
331
  #
332
332
  # class User < ActiveRecord::Base
333
333
  # serialize :preferences, Hash
@@ -389,6 +389,8 @@ module ActiveRecord #:nodoc:
389
389
  # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
390
390
  # instances in the current object space.
391
391
  class Base
392
+ ##
393
+ # :singleton-method:
392
394
  # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
393
395
  # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
394
396
  cattr_accessor :logger, :instance_writer => false
@@ -415,6 +417,8 @@ module ActiveRecord #:nodoc:
415
417
 
416
418
  @@subclasses = {}
417
419
 
420
+ ##
421
+ # :singleton-method:
418
422
  # Contains the database configuration - as is typically stored in config/database.yml -
419
423
  # as a Hash.
420
424
  #
@@ -443,6 +447,8 @@ module ActiveRecord #:nodoc:
443
447
  cattr_accessor :configurations, :instance_writer => false
444
448
  @@configurations = {}
445
449
 
450
+ ##
451
+ # :singleton-method:
446
452
  # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and
447
453
  # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
448
454
  # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
@@ -450,34 +456,46 @@ module ActiveRecord #:nodoc:
450
456
  cattr_accessor :primary_key_prefix_type, :instance_writer => false
451
457
  @@primary_key_prefix_type = nil
452
458
 
459
+ ##
460
+ # :singleton-method:
453
461
  # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
454
462
  # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
455
463
  # for tables in a shared database. By default, the prefix is the empty string.
456
464
  cattr_accessor :table_name_prefix, :instance_writer => false
457
465
  @@table_name_prefix = ""
458
466
 
467
+ ##
468
+ # :singleton-method:
459
469
  # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
460
470
  # "people_basecamp"). By default, the suffix is the empty string.
461
471
  cattr_accessor :table_name_suffix, :instance_writer => false
462
472
  @@table_name_suffix = ""
463
473
 
474
+ ##
475
+ # :singleton-method:
464
476
  # Indicates whether table names should be the pluralized versions of the corresponding class names.
465
477
  # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
466
478
  # See table_name for the full rules on table/class naming. This is true, by default.
467
479
  cattr_accessor :pluralize_table_names, :instance_writer => false
468
480
  @@pluralize_table_names = true
469
481
 
482
+ ##
483
+ # :singleton-method:
470
484
  # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
471
485
  # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
472
486
  # may complicate matters if you use software like syslog. This is true, by default.
473
487
  cattr_accessor :colorize_logging, :instance_writer => false
474
488
  @@colorize_logging = true
475
489
 
490
+ ##
491
+ # :singleton-method:
476
492
  # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
477
493
  # This is set to :local by default.
478
494
  cattr_accessor :default_timezone, :instance_writer => false
479
495
  @@default_timezone = :local
480
496
 
497
+ ##
498
+ # :singleton-method:
481
499
  # Specifies the format to use when dumping the database schema with Rails'
482
500
  # Rakefile. If :sql, the schema is dumped as (potentially database-
483
501
  # specific) SQL statements. If :ruby, the schema is dumped as an
@@ -487,6 +505,8 @@ module ActiveRecord #:nodoc:
487
505
  cattr_accessor :schema_format , :instance_writer => false
488
506
  @@schema_format = :ruby
489
507
 
508
+ ##
509
+ # :singleton-method:
490
510
  # Specify whether or not to use timestamps for migration numbers
491
511
  cattr_accessor :timestamped_migrations , :instance_writer => false
492
512
  @@timestamped_migrations = true
@@ -495,6 +515,10 @@ module ActiveRecord #:nodoc:
495
515
  superclass_delegating_accessor :store_full_sti_class
496
516
  self.store_full_sti_class = false
497
517
 
518
+ # Stores the default scope for the class
519
+ class_inheritable_accessor :default_scoping, :instance_writer => false
520
+ self.default_scoping = []
521
+
498
522
  class << self # Class methods
499
523
  # Find operates with four different retrieval approaches:
500
524
  #
@@ -517,10 +541,12 @@ module ActiveRecord #:nodoc:
517
541
  # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
518
542
  # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
519
543
  # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
544
+ # * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
520
545
  # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
521
546
  # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
522
- # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
523
- # or named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s).
547
+ # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
548
+ # named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s),
549
+ # or an array containing a mixture of both strings and named associations.
524
550
  # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
525
551
  # Pass <tt>:readonly => false</tt> to override.
526
552
  # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
@@ -635,23 +661,32 @@ module ActiveRecord #:nodoc:
635
661
  connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
636
662
  end
637
663
 
638
- # Checks whether a record exists in the database that matches conditions given. These conditions
639
- # can either be a single integer representing a primary key id to be found, or a condition to be
640
- # matched like using ActiveRecord#find.
664
+ # Returns true if a record exists in the table that matches the +id+ or
665
+ # conditions given, or false otherwise. The argument can take five forms:
666
+ #
667
+ # * Integer - Finds the record with this primary key.
668
+ # * String - Finds the record with a primary key corresponding to this
669
+ # string (such as <tt>'5'</tt>).
670
+ # * Array - Finds the record that matches these +find+-style conditions
671
+ # (such as <tt>['color = ?', 'red']</tt>).
672
+ # * Hash - Finds the record that matches these +find+-style conditions
673
+ # (such as <tt>{:color => 'red'}</tt>).
674
+ # * No args - Returns false if the table is empty, true otherwise.
641
675
  #
642
- # The +id_or_conditions+ parameter can be an Integer or a String if you want to search the primary key
643
- # column of the table for a matching id, or if you're looking to match against a condition you can use
644
- # an Array or a Hash.
676
+ # For more information about specifying conditions as a Hash or Array,
677
+ # see the Conditions section in the introduction to ActiveRecord::Base.
645
678
  #
646
- # Possible gotcha: You can't pass in a condition as a string e.g. "name = 'Jamie'", this would be
647
- # sanitized and then queried against the primary key column as "id = 'name = \'Jamie"
679
+ # Note: You can't pass in a condition as a string (like <tt>name =
680
+ # 'Jamie'</tt>), since it would be sanitized and then queried against
681
+ # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
648
682
  #
649
683
  # ==== Examples
650
684
  # Person.exists?(5)
651
685
  # Person.exists?('5')
652
686
  # Person.exists?(:name => "David")
653
687
  # Person.exists?(['name LIKE ?', "%#{query}%"])
654
- def exists?(id_or_conditions)
688
+ # Person.exists?
689
+ def exists?(id_or_conditions = {})
655
690
  connection.select_all(
656
691
  construct_finder_sql(
657
692
  :select => "#{quoted_table_name}.#{primary_key}",
@@ -722,25 +757,26 @@ module ActiveRecord #:nodoc:
722
757
  end
723
758
  end
724
759
 
725
- # Delete an object (or multiple objects) where the +id+ given matches the primary_key. A SQL +DELETE+ command
726
- # is executed on the database which means that no callbacks are fired off running this. This is an efficient method
727
- # of deleting records that don't need cleaning up after or other actions to be taken.
760
+ # Deletes the row with a primary key matching the +id+ argument, using a
761
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
762
+ # Record objects are not instantiated, so the object's callbacks are not
763
+ # executed, including any <tt>:dependent</tt> association options or
764
+ # Observer methods.
728
765
  #
729
- # Objects are _not_ instantiated with this method, and so +:dependent+ rules
730
- # defined on associations are not honered.
766
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
731
767
  #
732
- # ==== Parameters
733
- #
734
- # * +id+ - Can be either an Integer or an Array of Integers.
768
+ # Note: Although it is often much faster than the alternative,
769
+ # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
770
+ # your application that ensures referential integrity or performs other
771
+ # essential jobs.
735
772
  #
736
773
  # ==== Examples
737
774
  #
738
- # # Delete a single object
775
+ # # Delete a single row
739
776
  # Todo.delete(1)
740
777
  #
741
- # # Delete multiple objects
742
- # todos = [1,2,3]
743
- # Todo.delete(todos)
778
+ # # Delete multiple rows
779
+ # Todo.delete([2,3,4])
744
780
  def delete(id)
745
781
  delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])
746
782
  end
@@ -778,8 +814,7 @@ module ActiveRecord #:nodoc:
778
814
  #
779
815
  # ==== Parameters
780
816
  #
781
- # * +updates+ - A string of column and value pairs that will be set on any records that match conditions.
782
- # What goes into the SET clause.
817
+ # * +updates+ - A string of column and value pairs that will be set on any records that match conditions. This creates the SET clause of the generated SQL.
783
818
  # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info.
784
819
  # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
785
820
  #
@@ -817,25 +852,32 @@ module ActiveRecord #:nodoc:
817
852
  connection.update(sql, "#{name} Update")
818
853
  end
819
854
 
820
- # Destroys the records matching +conditions+ by instantiating each record and calling their +destroy+ method.
821
- # This means at least 2*N database queries to destroy N records, so avoid +destroy_all+ if you are deleting
822
- # many records. If you want to simply delete records without worrying about dependent associations or
823
- # callbacks, use the much faster +delete_all+ method instead.
855
+ # Destroys the records matching +conditions+ by instantiating each
856
+ # record and calling its +destroy+ method. Each object's callbacks are
857
+ # executed (including <tt>:dependent</tt> association options and
858
+ # +before_destroy+/+after_destroy+ Observer methods). Returns the
859
+ # collection of objects that were destroyed; each will be frozen, to
860
+ # reflect that no changes should be made (since they can't be
861
+ # persisted).
862
+ #
863
+ # Note: Instantiation, callback execution, and deletion of each
864
+ # record can be time consuming when you're removing many records at
865
+ # once. It generates at least one SQL +DELETE+ query per record (or
866
+ # possibly more, to enforce your callbacks). If you want to delete many
867
+ # rows quickly, without concern for their associations or callbacks, use
868
+ # +delete_all+ instead.
824
869
  #
825
870
  # ==== Parameters
826
871
  #
827
- # * +conditions+ - Conditions are specified the same way as with +find+ method.
872
+ # * +conditions+ - A string, array, or hash that specifies which records
873
+ # to destroy. If omitted, all records are destroyed. See the
874
+ # Conditions section in the introduction to ActiveRecord::Base for
875
+ # more information.
828
876
  #
829
- # ==== Example
877
+ # ==== Examples
830
878
  #
831
879
  # Person.destroy_all("last_login < '2004-04-04'")
832
- #
833
- # This loads and destroys each person one by one, including its dependent associations and before_ and
834
- # after_destroy callbacks.
835
- #
836
- # +conditions+ can be anything that +find+ also accepts:
837
- #
838
- # Person.destroy_all(:last_login => 6.hours.ago)
880
+ # Person.destroy_all(:status => "inactive")
839
881
  def destroy_all(conditions = nil)
840
882
  find(:all, :conditions => conditions).each { |object| object.destroy }
841
883
  end
@@ -843,7 +885,8 @@ module ActiveRecord #:nodoc:
843
885
  # Deletes the records matching +conditions+ without instantiating the records first, and hence not
844
886
  # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
845
887
  # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
846
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored.
888
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
889
+ # the number of rows affected.
847
890
  #
848
891
  # ==== Parameters
849
892
  #
@@ -886,7 +929,7 @@ module ActiveRecord #:nodoc:
886
929
  #
887
930
  # ==== Parameters
888
931
  #
889
- # * +id+ - The id of the object you wish to update a counter on.
932
+ # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
890
933
  # * +counters+ - An Array of Hashes containing the names of the fields
891
934
  # to update as keys and the amount to update the field by as values.
892
935
  #
@@ -900,12 +943,27 @@ module ActiveRecord #:nodoc:
900
943
  # # SET comment_count = comment_count - 1,
901
944
  # # action_count = action_count + 1
902
945
  # # WHERE id = 5
946
+ #
947
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
948
+ # Post.update_counters [10, 15], :comment_count => 1
949
+ # # Executes the following SQL:
950
+ # # UPDATE posts
951
+ # # SET comment_count = comment_count + 1,
952
+ # # WHERE id IN (10, 15)
903
953
  def update_counters(id, counters)
904
954
  updates = counters.inject([]) { |list, (counter_name, increment)|
905
955
  sign = increment < 0 ? "-" : "+"
906
956
  list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
907
957
  }.join(", ")
908
- update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
958
+
959
+ if id.is_a?(Array)
960
+ ids_list = id.map {|i| quote_value(i)}.join(', ')
961
+ condition = "IN (#{ids_list})"
962
+ else
963
+ condition = "= #{quote_value(id)}"
964
+ end
965
+
966
+ update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}")
909
967
  end
910
968
 
911
969
  # Increment a number field by one, usually representing a count.
@@ -944,7 +1002,6 @@ module ActiveRecord #:nodoc:
944
1002
  update_counters(id, counter_name => -1)
945
1003
  end
946
1004
 
947
-
948
1005
  # Attributes named in this macro are protected from mass-assignment,
949
1006
  # such as <tt>new(attributes)</tt>,
950
1007
  # <tt>update_attributes(attributes)</tt>, or
@@ -1045,7 +1102,6 @@ module ActiveRecord #:nodoc:
1045
1102
  read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
1046
1103
  end
1047
1104
 
1048
-
1049
1105
  # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
1050
1106
  # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used
1051
1107
  # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
@@ -1288,7 +1344,7 @@ module ActiveRecord #:nodoc:
1288
1344
  subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
1289
1345
  end
1290
1346
 
1291
- def self_and_descendents_from_active_record#nodoc:
1347
+ def self_and_descendants_from_active_record#nodoc:
1292
1348
  klass = self
1293
1349
  classes = [klass]
1294
1350
  while klass != klass.base_class
@@ -1308,7 +1364,7 @@ module ActiveRecord #:nodoc:
1308
1364
  # module now.
1309
1365
  # Specify +options+ with additional translating options.
1310
1366
  def human_attribute_name(attribute_key_name, options = {})
1311
- defaults = self_and_descendents_from_active_record.map do |klass|
1367
+ defaults = self_and_descendants_from_active_record.map do |klass|
1312
1368
  :"#{klass.name.underscore}.#{attribute_key_name}"
1313
1369
  end
1314
1370
  defaults << options[:default] if options[:default]
@@ -1323,7 +1379,7 @@ module ActiveRecord #:nodoc:
1323
1379
  # Default scope of the translation is activerecord.models
1324
1380
  # Specify +options+ with additional translating options.
1325
1381
  def human_name(options = {})
1326
- defaults = self_and_descendents_from_active_record.map do |klass|
1382
+ defaults = self_and_descendants_from_active_record.map do |klass|
1327
1383
  :"#{klass.name.underscore}"
1328
1384
  end
1329
1385
  defaults << self.name.humanize
@@ -1358,7 +1414,6 @@ module ActiveRecord #:nodoc:
1358
1414
  end
1359
1415
  end
1360
1416
 
1361
-
1362
1417
  def quote_value(value, column = nil) #:nodoc:
1363
1418
  connection.quote(value,column)
1364
1419
  end
@@ -1384,8 +1439,8 @@ module ActiveRecord #:nodoc:
1384
1439
  def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
1385
1440
  if logger && logger.level <= log_level
1386
1441
  result = nil
1387
- seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
1388
- logger.add(log_level, "#{title} (#{'%.1f' % (seconds * 1000)}ms)")
1442
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
1443
+ logger.add(log_level, '%s (%.1fms)' % [title, ms])
1389
1444
  result
1390
1445
  else
1391
1446
  yield
@@ -1424,7 +1479,10 @@ module ActiveRecord #:nodoc:
1424
1479
  def respond_to?(method_id, include_private = false)
1425
1480
  if match = DynamicFinderMatch.match(method_id)
1426
1481
  return true if all_attributes_exists?(match.attribute_names)
1482
+ elsif match = DynamicScopeMatch.match(method_id)
1483
+ return true if all_attributes_exists?(match.attribute_names)
1427
1484
  end
1485
+
1428
1486
  super
1429
1487
  end
1430
1488
 
@@ -1462,15 +1520,20 @@ module ActiveRecord #:nodoc:
1462
1520
  end
1463
1521
 
1464
1522
  if scoped?(:find, :order)
1465
- scoped_order = reverse_sql_order(scope(:find, :order))
1466
- scoped_methods.select { |s| s[:find].update(:order => scoped_order) }
1523
+ scope = scope(:find)
1524
+ original_scoped_order = scope[:order]
1525
+ scope[:order] = reverse_sql_order(original_scoped_order)
1467
1526
  end
1468
1527
 
1469
- find_initial(options.merge({ :order => order }))
1528
+ begin
1529
+ find_initial(options.merge({ :order => order }))
1530
+ ensure
1531
+ scope[:order] = original_scoped_order if original_scoped_order
1532
+ end
1470
1533
  end
1471
1534
 
1472
1535
  def reverse_sql_order(order_query)
1473
- reversed_query = order_query.split(/,/).each { |s|
1536
+ reversed_query = order_query.to_s.split(/,/).each { |s|
1474
1537
  if s.match(/\s(asc|ASC)$/)
1475
1538
  s.gsub!(/\s(asc|ASC)$/, ' DESC')
1476
1539
  elsif s.match(/\s(desc|DESC)$/)
@@ -1623,12 +1686,12 @@ module ActiveRecord #:nodoc:
1623
1686
  def construct_finder_sql(options)
1624
1687
  scope = scope(:find)
1625
1688
  sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
1626
- sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
1689
+ sql << "FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} "
1627
1690
 
1628
1691
  add_joins!(sql, options[:joins], scope)
1629
1692
  add_conditions!(sql, options[:conditions], scope)
1630
1693
 
1631
- add_group!(sql, options[:group], scope)
1694
+ add_group!(sql, options[:group], options[:having], scope)
1632
1695
  add_order!(sql, options[:order], scope)
1633
1696
  add_limit!(sql, options, scope)
1634
1697
  add_lock!(sql, options, scope)
@@ -1651,7 +1714,7 @@ module ActiveRecord #:nodoc:
1651
1714
  end
1652
1715
  join
1653
1716
  end
1654
- joins.flatten.uniq
1717
+ joins.flatten.map{|j| j.strip}.uniq
1655
1718
  else
1656
1719
  joins.collect{|j| safe_to_array(j)}.flatten.uniq
1657
1720
  end
@@ -1686,13 +1749,15 @@ module ActiveRecord #:nodoc:
1686
1749
  end
1687
1750
  end
1688
1751
 
1689
- def add_group!(sql, group, scope = :auto)
1752
+ def add_group!(sql, group, having, scope = :auto)
1690
1753
  if group
1691
1754
  sql << " GROUP BY #{group}"
1755
+ sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having
1692
1756
  else
1693
1757
  scope = scope(:find) if :auto == scope
1694
1758
  if scope && (scoped_group = scope[:group])
1695
1759
  sql << " GROUP BY #{scoped_group}"
1760
+ sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having]
1696
1761
  end
1697
1762
  end
1698
1763
  end
@@ -1762,17 +1827,19 @@ module ActiveRecord #:nodoc:
1762
1827
  table_name
1763
1828
  end
1764
1829
 
1765
- # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
1766
- # find(:first, :conditions => ["user_name = ?", user_name]) and find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
1767
- # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]).
1830
+ # Enables dynamic finders like <tt>find_by_user_name(user_name)</tt> and <tt>find_by_user_name_and_password(user_name, password)</tt>
1831
+ # that are turned into <tt>find(:first, :conditions => ["user_name = ?", user_name])</tt> and
1832
+ # <tt>find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt> respectively. Also works for
1833
+ # <tt>find(:all)</tt> by using <tt>find_all_by_amount(50)</tt> that is turned into <tt>find(:all, :conditions => ["amount = ?", 50])</tt>.
1768
1834
  #
1769
- # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
1770
- # is actually find_all_by_amount(amount, options).
1835
+ # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
1836
+ # is actually <tt>find_all_by_amount(amount, options)</tt>.
1771
1837
  #
1772
- # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
1773
- # or find_or_create_by_user_and_password(user, password).
1838
+ # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
1839
+ # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
1840
+ # respectively.
1774
1841
  #
1775
- # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
1842
+ # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
1776
1843
  # attempts to use it do not run through method_missing.
1777
1844
  def method_missing(method_id, *arguments, &block)
1778
1845
  if match = DynamicFinderMatch.match(method_id)
@@ -1781,10 +1848,31 @@ module ActiveRecord #:nodoc:
1781
1848
  if match.finder?
1782
1849
  finder = match.finder
1783
1850
  bang = match.bang?
1851
+ # def self.find_by_login_and_activated(*args)
1852
+ # options = args.extract_options!
1853
+ # attributes = construct_attributes_from_arguments(
1854
+ # [:login,:activated],
1855
+ # args
1856
+ # )
1857
+ # finder_options = { :conditions => attributes }
1858
+ # validate_find_options(options)
1859
+ # set_readonly_option!(options)
1860
+ #
1861
+ # if options[:conditions]
1862
+ # with_scope(:find => finder_options) do
1863
+ # find(:first, options)
1864
+ # end
1865
+ # else
1866
+ # find(:first, options.merge(finder_options))
1867
+ # end
1868
+ # end
1784
1869
  self.class_eval %{
1785
1870
  def self.#{method_id}(*args)
1786
1871
  options = args.extract_options!
1787
- attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
1872
+ attributes = construct_attributes_from_arguments(
1873
+ [:#{attribute_names.join(',:')}],
1874
+ args
1875
+ )
1788
1876
  finder_options = { :conditions => attributes }
1789
1877
  validate_find_options(options)
1790
1878
  set_readonly_option!(options)
@@ -1796,12 +1884,37 @@ module ActiveRecord #:nodoc:
1796
1884
  else
1797
1885
  find(:#{finder}, options.merge(finder_options))
1798
1886
  end
1799
- #{'result || raise(RecordNotFound)' if bang}
1887
+ #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
1800
1888
  end
1801
1889
  }, __FILE__, __LINE__
1802
1890
  send(method_id, *arguments)
1803
1891
  elsif match.instantiator?
1804
1892
  instantiator = match.instantiator
1893
+ # def self.find_or_create_by_user_id(*args)
1894
+ # guard_protected_attributes = false
1895
+ #
1896
+ # if args[0].is_a?(Hash)
1897
+ # guard_protected_attributes = true
1898
+ # attributes = args[0].with_indifferent_access
1899
+ # find_attributes = attributes.slice(*[:user_id])
1900
+ # else
1901
+ # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args)
1902
+ # end
1903
+ #
1904
+ # options = { :conditions => find_attributes }
1905
+ # set_readonly_option!(options)
1906
+ #
1907
+ # record = find(:first, options)
1908
+ #
1909
+ # if record.nil?
1910
+ # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
1911
+ # yield(record) if block_given?
1912
+ # record.save
1913
+ # record
1914
+ # else
1915
+ # record
1916
+ # end
1917
+ # end
1805
1918
  self.class_eval %{
1806
1919
  def self.#{method_id}(*args)
1807
1920
  guard_protected_attributes = false
@@ -1831,6 +1944,22 @@ module ActiveRecord #:nodoc:
1831
1944
  }, __FILE__, __LINE__
1832
1945
  send(method_id, *arguments, &block)
1833
1946
  end
1947
+ elsif match = DynamicScopeMatch.match(method_id)
1948
+ attribute_names = match.attribute_names
1949
+ super unless all_attributes_exists?(attribute_names)
1950
+ if match.scope?
1951
+ self.class_eval %{
1952
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
1953
+ options = args.extract_options! # options = args.extract_options!
1954
+ attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
1955
+ [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
1956
+ ) # )
1957
+ #
1958
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
1959
+ end # end
1960
+ }, __FILE__, __LINE__
1961
+ send(method_id, *arguments)
1962
+ end
1834
1963
  else
1835
1964
  super
1836
1965
  end
@@ -1862,12 +1991,16 @@ module ActiveRecord #:nodoc:
1862
1991
  attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
1863
1992
  end
1864
1993
 
1865
- def attribute_condition(argument)
1994
+ def attribute_condition(quoted_column_name, argument)
1866
1995
  case argument
1867
- when nil then "IS ?"
1868
- when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
1869
- when Range then "BETWEEN ? AND ?"
1870
- else "= ?"
1996
+ when nil then "#{quoted_column_name} IS ?"
1997
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)"
1998
+ when Range then if argument.exclude_end?
1999
+ "#{quoted_column_name} >= ? AND #{quoted_column_name} < ?"
2000
+ else
2001
+ "#{quoted_column_name} BETWEEN ? AND ?"
2002
+ end
2003
+ else "#{quoted_column_name} = ?"
1871
2004
  end
1872
2005
  end
1873
2006
 
@@ -1879,7 +2012,6 @@ module ActiveRecord #:nodoc:
1879
2012
  end
1880
2013
  end
1881
2014
 
1882
-
1883
2015
  # Defines an "attribute" method (like +inheritance_column+ or
1884
2016
  # +table_name+). A new (class) method will be created with the
1885
2017
  # given name. If a value is specified, the new method will
@@ -1926,7 +2058,11 @@ module ActiveRecord #:nodoc:
1926
2058
  # end
1927
2059
  #
1928
2060
  # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1929
- # <tt>:conditions</tt> and <tt>:include</tt> options in <tt>:find</tt>, which are merged.
2061
+ # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged.
2062
+ #
2063
+ # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing
2064
+ # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
2065
+ # array of strings format for your joins.
1930
2066
  #
1931
2067
  # class Article < ActiveRecord::Base
1932
2068
  # def self.find_with_scope
@@ -1972,7 +2108,7 @@ module ActiveRecord #:nodoc:
1972
2108
  end
1973
2109
 
1974
2110
  # Merge scopings
1975
- if action == :merge && current_scoped_methods
2111
+ if [:merge, :reverse_merge].include?(action) && current_scoped_methods
1976
2112
  method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
1977
2113
  case hash[method]
1978
2114
  when Hash
@@ -1980,7 +2116,11 @@ module ActiveRecord #:nodoc:
1980
2116
  (hash[method].keys + params.keys).uniq.each do |key|
1981
2117
  merge = hash[method][key] && params[key] # merge if both scopes have the same key
1982
2118
  if key == :conditions && merge
1983
- hash[method][key] = merge_conditions(params[key], hash[method][key])
2119
+ if params[key].is_a?(Hash) && hash[method][key].is_a?(Hash)
2120
+ hash[method][key] = merge_conditions(hash[method][key].deep_merge(params[key]))
2121
+ else
2122
+ hash[method][key] = merge_conditions(params[key], hash[method][key])
2123
+ end
1984
2124
  elsif key == :include && merge
1985
2125
  hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
1986
2126
  elsif key == :joins && merge
@@ -1990,7 +2130,11 @@ module ActiveRecord #:nodoc:
1990
2130
  end
1991
2131
  end
1992
2132
  else
1993
- hash[method] = params.merge(hash[method])
2133
+ if action == :reverse_merge
2134
+ hash[method] = hash[method].merge(params)
2135
+ else
2136
+ hash[method] = params.merge(hash[method])
2137
+ end
1994
2138
  end
1995
2139
  else
1996
2140
  hash[method] = params
@@ -2000,7 +2144,6 @@ module ActiveRecord #:nodoc:
2000
2144
  end
2001
2145
 
2002
2146
  self.scoped_methods << method_scoping
2003
-
2004
2147
  begin
2005
2148
  yield
2006
2149
  ensure
@@ -2018,10 +2161,20 @@ module ActiveRecord #:nodoc:
2018
2161
  @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
2019
2162
  end
2020
2163
 
2164
+ # Sets the default options for the model. The format of the
2165
+ # <tt>options</tt> argument is the same as in find.
2166
+ #
2167
+ # class Person < ActiveRecord::Base
2168
+ # default_scope :order => 'last_name, first_name'
2169
+ # end
2170
+ def default_scope(options = {})
2171
+ self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
2172
+ end
2173
+
2021
2174
  # Test whether the given method and optional key are scoped.
2022
2175
  def scoped?(method, key = nil) #:nodoc:
2023
2176
  if current_scoped_methods && (scope = current_scoped_methods[method])
2024
- !key || scope.has_key?(key)
2177
+ !key || !scope[key].nil?
2025
2178
  end
2026
2179
  end
2027
2180
 
@@ -2033,14 +2186,14 @@ module ActiveRecord #:nodoc:
2033
2186
  end
2034
2187
 
2035
2188
  def scoped_methods #:nodoc:
2036
- Thread.current[:"#{self}_scoped_methods"] ||= []
2189
+ Thread.current[:"#{self}_scoped_methods"] ||= self.default_scoping.dup
2037
2190
  end
2038
2191
 
2039
2192
  def current_scoped_methods #:nodoc:
2040
2193
  scoped_methods.last
2041
2194
  end
2042
2195
 
2043
- # Returns the class type of the record using the current module as a prefix. So descendents of
2196
+ # Returns the class type of the record using the current module as a prefix. So descendants of
2044
2197
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
2045
2198
  def compute_type(type_name)
2046
2199
  modularized_name = type_name_with_module(type_name)
@@ -2053,7 +2206,8 @@ module ActiveRecord #:nodoc:
2053
2206
  end
2054
2207
  end
2055
2208
 
2056
- # Returns the class descending directly from Active Record in the inheritance hierarchy.
2209
+ # Returns the class descending directly from ActiveRecord::Base or an
2210
+ # abstract class, if any, in the inheritance hierarchy.
2057
2211
  def class_of_active_record_descendant(klass)
2058
2212
  if klass.superclass == Base || klass.superclass.abstract_class?
2059
2213
  klass
@@ -2074,12 +2228,12 @@ module ActiveRecord #:nodoc:
2074
2228
  # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
2075
2229
  # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
2076
2230
  # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
2077
- def sanitize_sql_for_conditions(condition, table_name = quoted_table_name)
2231
+ def sanitize_sql_for_conditions(condition)
2078
2232
  return nil if condition.blank?
2079
2233
 
2080
2234
  case condition
2081
2235
  when Array; sanitize_sql_array(condition)
2082
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
2236
+ when Hash; sanitize_sql_hash_for_conditions(condition)
2083
2237
  else condition
2084
2238
  end
2085
2239
  end
@@ -2158,7 +2312,7 @@ module ActiveRecord #:nodoc:
2158
2312
  table_name = connection.quote_table_name(table_name)
2159
2313
  end
2160
2314
 
2161
- "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
2315
+ attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
2162
2316
  else
2163
2317
  sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
2164
2318
  end
@@ -2247,7 +2401,7 @@ module ActiveRecord #:nodoc:
2247
2401
  end
2248
2402
 
2249
2403
  VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
2250
- :order, :select, :readonly, :group, :from, :lock ]
2404
+ :order, :select, :readonly, :group, :having, :from, :lock ]
2251
2405
 
2252
2406
  def validate_find_options(options) #:nodoc:
2253
2407
  options.assert_valid_keys(VALID_FIND_OPTIONS)
@@ -2305,15 +2459,15 @@ module ActiveRecord #:nodoc:
2305
2459
  # object. The default implementation returns this record's id as a String,
2306
2460
  # or nil if this record's unsaved.
2307
2461
  #
2308
- # For example, suppose that you have a Users model, and that you have a
2309
- # <tt>map.resources :users</tt> route. Normally, +users_path+ will
2310
- # construct an URI with the user object's 'id' in it:
2462
+ # For example, suppose that you have a User model, and that you have a
2463
+ # <tt>map.resources :users</tt> route. Normally, +user_path+ will
2464
+ # construct a path with the user object's 'id' in it:
2311
2465
  #
2312
2466
  # user = User.find_by_name('Phusion')
2313
- # user_path(path) # => "/users/1"
2467
+ # user_path(user) # => "/users/1"
2314
2468
  #
2315
- # You can override +to_param+ in your model to make +users_path+ construct
2316
- # an URI using the user's name instead of the user's id:
2469
+ # You can override +to_param+ in your model to make +user_path+ construct
2470
+ # a path using the user's name instead of the user's id:
2317
2471
  #
2318
2472
  # class User < ActiveRecord::Base
2319
2473
  # def to_param # overridden
@@ -2322,7 +2476,7 @@ module ActiveRecord #:nodoc:
2322
2476
  # end
2323
2477
  #
2324
2478
  # user = User.find_by_name('Phusion')
2325
- # user_path(path) # => "/users/Phusion"
2479
+ # user_path(user) # => "/users/Phusion"
2326
2480
  def to_param
2327
2481
  # We can't use alias_method here, because method 'id' optimizes itself on the fly.
2328
2482
  (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
@@ -2359,9 +2513,9 @@ module ActiveRecord #:nodoc:
2359
2513
  write_attribute(self.class.primary_key, value)
2360
2514
  end
2361
2515
 
2362
- # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
2516
+ # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false.
2363
2517
  def new_record?
2364
- defined?(@new_record) && @new_record
2518
+ @new_record || false
2365
2519
  end
2366
2520
 
2367
2521
  # :call-seq:
@@ -2402,14 +2556,16 @@ module ActiveRecord #:nodoc:
2402
2556
  create_or_update || raise(RecordNotSaved)
2403
2557
  end
2404
2558
 
2405
- # Deletes the record in the database and freezes this instance to reflect that no changes should
2406
- # be made (since they can't be persisted).
2559
+ # Deletes the record in the database and freezes this instance to
2560
+ # reflect that no changes should be made (since they can't be
2561
+ # persisted). Returns the frozen instance.
2562
+ #
2563
+ # The row is simply removed with a SQL +DELETE+ statement on the
2564
+ # record's primary key, and no callbacks are executed.
2407
2565
  #
2408
- # Unlike #destroy, this method doesn't run any +before_delete+ and +after_delete+
2409
- # callbacks, nor will it enforce any association +:dependent+ rules.
2410
- #
2411
- # In addition to deleting this record, any defined +before_delete+ and +after_delete+
2412
- # callbacks are run, and +:dependent+ rules defined on associations are run.
2566
+ # To enforce the object's +before_destroy+ and +after_destroy+
2567
+ # callbacks, Observer methods, or any <tt>:dependent</tt> association
2568
+ # options, use <tt>#destroy</tt>.
2413
2569
  def delete
2414
2570
  self.class.delete(id) unless new_record?
2415
2571
  freeze
@@ -2593,7 +2749,6 @@ module ActiveRecord #:nodoc:
2593
2749
  assign_multiparameter_attributes(multi_parameter_attributes)
2594
2750
  end
2595
2751
 
2596
-
2597
2752
  # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
2598
2753
  def attributes
2599
2754
  self.attribute_names.inject({}) do |attrs, name|
@@ -2610,7 +2765,19 @@ module ActiveRecord #:nodoc:
2610
2765
  end
2611
2766
  end
2612
2767
 
2613
- # Format attributes nicely for inspect.
2768
+ # Returns an <tt>#inspect</tt>-like string for the value of the
2769
+ # attribute +attr_name+. String attributes are elided after 50
2770
+ # characters, and Date and Time attributes are returned in the
2771
+ # <tt>:db</tt> format. Other attributes return the value of
2772
+ # <tt>#inspect</tt> without modification.
2773
+ #
2774
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
2775
+ #
2776
+ # person.attribute_for_inspect(:name)
2777
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
2778
+ #
2779
+ # person.attribute_for_inspect(:created_at)
2780
+ # # => '"2009-01-12 04:48:57"'
2614
2781
  def attribute_for_inspect(attr_name)
2615
2782
  value = read_attribute(attr_name)
2616
2783
 
@@ -2739,7 +2906,7 @@ module ActiveRecord #:nodoc:
2739
2906
  id
2740
2907
  end
2741
2908
 
2742
- # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent.
2909
+ # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendant.
2743
2910
  # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to
2744
2911
  # set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. No such attribute would be set for objects of the
2745
2912
  # Message class in that example.
@@ -2966,4 +3133,23 @@ module ActiveRecord #:nodoc:
2966
3133
  value
2967
3134
  end
2968
3135
  end
3136
+
3137
+ Base.class_eval do
3138
+ extend QueryCache::ClassMethods
3139
+ include Validations
3140
+ include Locking::Optimistic, Locking::Pessimistic
3141
+ include AttributeMethods
3142
+ include Dirty
3143
+ include Callbacks, Observing, Timestamp
3144
+ include Associations, AssociationPreload, NamedScope
3145
+
3146
+ # AutosaveAssociation needs to be included before Transactions, because we want
3147
+ # #save_with_autosave_associations to be wrapped inside a transaction.
3148
+ include AutosaveAssociation, NestedAttributes
3149
+
3150
+ include Aggregations, Transactions, Reflection, Batches, Calculations, Serialization
3151
+ end
2969
3152
  end
3153
+
3154
+ # TODO: Remove this and make it work with LAZY flag
3155
+ require 'active_record/connection_adapters/abstract_adapter'