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.
- data/CHANGELOG +438 -396
- data/Rakefile +4 -2
- data/lib/active_record.rb +46 -43
- data/lib/active_record/association_preload.rb +34 -19
- data/lib/active_record/associations.rb +193 -251
- data/lib/active_record/associations/association_collection.rb +38 -21
- data/lib/active_record/associations/association_proxy.rb +11 -4
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +8 -8
- data/lib/active_record/associations/has_one_association.rb +11 -2
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/autosave_association.rb +349 -0
- data/lib/active_record/base.rb +292 -106
- data/lib/active_record/batches.rb +73 -0
- data/lib/active_record/calculations.rb +34 -16
- data/lib/active_record/callbacks.rb +37 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +16 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +103 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +28 -25
- data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -5
- data/lib/active_record/connection_adapters/mysql_adapter.rb +50 -21
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -41
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +41 -21
- data/lib/active_record/dirty.rb +1 -1
- data/lib/active_record/dynamic_scope_match.rb +25 -0
- data/lib/active_record/fixtures.rb +193 -198
- data/lib/active_record/locale/en.yml +1 -1
- data/lib/active_record/locking/optimistic.rb +33 -0
- data/lib/active_record/migration.rb +8 -2
- data/lib/active_record/named_scope.rb +13 -6
- data/lib/active_record/nested_attributes.rb +329 -0
- data/lib/active_record/query_cache.rb +25 -13
- data/lib/active_record/reflection.rb +6 -1
- data/lib/active_record/schema_dumper.rb +2 -0
- data/lib/active_record/serialization.rb +3 -1
- data/lib/active_record/serializers/json_serializer.rb +19 -0
- data/lib/active_record/serializers/xml_serializer.rb +28 -13
- data/lib/active_record/session_store.rb +318 -0
- data/lib/active_record/test_case.rb +15 -9
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +58 -8
- data/lib/active_record/validations.rb +29 -24
- data/lib/active_record/version.rb +2 -2
- data/test/cases/ar_schema_test.rb +0 -1
- data/test/cases/associations/belongs_to_associations_test.rb +35 -131
- data/test/cases/associations/cascaded_eager_loading_test.rb +8 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +29 -0
- data/test/cases/associations/eager_test.rb +137 -7
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +45 -7
- data/test/cases/associations/has_many_associations_test.rb +110 -149
- data/test/cases/associations/has_many_through_associations_test.rb +39 -7
- data/test/cases/associations/has_one_associations_test.rb +39 -92
- data/test/cases/associations/has_one_through_associations_test.rb +34 -3
- data/test/cases/associations/inner_join_association_test.rb +0 -5
- data/test/cases/associations/join_model_test.rb +5 -7
- data/test/cases/attribute_methods_test.rb +13 -1
- data/test/cases/autosave_association_test.rb +901 -0
- data/test/cases/base_test.rb +41 -21
- data/test/cases/batches_test.rb +61 -0
- data/test/cases/calculations_test.rb +37 -17
- data/test/cases/callbacks_test.rb +43 -5
- data/test/cases/connection_pool_test.rb +25 -0
- data/test/cases/copy_table_test_sqlite.rb +11 -0
- data/test/cases/datatype_test_postgresql.rb +1 -0
- data/test/cases/defaults_test.rb +37 -26
- data/test/cases/dirty_test.rb +26 -2
- data/test/cases/finder_test.rb +79 -44
- data/test/cases/fixtures_test.rb +15 -19
- data/test/cases/helper.rb +26 -19
- data/test/cases/inheritance_test.rb +2 -2
- data/test/cases/json_serialization_test.rb +1 -1
- data/test/cases/locking_test.rb +23 -5
- data/test/cases/method_scoping_test.rb +126 -3
- data/test/cases/migration_test.rb +253 -237
- data/test/cases/named_scope_test.rb +73 -3
- data/test/cases/nested_attributes_test.rb +509 -0
- data/test/cases/query_cache_test.rb +0 -4
- data/test/cases/reflection_test.rb +13 -3
- data/test/cases/reload_models_test.rb +3 -1
- data/test/cases/repair_helper.rb +50 -0
- data/test/cases/schema_dumper_test.rb +0 -1
- data/test/cases/transactions_test.rb +177 -12
- data/test/cases/validations_i18n_test.rb +288 -294
- data/test/cases/validations_test.rb +230 -180
- data/test/cases/xml_serialization_test.rb +19 -1
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/member_types.yml +6 -0
- data/test/fixtures/members.yml +3 -1
- data/test/fixtures/people.yml +10 -1
- data/test/fixtures/toys.yml +4 -0
- data/test/models/author.rb +1 -2
- data/test/models/bird.rb +3 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +3 -0
- data/test/models/developer.rb +12 -0
- data/test/models/event.rb +3 -0
- data/test/models/member.rb +1 -0
- data/test/models/member_detail.rb +1 -0
- data/test/models/member_type.rb +3 -0
- data/test/models/owner.rb +2 -1
- data/test/models/parrot.rb +2 -0
- data/test/models/person.rb +6 -0
- data/test/models/pet.rb +2 -1
- data/test/models/pirate.rb +55 -1
- data/test/models/post.rb +6 -0
- data/test/models/project.rb +1 -0
- data/test/models/reply.rb +6 -0
- data/test/models/ship.rb +8 -1
- data/test/models/ship_part.rb +5 -0
- data/test/models/topic.rb +13 -1
- data/test/models/toy.rb +4 -0
- data/test/schema/schema.rb +35 -2
- metadata +70 -9
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
data/lib/active_record/base.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
639
|
-
#
|
640
|
-
#
|
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
|
-
#
|
643
|
-
#
|
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
|
-
#
|
647
|
-
# sanitized and then queried against
|
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
|
-
|
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
|
-
#
|
726
|
-
#
|
727
|
-
#
|
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
|
-
#
|
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
|
-
#
|
733
|
-
#
|
734
|
-
#
|
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
|
775
|
+
# # Delete a single row
|
739
776
|
# Todo.delete(1)
|
740
777
|
#
|
741
|
-
# # Delete multiple
|
742
|
-
#
|
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
|
821
|
-
#
|
822
|
-
#
|
823
|
-
#
|
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+ -
|
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
|
-
# ====
|
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
|
-
|
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
|
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 =
|
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 =
|
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
|
-
|
1388
|
-
logger.add(log_level,
|
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
|
-
|
1466
|
-
|
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
|
-
|
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]) ||
|
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)
|
1766
|
-
#
|
1767
|
-
#
|
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
|
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
|
-
#
|
1773
|
-
#
|
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(
|
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
|
1870
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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.
|
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
|
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
|
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
|
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
|
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)}
|
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
|
2309
|
-
# <tt>map.resources :users</tt> route. Normally, +
|
2310
|
-
# construct
|
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(
|
2467
|
+
# user_path(user) # => "/users/1"
|
2314
2468
|
#
|
2315
|
-
# You can override +to_param+ in your model to make +
|
2316
|
-
#
|
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(
|
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
|
-
|
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
|
2406
|
-
# be made (since they can't be
|
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
|
-
#
|
2409
|
-
# callbacks,
|
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
|
-
#
|
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
|
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'
|