aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2010-06-28 00:12:15 +0200
committerXavier Noria <fxn@hashref.com>2010-06-28 00:12:15 +0200
commit4329f8133fee8e4f3e558787f67de59f0c4a4dd1 (patch)
tree346ef7340d8348e50d119ca749a16c1654c20a08 /activerecord
parentc37f7d66e49ffe5ac2115cc30e5529fd1c2924a8 (diff)
parentebee77a28a7267d5f23a28ba23c1eb88a2d7d527 (diff)
downloadrails-4329f8133fee8e4f3e558787f67de59f0c4a4dd1.tar.gz
rails-4329f8133fee8e4f3e558787f67de59f0c4a4dd1.tar.bz2
rails-4329f8133fee8e4f3e558787f67de59f0c4a4dd1.zip
Merge remote branch 'rails/master'
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG508
-rw-r--r--activerecord/Rakefile5
-rw-r--r--activerecord/examples/performance.rb6
-rw-r--r--activerecord/lib/active_record/aggregations.rb6
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb14
-rw-r--r--activerecord/lib/active_record/autosave_association.rb4
-rw-r--r--activerecord/lib/active_record/base.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb52
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb27
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb20
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb32
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb19
-rw-r--r--activerecord/lib/active_record/railtie.rb3
-rw-r--r--activerecord/lib/active_record/railties/log_subscriber.rb32
-rw-r--r--activerecord/lib/active_record/relation.rb11
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb8
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb169
-rw-r--r--activerecord/lib/active_record/session_store.rb8
-rw-r--r--activerecord/lib/active_record/validations/associated.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb14
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb2
-rw-r--r--activerecord/test/cases/adapters/firebird/connection_test.rb (renamed from activerecord/test/cases/connection_test_firebird.rb)0
-rw-r--r--activerecord/test/cases/adapters/firebird/default_test.rb (renamed from activerecord/test/cases/default_test_firebird.rb)0
-rw-r--r--activerecord/test/cases/adapters/firebird/migration_test.rb (renamed from activerecord/test/cases/migration_test_firebird.rb)0
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb (renamed from activerecord/test/cases/active_schema_test_mysql.rb)6
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb (renamed from activerecord/test/cases/connection_test_mysql.rb)0
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb (renamed from activerecord/test/cases/reserved_word_test_mysql.rb)0
-rw-r--r--activerecord/test/cases/adapters/oracle/synonym_test.rb (renamed from activerecord/test/cases/synonym_test_oracle.rb)0
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb (renamed from activerecord/test/cases/active_schema_test_postgresql.rb)5
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb (renamed from activerecord/test/cases/datatype_test_postgresql.rb)2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb (renamed from activerecord/test/cases/schema_authorization_test_postgresql.rb)2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb (renamed from activerecord/test/cases/schema_test_postgresql.rb)4
-rw-r--r--activerecord/test/cases/adapters/sqlite/copy_table_test.rb (renamed from activerecord/test/cases/copy_table_test_sqlite.rb)0
-rw-r--r--activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb56
-rw-r--r--activerecord/test/cases/aggregations_test.rb28
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb4
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/calculations_test.rb2
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb9
-rw-r--r--activerecord/test/cases/method_scoping_test.rb8
-rw-r--r--activerecord/test/cases/migration_test.rb112
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb28
-rw-r--r--activerecord/test/cases/reflection_test.rb10
-rw-r--r--activerecord/test/cases/relations_test.rb56
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb12
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb50
-rw-r--r--activerecord/test/fixtures/topics.yml2
-rw-r--r--activerecord/test/schema/schema.rb1
55 files changed, 846 insertions, 554 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index def519c05d..09f897eaac 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -309,7 +309,7 @@
* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
-* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess]
+* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess]
* Added first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
@@ -620,7 +620,7 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* Ensure that mysql quotes table names with database names correctly. Closes #9911 [crayz]
- "foo.bar" => "`foo`.`bar`"
+ "foo.bar" => "`foo`.`bar`"
* Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett]
http://errtheblog.com/post/2381
@@ -755,7 +755,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
# Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik]
-* Added :unless clause to validations #8003 [monki]. Example:
+* Added :unless clause to validations #8003 [monki]. Example:
def using_open_id?
!identity_url.blank?
@@ -775,7 +775,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
OLD
belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
-
+
NEW
belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id
@@ -881,9 +881,9 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
-
+
...can now be written as:
-
+
create_table :products do |t|
t.integer :shop_id, :creator_id
t.string :name, :value, :default => "Untitled"
@@ -959,7 +959,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke]
Task.cache { Task.find(1); Task.find(1) } #=> 1 query
-
+
* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
* Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen]
@@ -1228,7 +1228,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen]
-* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.]
+* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.]
* Don't save has_one associations unnecessarily. #5735 [Jonathan Viney]
@@ -1248,8 +1248,8 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
- assert (Topic.exists?(:author_name => "David"))
- assert (Topic.exists?(:author_name => "Mary", :approved => true))
+ assert (Topic.exists?(:author_name => "David"))
+ assert (Topic.exists?(:author_name => "Mary", :approved => true))
assert (Topic.exists?(["parent_id = ?", 1]))
* Schema dumper quotes date :default values. [Dave Thomas]
@@ -1327,14 +1327,14 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
- Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
+ Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
...is the same as:
Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
-
+
This makes it easier to pass in the options from a form or otherwise outside.
-
+
* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com]
@@ -1362,7 +1362,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
-* Provide Association Extensions access to the instance that the association is being accessed from.
+* Provide Association Extensions access to the instance that the association is being accessed from.
Closes #4433 [Josh Susser]
* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
@@ -1381,7 +1381,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com]
-* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org]
@@ -1391,7 +1391,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
-* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
+* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
* Replace superfluous name_to_class_name variant with camelize. [Marcel Molina Jr.]
@@ -1401,7 +1401,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair@orcaware.com>]
-* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com]
+* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com]
* New Frontbase connections don't start in auto-commit mode. Closes #4922. [mlaster@metavillage.com]
@@ -1413,9 +1413,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
+* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
-* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
+* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com]
@@ -1466,7 +1466,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
*1.15.3* (March 12th, 2007)
- * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
+ * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
* Consistently quote primary key column names. #7763 [toolmantim]
@@ -1705,8 +1705,8 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
- assert (Topic.exists?(:author_name => "David"))
- assert (Topic.exists?(:author_name => "Mary", :approved => true))
+ assert (Topic.exists?(:author_name => "David"))
+ assert (Topic.exists?(:author_name => "Mary", :approved => true))
assert (Topic.exists?(["parent_id = ?", 1]))
* Schema dumper quotes date :default values. [Dave Thomas]
@@ -1780,7 +1780,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
- Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
+ Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
...is the same as:
@@ -1815,7 +1815,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
-* Provide Association Extensions access to the instance that the association is being accessed from.
+* Provide Association Extensions access to the instance that the association is being accessed from.
Closes #4433 [Josh Susser]
* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
@@ -1834,7 +1834,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com]
-* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org]
@@ -1844,7 +1844,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
-* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
+* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair@orcaware.com>]
@@ -1854,9 +1854,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
+* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
-* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
+* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com]
@@ -1904,7 +1904,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. #4147 [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. #4679 [Mislav Marohnić]
+* Fix syntax error in documentation. #4679 [Mislav Marohnić]
* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
@@ -2200,7 +2200,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
<id type="integer">1</id>
<credit-limit type="integer">50</credit-limit>
</account>
- </firm>
+ </firm>
* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
@@ -2390,17 +2390,17 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
-* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
+* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-* made .find() work on :through relations. [Tobias Lütke]
+* made .find() work on :through relations. [Tobias Lütke]
* Fix typo in association docs. #3296. [Blair Zajac]
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
*1.13.2* (December 13th, 2005)
@@ -2663,7 +2663,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net]
-* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
@@ -2818,7 +2818,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
def self.search(q)
find(:all, :conditions => ["body = ?", q])
end
- end
+ end
class Post < AR:B
has_many :comments
@@ -2877,7 +2877,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
def evaluate_velocity(developer)
...
end
- end
+ end
..raising an exception will cause the object not to be added (or removed, with before_remove).
@@ -2913,10 +2913,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio
...should instead be:
Developer.find(
- :all,
- :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
:conditions => 'project_id=1'
- )
+ )
* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
@@ -2953,7 +2953,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com]
-* Allow before/after update hooks to work on models using optimistic locking
+* Allow before/after update hooks to work on models using optimistic locking
* Eager loading of dependent has_one associations won't delete the association #1212
@@ -3133,10 +3133,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
- Developer.find_all nil, 'id ASC', 5 # return the first five developers
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
- This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
@@ -3171,8 +3171,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
- * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
- +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
+ +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
objects that should be inspected to determine which attributes triggered the errors.
* +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
@@ -3258,11 +3258,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
- 1. Created a new columns method that is much cleaner.
- 2. Corrected a problem with the select and select_all methods
- that didn't account for the LIMIT clause being passed into raw SQL statements.
- 3. Implemented the string_to_time method in order to create proper instances of the time class.
- 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
+ 1. Created a new columns method that is much cleaner.
+ 2. Corrected a problem with the select and select_all methods
+ that didn't account for the LIMIT clause being passed into raw SQL statements.
+ 3. Implemented the string_to_time method in order to create proper instances of the time class.
+ 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
@@ -3385,9 +3385,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed binary support for PostgreSQL #444 [alex@byzantine.no]
-* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the
- collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If
- it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards,
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the
+ collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If
+ it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards,
it'll take one less SELECT query if you use length.
* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
@@ -3400,7 +3400,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
-* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
* Added that Observers can use the observes class method instead of overwriting self.observed_class().
@@ -3463,7 +3463,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Post < ActiveRecord::Base
has_one :tagging, :as => :taggable
end
-
+
Post.find :all, :include => :tagging
* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson]
@@ -3474,7 +3474,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
has_many :photos, :as => :photographic
belongs_to :firm
end
-
+
class Firm < ActiveRecord::Base
has_many :photo_collections
has_many :photos, :through => :photo_collections
@@ -3536,7 +3536,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
In this example, :include => false disables the default eager association from loading. :select changes the standard
select clause. :joins specifies a join that is added to the end of the has_many :through query.
-
+
class Post < ActiveRecord::Base
has_many :tags, :through => :taggings, :include => :tagging do
def add_joins_and_select
@@ -3545,7 +3545,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
end
end
end
-
+
* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson]
* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney]
@@ -3562,12 +3562,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
has_many :clients
has_many :invoices, :through => :clients
end
-
+
class Client < ActiveRecord::Base
belongs_to :firm
has_many :invoices
end
-
+
class Invoice < ActiveRecord::Base
belongs_to :client
end
@@ -3619,19 +3619,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class CachedModel < ActiveRecord::Base
self.abstract_class = true
end
-
+
class Post < CachedModel
end
-
+
CachedModel.abstract_class?
=> true
-
+
Post.abstract_class?
=> false
Post.base_class
=> Post
-
+
Post.table_name
=> 'posts'
@@ -3656,9 +3656,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
topic.to_xml
-
+
...returns:
-
+
<?xml version="1.0" encoding="UTF-8"?>
<topic>
<title>The First Topic</title>
@@ -3673,13 +3673,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
<parent-id></parent-id>
<last-read type="date">2004-04-15</last-read>
</topic>
-
+
...and you can configure with:
-
+
topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
-
+
...that'll return:
-
+
<topic>
<title>The First Topic</title>
<author-name>David</author-name>
@@ -3689,13 +3689,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
<parent-id></parent-id>
<last-read type="date">2004-04-15</last-read>
</topic>
-
+
You can even do load first-level associations as part of the document:
-
+
firm.to_xml :include => [ :account, :clients ]
-
+
...that'll return something like:
-
+
<?xml version="1.0" encoding="UTF-8"?>
<firm>
<id type="integer">1</id>
@@ -3715,7 +3715,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
<id type="integer">1</id>
<credit-limit type="integer">50</credit-limit>
</account>
- </firm>
+ </firm>
* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
@@ -3733,9 +3733,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* The first time a table is referenced in a join, no alias is used.
* After that, the parent class name and the reflection name are used.
-
+
Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
-
+
* Any additional join references get a numerical suffix like '_2', '_3', etc.
* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
@@ -3747,28 +3747,28 @@ in effect. Added :readonly finder constraint. Calling an association collectio
=> authors
+- posts
+- comments
-
+
# cascaded in two levels and normal association
>> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
=> authors
+- posts
+- comments
+- categorizations
-
+
# cascaded in two levels with two has_many associations
>> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
=> authors
+- posts
+- comments
+- categorizations
-
+
# cascaded in three levels
>> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
=> companies
+- groups
+- members
+- favorites
-
+
* Make counter cache work when replacing an association #3245 [eugenol@gmail.com]
* Make migrations verbose [Jamis Buck]
@@ -3905,17 +3905,17 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
-* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
+* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-* made .find() work on :through relations. [Tobias Lütke]
+* made .find() work on :through relations. [Tobias Lütke]
* Fix typo in association docs. #3296. [Blair Zajac]
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
*1.13.2* (December 13th, 2005)
@@ -3928,7 +3928,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Post
has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
end
-
+
post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
@@ -4077,9 +4077,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# Associated with :post_id
Comment.create :body => "Hello world"
end
-
+
This is rarely used directly, but allows for find_or_create on associations. So you can do:
-
+
# If the tag doesn't exist, a new one is created that's associated with the person
person.tags.find_or_create_by_name("Summer")
@@ -4087,7 +4087,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# No 'Summer' tag exists
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
-
+
# Now the 'Summer' tag does exist
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
@@ -4178,7 +4178,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net]
-* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
@@ -4333,14 +4333,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
def self.search(q)
find(:all, :conditions => ["body = ?", q])
end
- end
+ end
class Post < AR:B
has_many :comments
end
Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
-
+
NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
@@ -4388,14 +4388,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Project
has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
-
+
def evaluate_velocity(developer)
...
end
- end
-
+ end
+
..raising an exception will cause the object not to be added (or removed, with before_remove).
-
+
* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
@@ -4424,14 +4424,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
-
+
...should instead be:
-
+
Developer.find(
- :all,
- :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
:conditions => 'project_id=1'
- )
+ )
* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
@@ -4450,12 +4450,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
david.save
-
+
If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
project is saved when david.save is called.
-
+
Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
-
+
david.project_ids = [1, 5, 7]
* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
@@ -4468,7 +4468,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com]
-* Allow before/after update hooks to work on models using optimistic locking
+* Allow before/after update hooks to work on models using optimistic locking
* Eager loading of dependent has_one associations won't delete the association #1212
@@ -4499,7 +4499,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Conditional validations can also solve the salted login generator problem:
validates_confirmation_of :password, :if => :new_password?
-
+
Using blocks:
validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
@@ -4562,11 +4562,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio
puts "Written by: " + post.author.name
puts "Last comment on: " + post.comments.first.created_on
end
-
+
This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
-
+
for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
-
+
...and the number of database queries needed is now 1.
* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
@@ -4648,10 +4648,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
- Developer.find_all nil, 'id ASC', 5 # return the first five developers
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
-
- This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
+
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
@@ -4686,8 +4686,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
- * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
- +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
+ +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
objects that should be inspected to determine which attributes triggered the errors.
* +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
@@ -4715,16 +4715,16 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Validates whether the value of the specified attribute is numeric by trying to convert it to
a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
<tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
-
+
class Person < ActiveRecord::Base
validates_numericality_of :value, :on => :create
end
-
+
Configuration options:
* <tt>message</tt> - A custom error message (default is: "is not a number")
* <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
* <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
-
+
* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
@@ -4773,17 +4773,17 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
- 1. Created a new columns method that is much cleaner.
- 2. Corrected a problem with the select and select_all methods
- that didn't account for the LIMIT clause being passed into raw SQL statements.
- 3. Implemented the string_to_time method in order to create proper instances of the time class.
- 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
+ 1. Created a new columns method that is much cleaner.
+ 2. Corrected a problem with the select and select_all methods
+ that didn't account for the LIMIT clause being passed into raw SQL statements.
+ 3. Implemented the string_to_time method in order to create proper instances of the time class.
+ 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
-
+
class Person < ActiveRecord::Base
validates_each :first_name, :last_name do |record, attr|
record.errors.add attr, 'starts with z.' if attr[0] == ?z
@@ -4861,19 +4861,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Book < ActiveRecord::Base
has_many :pages
belongs_to :library
-
+
validates_associated :pages, :library
end
-
+
* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
== Unsaved objects and associations
-
+
You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
aware of, mostly involving the saving of associated objects.
-
+
=== One-to-one associations
-
+
* Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
order to update their primary keys - except if the parent object is unsaved (new_record? == true).
* If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
@@ -4881,9 +4881,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
* Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
not save the parent either.
-
+
=== Collections
-
+
* Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
(the owner of the collection) is not yet stored in the database.
* If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
@@ -4900,9 +4900,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed binary support for PostgreSQL #444 [alex@byzantine.no]
-* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the
- collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If
- it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards,
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the
+ collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If
+ it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards,
it'll take one less SELECT query if you use length.
* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
@@ -4915,7 +4915,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
-* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
* Added that Observers can use the observes class method instead of overwriting self.observed_class().
@@ -4923,7 +4923,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class ListSweeper < ActiveRecord::Base
def self.observed_class() [ List, Item ]
end
-
+
After:
class ListSweeper < ActiveRecord::Base
observes List, Item
@@ -4943,7 +4943,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
page.views # => 1
page.increment!(:views) # executes an UPDATE statement
page.views # => 2
-
+
page.increment(:views).increment!(:views)
page.views # => 4
@@ -4955,32 +4955,32 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added automated optimistic locking if the field <tt>lock_version</tt> is present. Each update to the
record increments the lock_version column and the locking facilities ensure that records instantiated twice
will let the last one saved raise a StaleObjectError if the first was also updated. Example:
-
+
p1 = Person.find(1)
p2 = Person.find(1)
-
+
p1.first_name = "Michael"
p1.save
-
+
p2.first_name = "should fail"
p2.save # Raises a ActiveRecord::StaleObjectError
-
+
You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
or otherwise apply the business logic needed to resolve the conflict.
#384 [Michael Koziarski]
-* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL.
+* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL.
They work by appending the name of an attribute to <tt>find_by_</tt>, so you get finders like <tt>Person.find_by_user_name,
Payment.find_by_transaction_id</tt>. So instead of writing <tt>Person.find_first(["user_name = ?", user_name])</tt>, you just do
<tt>Person.find_by_user_name(user_name)</tt>.
-
+
It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
<tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
- <tt>Person.find_first(["user_name = ? AND password = ?", user_name, password])</tt>, you just do
+ <tt>Person.find_first(["user_name = ? AND password = ?", user_name, password])</tt>, you just do
<tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
- While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like
+ While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like
<tt>Payment.find_all_by_amount(50)</tt> that is turned into <tt>Payment.find_all(["amount = ?", 50])</tt>. This is something not as equally useful,
though, as it's not possible to specify the order in which the objects are returned.
@@ -4988,13 +4988,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Before:
before_destroy(Proc.new{ |record| Person.destroy_all "firm_id = #{record.id}" })
-
+
After:
before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh Peek]
-* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates
+* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates
and times from the database. This is set to :local by default.
* Added the possibility for adapters to overwrite add_limit! to implement a different limiting scheme than "LIMIT X" used by MySQL, PostgreSQL, and SQLite.
@@ -5013,7 +5013,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Lütke]
-* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it.
+* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it.
If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 [Jeremy Kemper]
* Fixed broken transactions that were actually only running object-level and not db level transactions [andreas]
@@ -5033,12 +5033,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
require_association 'person'
class Employee < Person
end
-
+
after:
class Employee < Person
end
- This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes.
+ This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes.
* Added that Base.update_all and Base.delete_all return an integer of the number of affected rows #341
@@ -5056,12 +5056,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added more informative exceptions in establish_connection #356 [Jeremy Kemper]
-* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise).
+* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise).
Before:
person.attributes = @params["person"]
person.save
-
+
Now:
person.update_attributes(@params["person"])
@@ -5073,7 +5073,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 [Jeremy Kemper]
-* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used
+* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used
the attributes[] hash instead of checking for @base.respond_to?(attr.to_s). [Marten]
* Fixed that Base.table_name would expect a parameter when used in has_and_belongs_to_many joins [Anna Lissa Cruz]
@@ -5107,10 +5107,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio
belongs_to :todo_list
end
-* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in
+* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in
categories and the likes. [Tobias Lütke]
-* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names
+* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names
created_at/created_on or updated_at/updated_on are present. [Tobias Lütke]
* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Lütke]
@@ -5124,14 +5124,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added a better exception for when a type column is used in a table without the intention of triggering single-table inheritance. Example:
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'bad_class!'.
- This error is raised because the column 'type' is reserved for storing the class in case of inheritance.
- Please rename this column if you didn't intend it to be used for storing the inheritance class or
+ This error is raised because the column 'type' is reserved for storing the class in case of inheritance.
+ Please rename this column if you didn't intend it to be used for storing the inheritance class or
overwrite Company.inheritance_column to use another column for that information.
* Added that single-table inheritance will only kick in if the inheritance_column (by default "type") is present. Otherwise, inheritance won't
have any magic side effects.
-* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin]
+* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin]
* Fixed Base.errors to be indifferent as to whether strings or symbols are used. Examples:
@@ -5145,7 +5145,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
errors.on(:name) # => "must be shorter"
errors.on("name") # => "must be shorter"
-* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching
+* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching
it against the regular expression provided. [Marcel Molina Jr.]
class Person < ActiveRecord::Base
@@ -5155,14 +5155,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Lütke]. Example:
Validates that the specified attribute matches the length restrictions supplied in either:
-
+
- configuration[:minimum]
- configuration[:maximum]
- configuration[:is]
- configuration[:within] (aka. configuration[:in])
-
+
Only one option can be used at a time.
-
+
class Person < ActiveRecord::Base
validates_length_of :first_name, :maximum=>30
validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
@@ -5170,53 +5170,53 @@ in effect. Added :readonly finder constraint. Calling an association collectio
validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
end
-
+
* Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself.
-* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system.
+* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system.
Useful for making sure that only one user can be named "davidhh".
-
+
class Person < ActiveRecord::Base
validates_uniqueness_of :user_name
end
-
+
When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified
attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
* Added Base.validates_confirmation_of that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
-
+
Model:
class Person < ActiveRecord::Base
validates_confirmation_of :password
end
-
+
View:
<%= password_field "person", "password" %>
<%= password_field "person", "password_confirmation" %>
-
+
The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
* Added Base.validates_acceptance_of that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
-
+
class Person < ActiveRecord::Base
validates_acceptance_of :terms_of_service
end
-
+
The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
-
+
* Added validation macros to make the stackable just like the lifecycle callbacks. Examples:
class Person < ActiveRecord::Base
validate { |record| record.errors.add("name", "too short") unless name.size > 10 }
validate { |record| record.errors.add("name", "too long") unless name.size < 20 }
validate_on_create :validate_password
-
+
private
def validate_password
errors.add("password", "too short") unless password.size > 6
@@ -5232,7 +5232,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer
columns.
-* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient
+* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient
[Florian Weber]
* Added counter_sql option for has_many associations [Jeremy Kemper]. Documentation:
@@ -5299,9 +5299,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
fixtures/developers/fixtures.yaml
fixtures/accounts/fixtures.yaml
-
+
...you now need to do:
-
+
fixtures/developers.yaml
fixtures/accounts.yaml
@@ -5328,13 +5328,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
name: David Heinemeier Hansson
birthday: 1979-10-15
profession: Systems development
-
+
steve:
id: 2
name: Steve Ross Kellock
birthday: 1974-09-27
profession: guy with keyboard
-
+
The change is NOT backwards compatible. Fixtures written in the old YAML style needs to be rewritten!
* All associations will now attempt to require the classes that they associate to. Relieving the need for most explicit 'require' statements.
@@ -5342,7 +5342,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
*1.1.0* (34)
-* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically
+* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically
instantiated in instance variables relating to their names using the following style:
class FixturesTest < Test::Unit::TestCase
@@ -5359,10 +5359,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio
attributes. This is really useful when you have information that's only relevant to the join itself, such as a "added_on" column for an association
between post and category. The added attributes will automatically be injected into objects retrieved through the association similar to the piggy-back
approach:
-
+
post.categories.push_with_attributes(category, :added_on => Date.today)
post.categories.first.added_on # => Date.today
-
+
NOTE: The categories table doesn't have a added_on column, it's the categories_post join table that does!
* Fixed that :exclusively_dependent and :dependent can't be activated at the same time on has_many associations [Jeremy Kemper]
@@ -5415,8 +5415,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Improved the speed of respond_to? by placing the dynamic methods lookup table in a hash [geech]
-* Added that any additional fields added to the join table in a has_and_belongs_to_many association
- will be placed as attributes when pulling records out through has_and_belongs_to_many associations.
+* Added that any additional fields added to the join table in a has_and_belongs_to_many association
+ will be placed as attributes when pulling records out through has_and_belongs_to_many associations.
This is helpful when have information about the association itself that you want available on retrival.
* Added better loading exception catching and RubyGems retries to the database adapters [alexeyv]
@@ -5425,9 +5425,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed Base#transaction so that it returns the result of the last expression in the transaction block [alexeyv]
-* Added Fixture#find to find the record corresponding to the fixture id. The record
+* Added Fixture#find to find the record corresponding to the fixture id. The record
class name is guessed by using Inflector#classify (also new) on the fixture directory name.
-
+
Before: Document.find(@documents["first"]["id"])
After : @documents["first"].find
@@ -5437,7 +5437,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed quoting problems on SQLite by adding quote_string to the AbstractAdapter that can be overwritten by the concrete
adapters for a call to the dbm. [Andreas Schwarz]
-
+
* Removed RubyGems backup strategy for requiring SQLite-adapter -- if people want to use gems, they're already doing it with AR.
@@ -5453,19 +5453,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added serialize as a new class method to control when text attributes should be YAMLized or not. This means that automated
serialization of hashes, arrays, and so on WILL NO LONGER HAPPEN (#10). You need to do something like this:
-
+
class User < ActiveRecord::Base
serialize :settings
end
-
+
This will assume that settings is a text column and will now YAMLize any object put in that attribute. You can also specify
an optional :class_name option that'll raise an exception if a serialized object is retrieved as a descendant of a class not in
the hierarchy. Example:
-
+
class User < ActiveRecord::Base
serialize :settings, :class_name => "Hash"
end
-
+
user = User.create("settings" => %w( one two three ))
User.find(user.id).settings # => raises SerializationTypeMismatch
@@ -5480,7 +5480,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added the possibility to chain the return of what happened inside a logged block [geech]:
- This now works:
+ This now works:
log { ... }.map { ... }
Instead of doing:
@@ -5490,7 +5490,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added "socket" option for the MySQL adapter, so you can change it to something else than "/tmp/mysql.sock" [Anna Lissa Cruz]
-* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema,
+* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema,
person.respond_to? "name" will return true.
* Added Base.benchmark which can be used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block.
@@ -5511,7 +5511,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added compatibility with 2.x series of sqlite-ruby drivers. [Jamis Buck]
* Added type safety for association assignments, so a ActiveRecord::AssociationTypeMismatch will be raised if you attempt to
- assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on
+ assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on
mistaken association assignments. [Reported by Andreas Schwarz]
* Added the option to keep many fixtures in one single YAML document [what-a-day]
@@ -5523,7 +5523,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added the option to observer more than one class at the time by specifying observed_class as an array
-* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them
+* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them
on PostgreSQL. [Dave Steinberg]
* Changed that integer and floats set to "" through attributes= remain as NULL. This was especially a problem for scaffolding and postgresql. (#49)
@@ -5565,8 +5565,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio
directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
to guess the table name from even when called on Reply. The guessing rules are as follows:
* Class name ends in "x", "ch" or "ss": "es" is appended, so a Search class becomes a searches table.
- * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies",
- so a Category class becomes a categories table.
+ * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies",
+ so a Category class becomes a categories table.
* Class name ends in "fe": The "fe" is replaced with "ves", so a Wife class becomes a wives table.
* Class name ends in "lf" or "rf": The "f" is replaced with "ves", so a Half class becomes a halves table.
* Class name ends in "person": The "person" is replaced with "people", so a Salesperson class becomes a salespeople table.
@@ -5579,14 +5579,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Class name with word compositions: Compositions are underscored, so CreditCard class becomes a credit_cards table.
Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended.
So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
-
+
You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a
"mice" table. Example:
-
+
class Mouse < ActiveRecord::Base
def self.table_name() "mice" end
end
-
+
This conversion is now done through an external class called Inflector residing in lib/active_record/support/inflector.rb.
* Added find_all_in_collection to has_many defined collections. Works like this:
@@ -5594,7 +5594,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Firm < ActiveRecord::Base
has_many :clients
end
-
+
firm.id # => 1
firm.find_all_in_clients "revenue > 1000" # SELECT * FROM clients WHERE firm_id = 1 AND revenue > 1000
@@ -5610,11 +5610,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio
errors.add_on_boundry_breaking "password", 3..20
end
end
-
+
This will add an error to the tune of "is too short (minimum is 3 characters)" or "is too long (minimum is 20 characters)" if
the password is outside the boundry. The messages can be changed by passing a third and forth parameter as message strings.
-* Implemented a clone method that works properly with AR. It returns a clone of the record that
+* Implemented a clone method that works properly with AR. It returns a clone of the record that
hasn't been assigned an id yet and is treated as a new record.
* Allow for domain sockets in PostgreSQL by not assuming localhost when no host is specified [Scott Barron]
@@ -5645,12 +5645,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
*0.9.3*
-* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott]
+* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott]
* Added :exclusively_dependent option to the has_many association macro. The doc reads:
If set to true all the associated object are deleted in one SQL statement without having their
- before_destroy callback run. This should only be used on associations that depend solely on
+ before_destroy callback run. This should only be used on associations that depend solely on
this class and don't need to do any clean-up in before_destroy. The upside is that it's much
faster, especially if there's a counter_cache involved.
@@ -5662,7 +5662,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed the use of floats (was broken since 0.9.0+)
-* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with
+* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with
Action Pack scaffolding.
* Fixed booleans support for PostgreSQL (use real true/false on boolean fields instead of 0/1 on tinyints) [radsaq]
@@ -5688,21 +5688,21 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Event < ActiveRecord::Base
has_one_and_belongs_to_many :sponsors
end
-
+
class Sponsor < ActiveRecord::Base
has_one_and_belongs_to_many :sponsors
end
Earlier, you'd have to use synthetic methods for creating associations between two objects of the above class:
-
+
roskilde_festival.add_to_sponsors(carlsberg)
roskilde_festival.remove_from_sponsors(carlsberg)
nike.add_to_events(world_cup)
nike.remove_from_events(world_cup)
-
+
Now you can use regular array-styled methods:
-
+
roskilde_festival.sponsors << carlsberg
roskilde_festival.sponsors.delete(carlsberg)
@@ -5711,7 +5711,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added delete method for has_many associations. Using this will nullify an association between the has_many and the belonging
object by setting the foreign key to null. Consider this model:
-
+
class Post < ActiveRecord::Base
has_many :comments
end
@@ -5732,7 +5732,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Active Record is now thread safe! (So you can use it with Cerise and WEBrick applications)
[Implementation idea by Michael Neumann, debugging assistance by Jamis Buck]
-* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute.
+* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute.
This brings the tax for using Active Record instead of "riding on the metal" (using MySQL-ruby C-driver directly) down to ~50%.
Done by doing lazy type conversions and caching column information on the class-level.
@@ -5749,46 +5749,46 @@ in effect. Added :readonly finder constraint. Calling an association collectio
end
Iterating over 100 posts like this:
-
+
<% for post in @posts %>
<%= post.title %> has <%= post.comments_count %> comments
<% end %>
-
+
Will generate 100 SQL count queries -- one for each call to post.comments_count. If you instead add a "comments_count" int column
to the posts table and rewrite the comments association macro with:
class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
end
-
+
Those 100 SQL count queries will be reduced to zero. Beware that counter caching is only appropriate for objects that begin life
with the object it's specified to belong with and is destroyed like that as well. Typically objects where you would also specify
:dependent => true. If your objects switch from one belonging to another (like a post that can be move from one category to another),
- you'll have to manage the counter yourself.
+ you'll have to manage the counter yourself.
* Added natural object-style assignment for has_one and belongs_to associations. Consider the following model:
class Project < ActiveRecord::Base
has_one :manager
end
-
+
class Manager < ActiveRecord::Base
belongs_to :project
end
-
+
Earlier, assignments would work like following regardless of which way the assignment told the best story:
-
+
active_record.manager_id = david.id
-
+
Now you can do it either from the belonging side:
david.project = active_record
-
+
...or from the having side:
-
+
active_record.manager = david
-
- If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the
+
+ If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the
project_id attribute on david would be set to the id of active_record, then david would be saved.
* Added natural object-style assignment for has_many associations [Florian Weber]. Consider the following model:
@@ -5796,36 +5796,36 @@ in effect. Added :readonly finder constraint. Calling an association collectio
class Project < ActiveRecord::Base
has_many :milestones
end
-
+
class Milestone < ActiveRecord::Base
belongs_to :project
end
-
+
Earlier, assignments would work like following regardless of which way the assignment told the best story:
-
+
deadline.project_id = active_record.id
-
+
Now you can do it either from the belonging side:
deadline.project = active_record
-
+
...or from the having side:
-
+
active_record.milestones << deadline
-
+
The milestone is automatically saved with the new foreign key.
* API CHANGE: Attributes for text (or blob or similar) columns will now have unknown classes stored using YAML instead of using
to_s. (Known classes that won't be yamelized are: String, NilClass, TrueClass, FalseClass, Fixnum, Date, and Time).
Likewise, data pulled out of text-based attributes will be attempted converged using Yaml if they have the "--- " header.
This was primarily done to be enable the storage of hashes and arrays without wrapping them in aggregations, so now you can do:
-
+
user = User.find(1)
user.preferences = { "background" => "black", "display" => large }
user.save
-
+
User.find(1).preferences # => { "background" => "black", "display" => large }
-
+
Please note that this method should only be used when you don't care about representing the object in proper columns in
the database. A money object consisting of an amount and a currency is still a much better fit for a value object done through
aggregations than this new option.
@@ -5833,14 +5833,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* POSSIBLE CODE BREAKAGE: As a consequence of the lazy type conversions, it's a bad idea to reference the @attributes hash
directly (it always was, but now it's paramount that you don't). If you do, you won't get the type conversion. So to implement
new accessors for existing attributes, use read_attribute(attr_name) and write_attribute(attr_name, value) instead. Like this:
-
+
class Song < ActiveRecord::Base
# Uses an integer of seconds to hold the length of the song
-
+
def length=(minutes)
write_attribute("length", minutes * 60)
end
-
+
def length
read_attribute("length") / 60
end
@@ -5876,7 +5876,7 @@ _Misc_
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
# parenteses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
# s for String, and a for Array.
-
+
This is incredibly useful for assigning dates from HTML drop-downs of month, year, and day.
* Fixed bug with custom primary key column name and Base.find on multiple parameters.
@@ -5898,17 +5898,17 @@ _Transactions_
_Mapping_
* Added support for non-integer primary keys [Aredridel/earlier work by Michael Neumann]
-
+
User.find "jdoe"
Product.find "PDKEY-INT-12"
* Added option to specify naming method for primary key column. ActiveRecord::Base.primary_key_prefix_type can either
be set to nil, :table_name, or :table_name_with_underscore. :table_name will assume that Product class has a primary key
of "productid" and :table_name_with_underscore will assume "product_id". The default nil will just give "id".
-
-* Added an overwriteable primary_key method that'll instruct AR to the name of the
+
+* Added an overwriteable primary_key method that'll instruct AR to the name of the
id column [Aredridele/earlier work by Guan Yang]
-
+
class Project < ActiveRecord::Base
def self.primary_key() "project_id" end
end
@@ -5929,8 +5929,8 @@ _Mapping_
_Misc_
* Added freeze call to value object assignments to ensure they remain immutable [Spotted by Gavin Sinclair]
-
-* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is
+
+* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is
observed_class() class method. This is more consistant with things like self.table_name(). Works like this:
class AuditObserver < ActiveRecord::Observer
@@ -5957,20 +5957,20 @@ _Misc_
* Added inheritable callback queues that can ensure that certain callback methods or inline fragments are
run throughout the entire inheritance hierarchy. Regardless of whether a descendant overwrites the callback
method:
-
+
class Topic < ActiveRecord::Base
before_destroy :destroy_author, 'puts "I'm an inline fragment"'
end
-
+
Learn more in link:classes/ActiveRecord/Callbacks.html
-* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when
+* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when
the holder is destroyed:
-
+
class Album < ActiveRecord::Base
has_many :tracks, :dependent => true
end
-
+
All the associated tracks are destroyed when the album is.
* Added Base.create as a factory that'll create, save, and return a new object in one step.
@@ -5980,7 +5980,7 @@ _Misc_
* Fixed the install.rb to include simple.rb [Spotted by Kevin Bullock]
-* Modified block syntax to better follow our code standards outlined in
+* Modified block syntax to better follow our code standards outlined in
http://www.rubyonrails.org/CodingStandards
@@ -5991,7 +5991,7 @@ _Misc_
* Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection,
which is parametized through a config hash with symbol keys instead of a regular parameter list.
This will allow for database connections to be opened in a more generic fashion. (Luke)
-
+
NOTE: This requires all *_connections to be updated! Read more in:
http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000081
@@ -6000,7 +6000,7 @@ _Misc_
* Fixed SQLite adapter so dates are returned as Date objects, not Time objects [Spotted by Gavin Sinclair]
-* Fixed requirement of date class, so date conversions are succesful regardless of whether you
+* Fixed requirement of date class, so date conversions are succesful regardless of whether you
manually require date or not.
@@ -6008,10 +6008,10 @@ _Misc_
* Added transactions
-* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7])
+* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7])
as parameter and then return an array of objects instead of just an object
-* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a
+* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a
collection, not an association
* Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 12e094b406..22a17a62af 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -49,7 +49,10 @@ end
connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}"
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
t.libs << "test" << connection_path
- t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
+ t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject {
+ |x| x =~ /\/adapters\//
+ } + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb")).sort
+
t.verbose = true
t.warning = true
}
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index f69576b240..f7d358337c 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -116,15 +116,15 @@ RBench.run(TIMES) do
end
report 'Model.all limit(100)', (TIMES / 10).ceil do
- ar { Exhibit.look Exhibit.all(:limit => 100) }
+ ar { Exhibit.look Exhibit.limit(100) }
end
report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do
- ar { Exhibit.feel Exhibit.all(:limit => 100, :include => :user) }
+ ar { Exhibit.feel Exhibit.limit(100).includes(:user) }
end
report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
- ar { Exhibit.look Exhibit.all(:limit => 10000) }
+ ar { Exhibit.look Exhibit.limit(10000) }
end
exhibit = {
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 51ffc7542c..c45400d3d9 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -190,7 +190,7 @@ module ActiveRecord
# :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
# :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
#
- def composed_of(part_id, options = {}, &block)
+ def composed_of(part_id, options = {})
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
name = part_id.id2name
@@ -199,9 +199,7 @@ module ActiveRecord
mapping = [ mapping ] unless mapping.first.is_a?(Array)
allow_nil = options[:allow_nil] || false
constructor = options[:constructor] || :new
- converter = options[:converter] || block
-
- ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given?
+ converter = options[:converter]
reader_method(name, class_name, mapping, allow_nil, constructor)
writer_method(name, class_name, mapping, allow_nil, converter)
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index f8d46bcb48..186b531ffb 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -26,10 +26,10 @@ module ActiveRecord
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
- def select(select = nil, &block)
+ def select(select = nil)
if block_given?
load_target
- @target.select(&block)
+ @target.select.each { |e| yield e }
else
scoped.select(select)
end
@@ -123,7 +123,7 @@ module ActiveRecord
end
end
- # Add +records+ to this association. Returns +self+ so method calls may be chained.
+ # Add +records+ to this association. Returns +self+ so method calls may be chained.
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def <<(*records)
result = true
@@ -168,7 +168,7 @@ module ActiveRecord
reset_target!
reset_named_scopes_cache!
end
-
+
# Calculate sum using SQL, not Enumerable
def sum(*args)
if block_given?
@@ -241,7 +241,7 @@ module ActiveRecord
if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
destroy_all
- else
+ else
delete_all
end
@@ -520,8 +520,8 @@ module ActiveRecord
def callbacks_for(callback_name)
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
@owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
- end
-
+ end
+
def ensure_owner_is_not_new
if @owner.new_record?
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index c378e19864..7517896235 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -147,12 +147,12 @@ module ActiveRecord
# add_autosave_association_callbacks(reflect_on_association(name))
# end
ASSOCIATION_TYPES.each do |type|
- module_eval %{
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
def #{type}(name, options = {})
super
add_autosave_association_callbacks(reflect_on_association(name))
end
- }
+ CODE
end
# Adds a validate and save callback for the association as specified by
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 3f81ca7555..e7b52287a5 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -20,6 +20,7 @@ require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/blank'
require 'arel'
require 'active_record/errors'
+require 'active_record/log_subscriber'
module ActiveRecord #:nodoc:
# = Active Record
@@ -916,8 +917,8 @@ module ActiveRecord #:nodoc:
def instantiate(record)
object = find_sti_class(record[inheritance_column]).allocate
- object.instance_variable_set(:'@attributes', record)
- object.instance_variable_set(:'@attributes_cache', {})
+ object.instance_variable_set(:@attributes, record)
+ object.instance_variable_set(:@attributes_cache, {})
object.instance_variable_set(:@new_record, false)
object.instance_variable_set(:@readonly, false)
object.instance_variable_set(:@destroyed, false)
@@ -1413,14 +1414,6 @@ module ActiveRecord #:nodoc:
# as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
# application specific and is therefore left to the application to implement according to its need.
def initialize_copy(other)
- # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
- # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
- # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
- # For example in the test suite the topic model's after_initialize method sets the author_email_address to
- # test@test.com. I would have thought this would mean that all cloned models would have an author email address
- # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
- # after_initialize callback has to be run *before* the copying of the attributes rather than afterwards in order
- # for all tests to pass. This makes no sense to me.
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
cloned_attributes.delete(self.class.primary_key)
@@ -1433,6 +1426,7 @@ module ActiveRecord #:nodoc:
end
clear_aggregation_cache
+ clear_association_cache
@attributes_cache = {}
@new_record = true
ensure_proper_type
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 979ed52f4a..c2d79a421d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -144,7 +144,9 @@ module ActiveRecord
@connections.each do |conn|
conn.disconnect! if conn.requires_reloading?
end
- @connections = []
+ @connections.delete_if do |conn|
+ conn.requires_reloading?
+ end
end
# Verify active connections and remove and disconnect connections
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 7d58bc2adf..7691b6a788 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -582,6 +582,11 @@ module ActiveRecord
@base.add_column(@table_name, column_name, type, options)
end
+ # Checks to see if a column exists. See SchemaStatements#column_exists?
+ def column_exists?(column_name, type = nil, options = nil)
+ @base.column_exists?(@table_name, column_name, type, options)
+ end
+
# Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols. See SchemaStatements#add_index
#
@@ -596,6 +601,11 @@ module ActiveRecord
@base.add_index(@table_name, column_name, options)
end
+ # Checks to see if an index exists. See SchemaStatements#index_exists?
+ def index_exists?(column_name, options = {})
+ @base.index_exists?(@table_name, column_name, options)
+ end
+
# Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
# ===== Example
# t.timestamps
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index d3499cea72..0216a8f4ac 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -24,10 +24,53 @@ module ActiveRecord
# Returns an array of indexes for the given table.
# def indexes(table_name, name = nil) end
+ # Checks to see if an index exists on a table for a given index definition
+ #
+ # === Examples
+ # # Check an index exists
+ # index_exists?(:suppliers, :company_id)
+ #
+ # # Check an index on multiple columns exists
+ # index_exists?(:suppliers, [:company_id, :company_type])
+ #
+ # # Check a unique index exists
+ # index_exists?(:suppliers, :company_id, :unique => true)
+ #
+ # # Check an index with a custom name exists
+ # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
+ def index_exists?(table_name, column_name, options = {})
+ column_names = Array.wrap(column_name)
+ index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
+ if options[:unique]
+ indexes(table_name).any?{ |i| i.unique && i.name == index_name }
+ else
+ indexes(table_name).any?{ |i| i.name == index_name }
+ end
+ end
+
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
def columns(table_name, name = nil) end
+ # Checks to see if a column exists in a given table.
+ #
+ # === Examples
+ # # Check a column exists
+ # column_exists?(:suppliers, :name)
+ #
+ # # Check a column exists of a particular type
+ # column_exists?(:suppliers, :name, :string)
+ #
+ # # Check a column exists with a specific definition
+ # column_exists?(:suppliers, :name, :string, :limit => 100)
+ def column_exists?(table_name, column_name, type = nil, options = {})
+ columns(table_name).any?{ |c| c.name == column_name.to_s &&
+ (!type || c.type == type) &&
+ (!options[:limit] || c.limit == options[:limit]) &&
+ (!options[:precision] || c.precision == options[:precision]) &&
+ (!options[:scale] || c.scale == options[:scale]) }
+ end
+
# Creates a new table with the name +table_name+. +table_name+ may either
# be a String or a Symbol.
#
@@ -205,6 +248,7 @@ module ActiveRecord
# remove_column(:suppliers, :qualification)
# remove_columns(:suppliers, :qualification, :experience)
def remove_column(table_name, *column_names)
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
end
@@ -292,7 +336,7 @@ module ActiveRecord
@logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
return
end
- if index_exists?(table_name, index_name, false)
+ if index_name_exists?(table_name, index_name, false)
@logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
return
end
@@ -313,7 +357,7 @@ module ActiveRecord
# remove_index :accounts, :name => :by_branch_party
def remove_index(table_name, options = {})
index_name = index_name(table_name, options)
- unless index_exists?(table_name, index_name, true)
+ unless index_name_exists?(table_name, index_name, true)
@logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
return
end
@@ -350,11 +394,11 @@ module ActiveRecord
end
end
- # Verify the existence of an index.
+ # Verify the existence of an index with a given name.
#
# The default argument is returned if the underlying implementation does not define the indexes method,
# as there's no way to determine the correct answer in that case.
- def index_exists?(table_name, index_name, default)
+ def index_name_exists?(table_name, index_name, default)
return default unless respond_to?(:indexes)
indexes(table_name).detect { |i| i.name == index_name }
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 4ee9fee4a9..be8d1bd76b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -107,7 +107,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
# Override to turn off referential integrity while executing <tt>&block</tt>.
- def disable_referential_integrity(&block)
+ def disable_referential_integrity
yield
end
@@ -142,9 +142,10 @@ module ActiveRecord
# this should be overridden by concrete adapters
end
- # Returns true if its safe to reload the connection between requests for development mode.
+ # Returns true if its required to reload the connection between requests for development mode.
+ # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
def requires_reloading?
- true
+ false
end
# Checks whether the connection to the database is still active (i.e. not stale).
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 7c7bc5e292..aa3626a37e 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -219,7 +219,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
- def disable_referential_integrity(&block) #:nodoc:
+ def disable_referential_integrity #:nodoc:
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
begin
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e84242601b..2fe2ae7136 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -216,7 +216,10 @@ module ActiveRecord
super(connection, logger)
@connection_parameters, @config = connection_parameters, config
+ # @local_tz is initialized as nil to avoid warnings when connect tries to use it
+ @local_tz = nil
connect
+ @local_tz = execute('SHOW TIME ZONE').first["TimeZone"]
end
# Is this connection alive and ready for queries?
@@ -372,7 +375,7 @@ module ActiveRecord
return false
end
- def disable_referential_integrity(&block) #:nodoc:
+ def disable_referential_integrity #:nodoc:
if supports_disable_referential_integrity?() then
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
end
@@ -606,27 +609,22 @@ module ActiveRecord
SQL
- indexes = []
-
- indexes = result.map do |row|
+ result.map do |row|
index_name = row[0]
unique = row[1] == 't'
indkey = row[2].split(" ")
oid = row[3]
- columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
- SELECT a.attname, a.attnum
+ columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
+ SELECT a.attnum, a.attname
FROM pg_attribute a
WHERE a.attrelid = #{oid}
AND a.attnum IN (#{indkey.join(",")})
SQL
- column_names = indkey.map {|attnum| columns[attnum] }
- IndexDefinition.new(table_name, index_name, unique, column_names)
-
- end
-
- indexes
+ column_names = columns.values_at(*indkey).compact
+ column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names)
+ end.compact
end
# Returns the list of all column definitions for a table.
@@ -929,9 +927,8 @@ module ActiveRecord
# TIMESTAMP WITH ZONE types in UTC.
if ActiveRecord::Base.default_timezone == :utc
execute("SET time zone 'UTC'")
- else
- offset = Time.local(2000).utc_offset / 3600
- execute("SET time zone '#{offset}'")
+ elsif @local_tz
+ execute("SET time zone '#{@local_tz}'")
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index f295af16f0..0d9a86a1ea 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -6,6 +6,10 @@ module ActiveRecord
def self.sqlite3_connection(config) # :nodoc:
parse_sqlite_config!(config)
+ unless 'sqlite3' == config[:adapter]
+ raise ArgumentError, 'adapter name should be "sqlite3"'
+ end
+
unless self.class.const_defined?(:SQLite3)
require_library_or_gem(config[:adapter])
end
@@ -24,13 +28,13 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLite3Adapter < SQLiteAdapter # :nodoc:
-
+
# Returns the current database encoding format as a string, eg: 'UTF-8'
def encoding
if @connection.respond_to?(:encoding)
- @connection.encoding[0]['encoding']
+ @connection.encoding.to_s
else
- encoding = @connection.send(:get_query_pragma, 'encoding')
+ encoding = @connection.execute('PRAGMA encoding')
encoding[0]['encoding']
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index deb62e3802..1927585c49 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -151,7 +151,7 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
def execute(sql, name = nil) #:nodoc:
- catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
+ log(sql, name) { @connection.execute(sql) }
end
def update_sql(sql, name = nil) #:nodoc:
@@ -176,15 +176,15 @@ module ActiveRecord
end
def begin_db_transaction #:nodoc:
- catch_schema_changes { @connection.transaction }
+ @connection.transaction
end
def commit_db_transaction #:nodoc:
- catch_schema_changes { @connection.commit }
+ @connection.commit
end
def rollback_db_transaction #:nodoc:
- catch_schema_changes { @connection.rollback }
+ @connection.rollback
end
# SCHEMA STATEMENTS ========================================
@@ -246,6 +246,7 @@ module ActiveRecord
end
def remove_column(table_name, *column_names) #:nodoc:
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
@@ -390,17 +391,6 @@ module ActiveRecord
end
end
- def catch_schema_changes
- return yield
- rescue ActiveRecord::StatementInvalid => exception
- if exception.message =~ /database schema has changed/
- reconnect!
- retry
- else
- raise
- end
- end
-
def sqlite_version
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
new file mode 100644
index 0000000000..71065f9908
--- /dev/null
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -0,0 +1,32 @@
+module ActiveRecord
+ class LogSubscriber < ActiveSupport::LogSubscriber
+ def initialize
+ super
+ @odd_or_even = false
+ end
+
+ def sql(event)
+ name = '%s (%.1fms)' % [event.payload[:name], event.duration]
+ sql = event.payload[:sql].squeeze(' ')
+
+ if odd?
+ name = color(name, :cyan, true)
+ sql = color(sql, nil, true)
+ else
+ name = color(name, :magenta, true)
+ end
+
+ debug " #{name} #{sql}"
+ end
+
+ def odd?
+ @odd_or_even = !@odd_or_even
+ end
+
+ def logger
+ ActiveRecord::Base.logger
+ end
+ end
+end
+
+ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 12a75f5d16..c0302136ea 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -296,7 +296,9 @@ module ActiveRecord
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
elsif attributes['id']
- raise_nested_attributes_record_not_found(association_name, attributes['id'])
+ existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+ self.send(association_name.to_s+'=', existing_record)
elsif !reject_new_record?(association_name, attributes)
method = "build_#{association_name}"
@@ -366,11 +368,16 @@ module ActiveRecord
unless reject_new_record?(association_name, attributes)
association.build(attributes.except(*UNASSIGNABLE_KEYS))
end
+
+ elsif existing_records.count == 0 #Existing record but not yet associated
+ existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
+ association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
- else
- raise_nested_attributes_record_not_found(association_name, attributes['id'])
+
end
end
end
@@ -390,7 +397,7 @@ module ActiveRecord
ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
end
- # Determines if a new record should be build by checking for
+ # Determines if a new record should be built by checking for
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
# association and evaluates to +true+.
def reject_new_record?(association_name, attributes)
@@ -406,9 +413,5 @@ module ActiveRecord
end
end
- def raise_nested_attributes_record_not_found(association_name, record_id)
- reflection = self.class.reflect_on_association(association_name)
- raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
- end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 36df878e1b..2808e199fe 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -26,9 +26,6 @@ module ActiveRecord
load "active_record/railties/databases.rake"
end
- require "active_record/railties/log_subscriber"
- log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new
-
initializer "active_record.initialize_timezone" do
ActiveSupport.on_load(:active_record) do
self.time_zone_aware_attributes = true
diff --git a/activerecord/lib/active_record/railties/log_subscriber.rb b/activerecord/lib/active_record/railties/log_subscriber.rb
deleted file mode 100644
index 31b98bb6ed..0000000000
--- a/activerecord/lib/active_record/railties/log_subscriber.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module ActiveRecord
- module Railties
- class LogSubscriber < Rails::LogSubscriber
- def initialize
- super
- @odd_or_even = false
- end
-
- def sql(event)
- name = '%s (%.1fms)' % [event.payload[:name], event.duration]
- sql = event.payload[:sql].squeeze(' ')
-
- if odd?
- name = color(name, :cyan, true)
- sql = color(sql, nil, true)
- else
- name = color(name, :magenta, true)
- end
-
- debug " #{name} #{sql}"
- end
-
- def odd?
- @odd_or_even = !@odd_or_even
- end
-
- def logger
- ActiveRecord::Base.logger
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 66970a5ea1..fd0660a138 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -10,7 +10,7 @@ module ActiveRecord
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
- delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a
+ delegate :to_xml, :to_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
delegate :insert, :to => :arel
attr_reader :table, :klass
@@ -328,6 +328,15 @@ module ActiveRecord
to_a.inspect
end
+ def extend(*args, &block)
+ if block_given?
+ apply_modules Module.new(&block)
+ self
+ else
+ super
+ end
+ end
+
protected
def method_missing(method, *args, &block)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 7a0c9dc612..f39951e16c 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -87,8 +87,8 @@ module ActiveRecord
# person.visits += 1
# person.save!
# end
- def find(*args, &block)
- return to_a.find(&block) if block_given?
+ def find(*args)
+ return to_a.find { |*block_args| yield(*block_args) } if block_given?
options = args.extract_options!
@@ -259,8 +259,8 @@ module ActiveRecord
record
end
- def find_with_ids(*ids, &block)
- return to_a.find(&block) if block_given?
+ def find_with_ids(*ids)
+ return to_a.find { |*block_args| yield(*block_args) } if block_given?
expects_array = ids.first.kind_of?(Array)
return ids.first if expects_array && ids.first.empty?
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index d0efa2189d..d853fd63d1 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -28,7 +28,7 @@ module ActiveRecord
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
values = value.to_a
attribute.in(values)
- when Range
+ when Range, Arel::Relation
attribute.in(value)
else
attribute.eq(value)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 50e94134f5..015ca8c24c 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -5,79 +5,98 @@ module ActiveRecord
module QueryMethods
extend ActiveSupport::Concern
- included do
- (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method|
- attr_accessor :"#{query_method}_values"
-
- next if [:where, :having, :select].include?(query_method)
- class_eval <<-CEVAL, __FILE__, __LINE__ + 1
- def #{query_method}(*args, &block)
- new_relation = clone
- new_relation.send(:apply_modules, Module.new(&block)) if block_given?
- value = Array.wrap(args.flatten).reject {|x| x.blank? }
- new_relation.#{query_method}_values += value if value.present?
- new_relation
- end
- CEVAL
- end
+ attr_accessor :includes_values, :eager_load_values, :preload_values,
+ :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values,
+ :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
- class_eval <<-CEVAL, __FILE__, __LINE__ + 1
- def select(*args, &block)
- if block_given?
- to_a.select(&block)
- else
- new_relation = clone
- value = Array.wrap(args.flatten).reject {|x| x.blank? }
- new_relation.select_values += value if value.present?
- new_relation
- end
- end
- CEVAL
-
- [:where, :having].each do |query_method|
- class_eval <<-CEVAL, __FILE__, __LINE__ + 1
- def #{query_method}(*args, &block)
- new_relation = clone
- new_relation.send(:apply_modules, Module.new(&block)) if block_given?
- value = build_where(*args)
- new_relation.#{query_method}_values += Array.wrap(value) if value.present?
- new_relation
- end
- CEVAL
- end
+ def includes(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.includes_values += args if args.present? }
+ end
- ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
- attr_accessor :"#{query_method}_value"
+ def eager_load(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.eager_load_values += args if args.present? }
+ end
- class_eval <<-CEVAL, __FILE__, __LINE__ + 1
- def #{query_method}(value = true, &block)
- new_relation = clone
- new_relation.send(:apply_modules, Module.new(&block)) if block_given?
- new_relation.#{query_method}_value = value
- new_relation
- end
- CEVAL
+ def preload(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.preload_values += args if args.present? }
+ end
+
+ def select(*args)
+ if block_given?
+ to_a.select { |*block_args| yield(*block_args) }
+ else
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.select_values += args if args.present? }
end
end
- def extending(*modules)
- new_relation = clone
- new_relation.send :apply_modules, *modules
- new_relation
+ def group(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.group_values += args if args.present? }
+ end
+
+ def order(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.order_values += args if args.present? }
+ end
+
+ def reorder(*args)
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.order_values = args if args.present? }
+ end
+
+ def joins(*args)
+ args.flatten!
+ args.reject! { |a| a.blank? }
+ clone.tap { |r| r.joins_values += args if args.present? }
+ end
+
+ def where(*args)
+ value = build_where(*args)
+ clone.tap { |r| r.where_values += Array.wrap(value) if value.present? }
end
- def lock(locks = true, &block)
- relation = clone
- relation.send(:apply_modules, Module.new(&block)) if block_given?
+ def having(*args)
+ value = build_where(*args)
+ clone.tap { |r| r.having_values += Array.wrap(value) if value.present? }
+ end
+
+ def limit(value = true)
+ clone.tap { |r| r.limit_value = value }
+ end
+ def offset(value = true)
+ clone.tap { |r| r.offset_value = value }
+ end
+
+ def lock(locks = true)
case locks
when String, TrueClass, NilClass
- clone.tap {|new_relation| new_relation.lock_value = locks || true }
+ clone.tap { |r| r.lock_value = locks || true }
else
- clone.tap {|new_relation| new_relation.lock_value = false }
+ clone.tap { |r| r.lock_value = false }
end
end
+ def readonly(value = true)
+ clone.tap { |r| r.readonly_value = value }
+ end
+
+ def create_with(value = true)
+ clone.tap { |r| r.create_with_value = value }
+ end
+
+ def from(value = true)
+ clone.tap { |r| r.from_value = value }
+ end
+
+ def extending(*modules)
+ clone.tap { |r| r.send :apply_modules, *modules }
+ end
+
def reverse_order
order_clause = arel.send(:order_clauses).join(', ')
relation = except(:order)
@@ -130,27 +149,18 @@ module ActiveRecord
end
end
- arel = arel.having(*@having_values.uniq.select{|h| h.present?})
+ arel = arel.having(*@having_values.uniq.select{|h| h.present?}) if @having_values.present?
arel = arel.take(@limit_value) if @limit_value.present?
arel = arel.skip(@offset_value) if @offset_value.present?
- arel = arel.group(*@group_values.uniq.select{|g| g.present?})
+ arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present?
- arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s))
+ arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present?
- selects = @select_values.uniq
+ arel = build_select(arel, @select_values.uniq)
- if selects.present?
- selects.each do |s|
- @implicit_readonly = false
- arel = arel.project(s) if s.present?
- end
- else
- arel = arel.project(@klass.quoted_table_name + '.*')
- end
-
- arel = @from_value.present? ? arel.from(@from_value) : arel.from(@klass.quoted_table_name)
+ arel = arel.from(@from_value) if @from_value.present?
case @lock_value
when TrueClass
@@ -221,6 +231,21 @@ module ActiveRecord
relation.join(custom_joins)
end
+ def build_select(arel, selects)
+ if selects.present?
+ @implicit_readonly = false
+ # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array.
+ # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
+ if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/)
+ arel.project(*selects)
+ else
+ arel.project(selects.last)
+ end
+ else
+ arel.project(@klass.quoted_table_name + '.*')
+ end
+ end
+
def apply_modules(modules)
values = Array.wrap(modules)
@extensions += values if values.present?
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index f712a2c94f..b88d550086 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -318,6 +318,14 @@ module ActiveRecord
sid
end
+ def destroy(env)
+ if sid = current_session_id(env)
+ Base.silence do
+ get_session_model(env, sid).destroy
+ end
+ end
+ end
+
def get_session_model(env, sid)
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
env[SESSION_RECORD_KEY] = find_session(sid)
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index e41635134c..0b0f5682aa 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -3,7 +3,7 @@ module ActiveRecord
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
- record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+ record.errors.add(attribute, :invalid, options.merge(:value => value))
end
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 6283bdd0d6..1c9ecc7b1b 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -32,7 +32,7 @@ module ActiveRecord
end
if relation.exists?
- record.errors.add(attribute, :taken, :default => options[:message], :value => value)
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
end
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index d6ab3257a0..8ac21c1410 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -1,15 +1,17 @@
class <%= migration_class_name %> < ActiveRecord::Migration
- def self.up<% attributes.each do |attribute| %>
- <%- if migration_action -%>
+ def self.up
+<% attributes.each do |attribute| -%>
+ <%- if migration_action -%>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %>
- <%- end -%>
<%- end -%>
+<%- end -%>
end
- def self.down<% attributes.reverse.each do |attribute| %>
- <%- if migration_action -%>
+ def self.down
+<% attributes.reverse.each do |attribute| -%>
+ <%- if migration_action -%>
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %>
- <%- end -%>
<%- end -%>
+<%- end -%>
end
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index 539c2517ee..960c29c49c 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -22,7 +22,7 @@ module ActiveRecord
def create_module_file
return if class_path.empty?
- template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb")
+ template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke
end
hook_for :test_framework
diff --git a/activerecord/test/cases/connection_test_firebird.rb b/activerecord/test/cases/adapters/firebird/connection_test.rb
index f57ea686a5..f57ea686a5 100644
--- a/activerecord/test/cases/connection_test_firebird.rb
+++ b/activerecord/test/cases/adapters/firebird/connection_test.rb
diff --git a/activerecord/test/cases/default_test_firebird.rb b/activerecord/test/cases/adapters/firebird/default_test.rb
index 713c7e11bf..713c7e11bf 100644
--- a/activerecord/test/cases/default_test_firebird.rb
+++ b/activerecord/test/cases/adapters/firebird/default_test.rb
diff --git a/activerecord/test/cases/migration_test_firebird.rb b/activerecord/test/cases/adapters/firebird/migration_test.rb
index 710661b9bd..710661b9bd 100644
--- a/activerecord/test/cases/migration_test_firebird.rb
+++ b/activerecord/test/cases/adapters/firebird/migration_test.rb
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index d7431e5158..6e6645511c 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -17,8 +17,8 @@ class ActiveSchemaTest < ActiveRecord::TestCase
end
def test_add_index
- # add_index calls index_exists? which can't work since execute is stubbed
- ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*|
+ # add_index calls index_name_exists? which can't work since execute is stubbed
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_name_exists?) do |*|
false
end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)"
@@ -35,7 +35,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))"
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
- ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?)
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_name_exists?)
end
def test_drop_table
diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 8e4842a1b6..8e4842a1b6 100644
--- a/activerecord/test/cases/connection_test_mysql.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
diff --git a/activerecord/test/cases/reserved_word_test_mysql.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index 90d8b0d923..90d8b0d923 100644
--- a/activerecord/test/cases/reserved_word_test_mysql.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
diff --git a/activerecord/test/cases/synonym_test_oracle.rb b/activerecord/test/cases/adapters/oracle/synonym_test.rb
index b9a422a6ca..b9a422a6ca 100644
--- a/activerecord/test/cases/synonym_test_oracle.rb
+++ b/activerecord/test/cases/adapters/oracle/synonym_test.rb
diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 4f04c6735c..f106e14319 100644
--- a/activerecord/test/cases/active_schema_test_postgresql.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -10,7 +10,10 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
end
def teardown
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:alias_method, :execute, :real_execute)
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
+ remove_method :execute
+ alias_method :execute, :real_execute
+ end
end
def test_create_database_with_encoding
diff --git a/activerecord/test/cases/datatype_test_postgresql.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 3c2d9fb7bd..5bb8fa2f93 100644
--- a/activerecord/test/cases/datatype_test_postgresql.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -53,7 +53,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
@connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)")
@first_oid = PostgresqlOid.find(1)
-
+
@connection.execute("INSERT INTO postgresql_timestamp_with_zones (time) VALUES ('2010-01-01 10:00:00-1')")
end
diff --git a/activerecord/test/cases/schema_authorization_test_postgresql.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 2860f1ad48..6f372edc38 100644
--- a/activerecord/test/cases/schema_authorization_test_postgresql.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -66,7 +66,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase
end
end
end
-
+
def test_tables_in_current_schemas
assert !@connection.tables.include?(TABLE_NAME)
USERS.each do |u|
diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 3ed9b1974d..a5c3e69af9 100644
--- a/activerecord/test/cases/schema_test_postgresql.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -9,9 +9,11 @@ class SchemaTest < ActiveRecord::TestCase
CAPITALIZED_TABLE_NAME = 'Things'
INDEX_A_NAME = 'a_index_things_on_name'
INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
+ INDEX_C_NAME = 'c_index_full_text_search'
INDEX_A_COLUMN = 'name'
INDEX_B_COLUMN_S1 = 'email'
INDEX_B_COLUMN_S2 = 'moment'
+ INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))}
COLUMNS = [
'id integer',
'name character varying(50)',
@@ -45,6 +47,8 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});"
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S1});"
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});"
+ @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
+ @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
end
def teardown
diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb
index 575b4806c1..575b4806c1 100644
--- a/activerecord/test/cases/copy_table_test_sqlite.rb
+++ b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb
diff --git a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb
new file mode 100644
index 0000000000..934cf72f72
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb
@@ -0,0 +1,56 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SQLite3AdapterTest < ActiveRecord::TestCase
+ def test_connection_no_db
+ assert_raises(ArgumentError) do
+ Base.sqlite3_connection {}
+ end
+ end
+
+ def test_connection_no_adapter
+ assert_raises(ArgumentError) do
+ Base.sqlite3_connection :database => ':memory:'
+ end
+ end
+
+ def test_connection_wrong_adapter
+ assert_raises(ArgumentError) do
+ Base.sqlite3_connection :database => ':memory:',:adapter => 'vuvuzela'
+ end
+ end
+
+ def test_bad_timeout
+ assert_raises(TypeError) do
+ Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 'usa'
+ end
+ end
+
+ # connection is OK with a nil timeout
+ def test_nil_timeout
+ conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => nil
+ assert conn, 'made a connection'
+ end
+
+ def test_connect
+ conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+ assert conn, 'should have connection'
+ end
+
+ # sqlite3 defaults to UTF-8 encoding
+ def test_encoding
+ conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+ assert_equal 'UTF-8', conn.encoding
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb
index e5fc1a2046..74588b4f47 100644
--- a/activerecord/test/cases/aggregations_test.rb
+++ b/activerecord/test/cases/aggregations_test.rb
@@ -121,34 +121,6 @@ class AggregationsTest < ActiveRecord::TestCase
end
end
-class DeprecatedAggregationsTest < ActiveRecord::TestCase
- class Person < ActiveRecord::Base; end
-
- def test_conversion_block_is_deprecated
- assert_deprecated 'conversion block has been deprecated' do
- Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
- end
- end
-
- def test_conversion_block_used_when_converter_option_is_nil
- assert_deprecated 'conversion block has been deprecated' do
- Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
- end
- assert_raise(NoMethodError) { Person.new.balance = 5 }
- end
-
- def test_converter_option_overrides_conversion_block
- assert_deprecated 'conversion block has been deprecated' do
- Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
- end
-
- person = Person.new
- assert_nothing_raised { person.balance = 5 }
- assert_equal 5, person.balance.amount
- assert_kind_of Money, person.balance
- end
-end
-
class OverridingAggregationsTest < ActiveRecord::TestCase
class Name; end
class DifferentName; end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 050b730dda..9c5dcc2ad9 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -41,9 +41,9 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
assert_nothing_raised do
- Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').all
+ Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').all
end
- assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').first
+ assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 7c74d87b61..f8b90d8877 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1401,6 +1401,14 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_equal clone.id, dev.id
end
+ def test_clone_does_not_clone_associations
+ author = authors(:david)
+ assert_not_equal [], author.posts
+
+ author_clone = author.clone
+ assert_equal [], author_clone.posts
+ end
+
def test_clone_preserves_subtype
clone = nil
assert_nothing_raised { clone = Company.find(3).clone }
@@ -2235,7 +2243,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_inspect_instance
topic = topics(:first)
- assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil>), topic.inspect
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil>), topic.inspect
end
def test_inspect_new_instance
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 329dd7d761..2c9d23c80f 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -272,7 +272,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_count_with_column_and_options_parameter
- assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50")
+ assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL")
end
def test_count_with_no_parameters_isnt_deprecated
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 1f544b4211..4aeae1fe45 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -1,20 +1,19 @@
require "cases/helper"
require "models/developer"
-require "rails/log_subscriber/test_helper"
-require "active_record/railties/log_subscriber"
+require "active_support/log_subscriber/test_helper"
class LogSubscriberTest < ActiveSupport::TestCase
- include Rails::LogSubscriber::TestHelper
+ include ActiveSupport::LogSubscriber::TestHelper
def setup
@old_logger = ActiveRecord::Base.logger
super
-
- Rails::LogSubscriber.add(:active_record, ActiveRecord::Railties::LogSubscriber.new)
+ ActiveRecord::LogSubscriber.attach_to(:active_record)
end
def teardown
super
+ ActiveRecord::LogSubscriber.log_subscribers.pop
ActiveRecord::Base.logger = @old_logger
end
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index e93f22bede..6cd42ff936 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -77,11 +77,13 @@ class MethodScopingTest < ActiveRecord::TestCase
end
end
- def test_options_select_replaces_scope_select
- Developer.send(:with_scope, :find => { :select => "id, name" }) do
+ def test_scope_select_concatenates
+ Developer.send(:with_scope, :find => { :select => "name" }) do
developer = Developer.find(:first, :select => 'id, salary', :conditions => "name = 'David'")
assert_equal 80000, developer.salary
- assert !developer.has_attribute?(:name)
+ assert developer.has_attribute?(:id)
+ assert developer.has_attribute?(:name)
+ assert developer.has_attribute?(:salary)
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6fe3b01281..99a3a12a8b 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -128,9 +128,9 @@ if ActiveRecord::Base.connection.supports_migrations?
good_index_name = 'x' * Person.connection.index_name_length
too_long_index_name = good_index_name + 'x'
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
- assert !Person.connection.index_exists?("people", too_long_index_name, false)
+ assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
- assert Person.connection.index_exists?("people", good_index_name, false)
+ assert Person.connection.index_name_exists?("people", good_index_name, false)
end
def test_remove_nonexistent_index
@@ -146,8 +146,8 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.add_index('people', [:first_name], :name => 'old_idx')
assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
# if the adapter doesn't support the indexes call, pick defaults that let the test pass
- assert !Person.connection.index_exists?('people', 'old_idx', false)
- assert Person.connection.index_exists?('people', 'new_idx', true)
+ assert !Person.connection.index_name_exists?('people', 'old_idx', false)
+ assert Person.connection.index_name_exists?('people', 'new_idx', true)
end
end
@@ -158,6 +158,53 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ def test_index_exists
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :string, :limit => 100
+ end
+ Person.connection.add_index :testings, :foo
+
+ assert Person.connection.index_exists?(:testings, :foo)
+ assert !Person.connection.index_exists?(:testings, :bar)
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ def test_index_exists_on_multiple_columns
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :string, :limit => 100
+ end
+ Person.connection.add_index :testings, [:foo, :bar]
+
+ assert Person.connection.index_exists?(:testings, [:foo, :bar])
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ def test_unique_index_exists
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ end
+ Person.connection.add_index :testings, :foo, :unique => true
+
+ assert Person.connection.index_exists?(:testings, :foo, :unique => true)
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ def test_named_index_exists
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ end
+ Person.connection.add_index :testings, :foo, :name => "custom_index_name"
+
+ assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
def testing_table_with_only_foo_attribute
Person.connection.create_table :testings, :id => false do |t|
t.column :foo, :string
@@ -748,6 +795,10 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.connection.drop_table(:hats)
end
+ def test_remove_column_no_second_parameter_raises_exception
+ assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
+ end
+
def test_change_type_of_not_null_column
assert_nothing_raised do
Topic.connection.change_column "topics", "written_on", :datetime, :null => false
@@ -970,6 +1021,45 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nil Person.new.first_name
end
+ def test_column_exists
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert Person.connection.column_exists?(:testings, :foo)
+ assert !Person.connection.column_exists?(:testings, :bar)
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ def test_column_exists_with_type
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert Person.connection.column_exists?(:testings, :foo, :string)
+ assert !Person.connection.column_exists?(:testings, :foo, :integer)
+ assert Person.connection.column_exists?(:testings, :bar, :decimal)
+ assert !Person.connection.column_exists?(:testings, :bar, :integer)
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ def test_column_exists_with_definition
+ Person.connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
+ assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
+ assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
+ assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
def test_add_table
assert !Reminder.table_exists?
@@ -1680,6 +1770,20 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ def test_index_exists
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {})
+ t.index_exists?(:bar)
+ end
+ end
+
+ def test_index_exists_with_options
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
+ t.index_exists?(:bar, :unique => true)
+ end
+ end
+
def test_change_changes_column
with_change_table do |t|
@connection.expects(:change_column).with(:delete_me, :bar, :string, {})
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 65d6080ea5..3c797076e0 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -176,12 +176,6 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
end
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
- @pirate.ship_attributes = { :id => 1234567890 }
- end
- end
-
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
@pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
@@ -331,11 +325,14 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
assert_equal 'Arr', @ship.pirate.catchphrase
end
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
- @ship.pirate_attributes = { :id => 1234567890 }
- end
- end
+ def test_should_associate_with_record_if_parent_record_is_not_saved
+ @ship.destroy
+ @pirate = Pirate.create(:catchphrase => 'Arr')
+ @ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase})
+
+ assert_equal @ship.name, 'Nights Dirty Lightning'
+ assert_equal @pirate, @ship.pirate
+ end
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
@ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
@@ -444,6 +441,11 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
end
+ def test_should_assign_existing_children_if_parent_is_new
+ @pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
+ end
+
def test_should_also_work_with_a_HashWithIndifferentAccess
@pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
@pirate.save
@@ -501,8 +503,8 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
end
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
+ def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
+ assert_nothing_raised ActiveRecord::RecordNotFound do
@pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
end
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 67818622d7..03c4fc4e80 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -24,25 +24,25 @@ class ReflectionTest < ActiveRecord::TestCase
def test_read_attribute_names
assert_equal(
- %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort,
+ %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type ).sort,
@first.attribute_names
)
end
def test_columns
- assert_equal 13, Topic.columns.length
+ assert_equal 14, Topic.columns.length
end
def test_columns_are_returned_in_the_order_they_were_declared
column_names = Topic.columns.map { |column| column.name }
- assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names
+ assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group), column_names
end
def test_content_columns
content_columns = Topic.content_columns
content_column_names = content_columns.map {|column| column.name}
- assert_equal 9, content_columns.length
- assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort
+ assert_equal 10, content_columns.length
+ assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title).sort, content_column_names.sort
end
def test_column_string_type_and_limit
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 43519db976..d27a69ea1c 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -16,12 +16,34 @@ class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
:taggings
+ def test_apply_relation_as_where_id
+ posts = Post.arel_table
+ post_authors = posts.where(posts[:author_id].eq(1)).project(posts[:id])
+ assert_equal 5, post_authors.to_a.size
+ assert_equal 5, Post.where(:id => post_authors).size
+ end
+
def test_scoped
topics = Topic.scoped
assert_kind_of ActiveRecord::Relation, topics
assert_equal 4, topics.size
end
+ def test_to_json
+ assert_nothing_raised { Bird.scoped.to_json }
+ assert_nothing_raised { Bird.scoped.all.to_json }
+ end
+
+ def test_to_yaml
+ assert_nothing_raised { Bird.scoped.to_yaml }
+ assert_nothing_raised { Bird.scoped.all.to_yaml }
+ end
+
+ def test_to_xml
+ assert_nothing_raised { Bird.scoped.to_xml }
+ assert_nothing_raised { Bird.scoped.all.to_xml }
+ end
+
def test_scoped_all
topics = Topic.scoped.all
assert_kind_of Array, topics
@@ -88,6 +110,18 @@ class RelationTest < ActiveRecord::TestCase
assert_equal topics(:first).title, topics.first.title
end
+ def test_finding_with_order_concatenated
+ topics = Topic.order('author_name').order('title')
+ assert_equal 4, topics.to_a.size
+ assert_equal topics(:fourth).title, topics.first.title
+ end
+
+ def test_finding_with_reorder
+ topics = Topic.order('author_name').order('title').reorder('id')
+ assert_equal 4, topics.to_a.size
+ assert_equal topics(:first).title, topics.first.title
+ end
+
def test_finding_with_order_and_take
entrants = Entrant.order("id ASC").limit(2).to_a
@@ -461,7 +495,7 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.scoped
assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq
- assert_equal 0, posts.where('id is not null').select('comments_count').count
+ assert_equal 7, posts.where('id is not null').select('comments_count').count
assert_equal 7, posts.select('comments_count').count('id')
assert_equal 0, posts.select('comments_count').count
@@ -469,6 +503,12 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 0, posts.count('comments_count')
end
+ def test_multiple_selects
+ post = Post.scoped.select('comments_count').select('title').order("id ASC").first
+ assert_equal "Welcome to the weblog", post.title
+ assert_equal 2, post.comments_count
+ end
+
def test_size
posts = Post.scoped
@@ -503,13 +543,13 @@ class RelationTest < ActiveRecord::TestCase
def test_many
posts = Post.scoped
-
+
assert_queries(2) do
assert posts.many? # Uses COUNT()
assert posts.many? {|p| p.id > 0 }
assert ! posts.many? {|p| p.id < 2 }
end
-
+
assert posts.loaded?
end
@@ -578,7 +618,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_anonymous_extension
- relation = Post.where(:author_id => 1).order('id ASC') do
+ relation = Post.where(:author_id => 1).order('id ASC').extend do
def author
'lifo'
end
@@ -593,4 +633,12 @@ class RelationTest < ActiveRecord::TestCase
assert_equal "lifo", relation.author
assert_equal "lifo", relation.limit(1).author
end
+
+ def test_order_by_relation_attribute
+ assert_equal Post.order(Post.arel_table[:title]).all, Post.order("title").all
+ end
+
+ def test_relations_limit_with_conditions_or_limit
+ assert_equal Post.limit(2).size, Post.limit(2).all.size
+ end
end
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index 8ee2a5868c..454e42ed37 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -9,22 +9,22 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
I18n.backend = I18n::Backend::Simple.new
end
- # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
+ # validates_associated: generate_message(attr_name, :invalid, :message => custom_message, :value => value)
def test_generate_message_invalid_with_default_message
- assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
+ assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :value => 'title')
end
def test_generate_message_invalid_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :message => 'custom message %{value}', :value => 'title')
end
- # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message])
+ # validates_uniqueness_of: generate_message(attr_name, :taken, :message => custom_message)
def test_generate_message_taken_with_default_message
- assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title')
+ assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :value => 'title')
end
def test_generate_message_taken_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :message => 'custom message %{value}', :value => 'title')
end
# ActiveRecord#RecordInvalid exception
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 38fa2b821d..15b97c02c8 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -31,34 +31,40 @@ class I18nValidationTest < ActiveRecord::TestCase
end
end
- # validates_uniqueness_of w/ mocha
+ # A set of common cases for ActiveModel::Validations message generation that
+ # are used to generate tests to keep things DRY
+ #
+ COMMON_CASES = [
+ # [ case, validation_options, generate_message_options]
+ [ "given no options", {}, {}],
+ [ "given custom message", {:message => "custom"}, {:message => "custom"}],
+ [ "given if condition", {:if => lambda { true }}, {}],
+ [ "given unless condition", {:unless => lambda { false }}, {}],
+ [ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }]
+ # TODO Add :on case, but below doesn't work, because then the validation isn't run for some reason
+ # even when using .save instead .valid?
+ # [ "given on condition", {:on => :save}, {}]
+ ]
- def test_validates_uniqueness_of_generates_message
- Topic.validates_uniqueness_of :title
- @topic.title = unique_topic.title
- @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'})
- @topic.valid?
- end
+ # validates_uniqueness_of w/ mocha
- def test_validates_uniqueness_of_generates_message_with_custom_default_message
- Topic.validates_uniqueness_of :title, :message => 'custom'
- @topic.title = unique_topic.title
- @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'})
- @topic.valid?
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_uniqueness_of on generated message #{name}" do
+ Topic.validates_uniqueness_of :title, validation_options
+ @topic.title = unique_topic.title
+ @topic.errors.expects(:generate_message).with(:title, :taken, generate_message_options.merge(:value => 'unique!'))
+ @topic.valid?
+ end
end
# validates_associated w/ mocha
- def test_validates_associated_generates_message
- Topic.validates_associated :replies
- replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
- replied_topic.valid?
- end
-
- def test_validates_associated_generates_message_with_custom_default_message
- Topic.validates_associated :replies
- replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
- replied_topic.valid?
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_associated on generated message #{name}" do
+ Topic.validates_associated :replies, validation_options
+ replied_topic.errors.expects(:generate_message).with(:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies))
+ replied_topic.save
+ end
end
# validates_associated w/o mocha
diff --git a/activerecord/test/fixtures/topics.yml b/activerecord/test/fixtures/topics.yml
index 1769152445..93f48aedc4 100644
--- a/activerecord/test/fixtures/topics.yml
+++ b/activerecord/test/fixtures/topics.yml
@@ -24,7 +24,7 @@ second:
third:
id: 3
title: The Third Topic of the day
- author_name: Nick
+ author_name: Carl
written_on: 2005-07-15t15:28:00.0099+01:00
content: I'm a troll
approved: true
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index f5fba2f87d..b212e7cff2 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -485,6 +485,7 @@ ActiveRecord::Schema.define do
t.integer :parent_id
t.string :parent_title
t.string :type
+ t.string :group
end
create_table :taggings, :force => true do |t|