From ccf9577aee86ce1f766c5e8854e0c285dc38f8ac Mon Sep 17 00:00:00 2001 From: Evgeniy Dolzhenko Date: Fri, 11 Jun 2010 14:15:34 +0400 Subject: Fix a bunch of minor spelling mistakes --- activerecord/lib/active_record/associations.rb | 10 +++++----- .../active_record/associations/through_association_scope.rb | 2 +- activerecord/lib/active_record/autosave_association.rb | 2 +- activerecord/lib/active_record/base.rb | 2 +- activerecord/lib/active_record/nested_attributes.rb | 2 +- activerecord/lib/active_record/reflection.rb | 8 ++++---- activerecord/lib/active_record/relation.rb | 2 +- activerecord/lib/active_record/schema_dumper.rb | 12 ++++++------ 8 files changed, 20 insertions(+), 20 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c1e16d08cb..c1d06ff68d 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -88,8 +88,8 @@ module ActiveRecord end end - # This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications - # (has_many, has_one) when there is at least 1 child assosociated instance. + # This error is raised when trying to destroy a parent instance in a N:1, 1:1 associations + # (has_many, has_one) when there is at least 1 child associated instance. # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project class DeleteRestrictionError < ActiveRecordError #:nodoc: def initialize(reflection) @@ -890,7 +890,7 @@ module ActiveRecord # [:inverse_of] # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_many # association. Does not work in combination with :through or :as options. - # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: # has_many :comments, :order => "posted_on" @@ -1005,7 +1005,7 @@ module ActiveRecord # [:inverse_of] # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_one # association. Does not work in combination with :through or :as options. - # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: # has_one :credit_card, :dependent => :destroy # destroys the associated credit card @@ -1110,7 +1110,7 @@ module ActiveRecord # [:inverse_of] # Specifies the name of the has_one or has_many association on the associated object that is the inverse of this belongs_to # association. Does not work in combination with the :polymorphic options. - # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: # belongs_to :firm, :foreign_key => "client_of" diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 1d2f323112..93bd6e3185 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -91,7 +91,7 @@ module ActiveRecord # Construct attributes for :through pointing to owner and associate. def construct_join_attributes(associate) - # TODO: revist this to allow it for deletion, supposing dependent option is supported + # TODO: revisit this to allow it for deletion, supposing dependent option is supported raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro) join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 0dcadfab5f..154d6ba8e5 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -375,7 +375,7 @@ module ActiveRecord if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) self[reflection.primary_key_name] = association_id - # TODO: Removing this code doesn't seem to matter… + # TODO: Removing this code doesn't seem to matter... if reflection.options[:polymorphic] self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7cff6d9f1a..277ab785da 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1456,7 +1456,7 @@ module ActiveRecord #:nodoc: # 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 atrributes rather than afterwards in order + # 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) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index eb9e792dd8..767ec85432 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -278,7 +278,7 @@ module ActiveRecord # Assigns the given attributes to the association. # # If update_only is false and the given attributes include an :id - # that matches the existing record’s id, then the existing record will be + # that matches the existing record's id, then the existing record will be # modified. If update_only is true, a new record is only created when no # object exists. Otherwise a new record will be built. # diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 0e48e229b3..d4af3d7d15 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -165,14 +165,14 @@ module ActiveRecord klass.new(*options) end - # Creates a new instance of the associated class, and immediates saves it + # Creates a new instance of the associated class, and immediately saves it # with ActiveRecord::Base#save. +options+ will be passed to the class's # creation method. Returns the newly created object. def create_association(*options) klass.create(*options) end - # Creates a new instance of the associated class, and immediates saves it + # Creates a new instance of the associated class, and immediately saves it # with ActiveRecord::Base#save!. +options+ will be passed to the class's # creation method. If the created record doesn't pass validations, then an # exception will be raised. @@ -267,10 +267,10 @@ module ActiveRecord # Returns whether or not the association should be validated as part of # the parent's validation. # - # Unless you explicitely disable validation with + # Unless you explicitly disable validation with # :validate => false, it will take place when: # - # * you explicitely enable validation; :validate => true + # * you explicitly enable validation; :validate => true # * you use autosave; :autosave => true # * the association is a +has_many+ association def validate? diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 99c914d7fc..5d99a15eda 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -67,7 +67,7 @@ module ActiveRecord preload += @includes_values unless eager_loading? preload.each {|associations| @klass.send(:preload_associations, @records, associations) } - # @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT. + # @readonly_value is true only if set explicitly. @implicit_readonly is true if there are JOINS and no explicit SELECT. readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value @records.each { |record| record.readonly! } if readonly diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index cd54653581..71ef249ee1 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -173,15 +173,15 @@ HEADER def indexes(table, stream) if (indexes = @connection.indexes(table)).any? add_index_statements = indexes.map do |index| - statment_parts = [ ('add_index ' + index.table.inspect) ] - statment_parts << index.columns.inspect - statment_parts << (':name => ' + index.name.inspect) - statment_parts << ':unique => true' if index.unique + statement_parts = [ ('add_index ' + index.table.inspect) ] + statement_parts << index.columns.inspect + statement_parts << (':name => ' + index.name.inspect) + statement_parts << ':unique => true' if index.unique index_lengths = index.lengths.compact if index.lengths.is_a?(Array) - statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present? + statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present? - ' ' + statment_parts.join(', ') + ' ' + statement_parts.join(', ') end stream.puts add_index_statements.sort.join("\n") -- cgit v1.2.3 From 51a9703b1e80caa30d699f30615f023079874623 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 15 Jun 2010 00:46:35 -0400 Subject: removing the extra \ --- activerecord/lib/active_record/associations/association_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index e88618d278..ee7dffab22 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -41,7 +41,7 @@ module ActiveRecord # though the object behind blog.posts is not an Array, but an # ActiveRecord::Associations::HasManyAssociation. # - # The @target object is not \loaded until needed. For example, + # The @target object is not loaded until needed. For example, # # blog.posts.count # -- cgit v1.2.3 From 3cc2d196c03fbc5cf21babf777dfa841f3d1012d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 15 Jun 2010 00:48:22 -0400 Subject: Revert "removing the extra \" This reverts commit 51a9703b1e80caa30d699f30615f023079874623. --- activerecord/lib/active_record/associations/association_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index ee7dffab22..e88618d278 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -41,7 +41,7 @@ module ActiveRecord # though the object behind blog.posts is not an Array, but an # ActiveRecord::Associations::HasManyAssociation. # - # The @target object is not loaded until needed. For example, + # The @target object is not \loaded until needed. For example, # # blog.posts.count # -- cgit v1.2.3 From 538b7b97bb64f56a9d8c62f280106f3916080f6d Mon Sep 17 00:00:00 2001 From: Bryan Woods Date: Tue, 15 Jun 2010 12:36:21 -0400 Subject: Active Record Associations - minor change of DeleteRestrictionError doc for clarity --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 680893a048..185e298f5c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -88,7 +88,7 @@ module ActiveRecord end end - # This error is raised when trying to destroy a parent instance in a N:1, 1:1 associations + # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations # (has_many, has_one) when there is at least 1 child associated instance. # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project class DeleteRestrictionError < ActiveRecordError #:nodoc: -- cgit v1.2.3 From 4ad6103c4f42c2dd2d2cad9b6f515737e9bf9de3 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Tue, 15 Jun 2010 22:41:41 +0430 Subject: Adds title and basic description where needed. --- activerecord/lib/active_record/aggregations.rb | 1 + activerecord/lib/active_record/attribute_methods.rb | 1 + activerecord/lib/active_record/autosave_association.rb | 2 ++ activerecord/lib/active_record/base.rb | 2 ++ activerecord/lib/active_record/callbacks.rb | 2 ++ activerecord/lib/active_record/counter_cache.rb | 1 + activerecord/lib/active_record/dynamic_finder_match.rb | 4 ++++ 7 files changed, 13 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 45aaea062d..51ffc7542c 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Aggregations module Aggregations # :nodoc: extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index c117271c71..56e18eced0 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/enumerable' module ActiveRecord + # = Active Record Attribute Methods module AttributeMethods #:nodoc: extend ActiveSupport::Concern include ActiveModel::AttributeMethods diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 154d6ba8e5..c378e19864 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/array/wrap' module ActiveRecord + # = Active Record Autosave Association + # # AutosaveAssociation is a module that takes care of automatically saving # your associations when the parent is saved. In addition to saving, it # also destroys any associations that were marked for destruction. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ce7259e96b..93249fc96c 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -21,6 +21,8 @@ require 'arel' require 'active_record/errors' module ActiveRecord #:nodoc: + # = Active Record + # # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 44fee12001..7b7de0b070 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/array/wrap' module ActiveRecord + # = Active Record Callbacks + # # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic # before or after an alteration of the object state. This can be used to make sure that associated and # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 9601ed6afd..999322129a 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Counter Cache module CounterCache # Resets one or more counter caches to their correct value using an SQL # count query. This is useful when adding new counter caches, or if the diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index fa7a19487c..0b66bea391 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -1,4 +1,8 @@ module ActiveRecord + # = Active Record Dynamic Finder Match + # + # Provides dynamic attribute-based finders such as find_by_country if, for + # example, the Person has an attribute with that name. class DynamicFinderMatch def self.match(method) df_match = self.new(method) -- cgit v1.2.3 From c7f78b184df5cdeee301af06472f7f14465ed7e4 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Tue, 15 Jun 2010 23:02:51 +0430 Subject: Added description and title to Dynamic Scope Match --- activerecord/lib/active_record/dynamic_scope_match.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/dynamic_scope_match.rb b/activerecord/lib/active_record/dynamic_scope_match.rb index f796ba669a..15f65be6bc 100644 --- a/activerecord/lib/active_record/dynamic_scope_match.rb +++ b/activerecord/lib/active_record/dynamic_scope_match.rb @@ -1,4 +1,11 @@ module ActiveRecord + + # = Active Record Dynamic Scope Match + # + # Provides dynamic attribute-based scopes such as scoped_by_price(4.99) + # if, for example, the Product has an attribute with that name. You can + # chain more scoped_by_* methods after the other. It acts like a named + # scope except that it's dynamic. class DynamicScopeMatch def self.match(method) ds_match = self.new(method) -- cgit v1.2.3 From cef442f92c409db6aec8ec313f46eeaf93d9b733 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Tue, 15 Jun 2010 23:03:54 +0430 Subject: Added typewriter tag to code. --- activerecord/lib/active_record/dynamic_finder_match.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index 0b66bea391..b39b291352 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -1,8 +1,9 @@ module ActiveRecord + # = Active Record Dynamic Finder Match # - # Provides dynamic attribute-based finders such as find_by_country if, for - # example, the Person has an attribute with that name. + # Provides dynamic attribute-based finders such as find_by_country + # if, for example, the Person has an attribute with that name. class DynamicFinderMatch def self.match(method) df_match = self.new(method) -- cgit v1.2.3 From c86915450af4ee800ca3eba474fe6d01c865c4c3 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Tue, 15 Jun 2010 23:11:30 +0430 Subject: Adds title. --- activerecord/lib/active_record/errors.rb | 3 + activerecord/lib/active_record/migration.rb | 98 ++++++++++++++++++----------- 2 files changed, 65 insertions(+), 36 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index cf5ddca2ba..7aa725d095 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -1,4 +1,7 @@ module ActiveRecord + + # = Active Record Errors + # # Generic Active Record exception class. class ActiveRecordError < StandardError end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 940f825038..4a1d6ac758 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -29,11 +29,15 @@ module ActiveRecord end end - # Migrations can manage the evolution of a schema used by several physical databases. It's a solution - # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to - # push that change to other developers and to the production server. With migrations, you can describe the transformations - # in self-contained classes that can be checked into version control systems and executed against another database that - # might be one, two, or five versions behind. + # Active Record Migrations + # + # Migrations can manage the evolution of a schema used by several physical + # databases. It's a solution to the common problem of adding a field to make + # a new feature work in your local database, but being unsure of how to + # push that change to other developers and to the production server. With + # migrations, you can describe the transformations in self-contained classes + # that can be checked into version control systems and executed against + # another database that might be one, two, or five versions behind. # # Example of a simple migration: # @@ -47,10 +51,13 @@ module ActiveRecord # end # end # - # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration. - # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement - # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column, - # but may also contain regular Ruby code for generating data needed for the transformations. + # This migration will add a boolean flag to the accounts table and remove it + # if you're backing out of the migration. It shows how all migrations have + # two class methods +up+ and +down+ that describes the transformations + # required to implement or remove the migration. These methods can consist + # of both the migration specific methods like add_column and remove_column, + # but may also contain regular Ruby code for generating data needed for the + # transformations. # # Example of a more complex migration that also needs to initialize data: # @@ -64,7 +71,9 @@ module ActiveRecord # t.integer :position # end # - # SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 + # SystemSetting.create :name => "notice", + # :label => "Use notice?", + # :value => 1 # end # # def self.down @@ -72,35 +81,49 @@ module ActiveRecord # end # end # - # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model - # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema - # in one block call. + # This migration first adds the system_settings table, then creates the very + # first row in it using the Active Record model that relies on the table. It + # also uses the more advanced create_table syntax where you can specify a + # complete table schema in one block call. # # == Available transformations # - # * create_table(name, options) Creates a table called +name+ and makes the table object available to a block - # that can then add columns to it, following the same format as add_column. See example above. The options hash is for - # fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition. + # * create_table(name, options) Creates a table called +name+ and + # makes the table object available to a block that can then add columns to it, + # following the same format as add_column. See example above. The options hash + # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create + # table definition. # * drop_table(name): Drops the table called +name+. - # * rename_table(old_name, new_name): Renames the table called +old_name+ to +new_name+. - # * add_column(table_name, column_name, type, options): Adds a new column to the table called +table_name+ + # * rename_table(old_name, new_name): Renames the table called +old_name+ + # to +new_name+. + # * add_column(table_name, column_name, type, options): Adds a new column + # to the table called +table_name+ # named +column_name+ specified to be one of the following types: - # :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, - # :date, :binary, :boolean. A default value can be specified by passing an - # +options+ hash like { :default => 11 }. Other options include :limit and :null (e.g. { :limit => 50, :null => false }) - # -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details. - # * rename_column(table_name, column_name, new_column_name): Renames a column but keeps the type and content. - # * change_column(table_name, column_name, type, options): Changes the column to a different type using the same - # parameters as add_column. - # * remove_column(table_name, column_name): Removes the column named +column_name+ from the table called +table_name+. - # * add_index(table_name, column_names, options): Adds a new index with the name of the column. Other options include - # :name and :unique (e.g. { :name => "users_name_index", :unique => true }). - # * remove_index(table_name, index_name): Removes the index specified by +index_name+. + # :string, :text, :integer, :float, + # :decimal, :datetime, :timestamp, :time, + # :date, :binary, :boolean. A default value can be + # specified by passing an +options+ hash like { :default => 11 }. + # Other options include :limit and :null (e.g. + # { :limit => 50, :null => false }) -- see + # ActiveRecord::ConnectionAdapters::TableDefinition#column for details. + # * rename_column(table_name, column_name, new_column_name): Renames + # a column but keeps the type and content. + # * change_column(table_name, column_name, type, options): Changes + # the column to a different type using the same parameters as add_column. + # * remove_column(table_name, column_name): Removes the column named + # +column_name+ from the table called +table_name+. + # * add_index(table_name, column_names, options): Adds a new index + # with the name of the column. Other options include + # :name and :unique (e.g. + # { :name => "users_name_index", :unique => true }). + # * remove_index(table_name, index_name): Removes the index specified + # by +index_name+. # # == Irreversible transformations # - # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise - # an ActiveRecord::IrreversibleMigration exception in their +down+ method. + # Some transformations are destructive in a manner that cannot be reversed. + # Migrations of that kind should raise an ActiveRecord::IrreversibleMigration + # exception in their +down+ method. # # == Running migrations from within Rails # @@ -110,13 +133,15 @@ module ActiveRecord # rails generate migration MyNewMigration # # where MyNewMigration is the name of your migration. The generator will - # create an empty migration file timestamp_my_new_migration.rb in the db/migrate/ - # directory where timestamp is the UTC formatted date and time that the migration was generated. + # create an empty migration file timestamp_my_new_migration.rb + # in the db/migrate/ directory where timestamp is the + # UTC formatted date and time that the migration was generated. # # You may then edit the self.up and self.down methods of # MyNewMigration. # # There is a special syntactic shortcut to generate migrations that add fields to a table. + # # rails generate migration add_fieldname_to_tablename fieldname:string # # This will generate the file timestamp_add_fieldname_to_tablename, which will look like this: @@ -191,9 +216,10 @@ module ActiveRecord # # == Using a model after changing its table # - # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need - # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from - # after the new column was added. Example: + # Sometimes you'll want to add a column in a migration and populate it + # immediately after. In that case, you'll need to make a call to + # Base#reset_column_information in order to ensure that the model has the + # latest column data from after the new column was added. Example: # # class AddPeopleSalary < ActiveRecord::Migration # def self.up -- cgit v1.2.3 From d8277804b2ad95275d21492f4d925d1148425253 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 22:08:14 +0430 Subject: Adds title and minor changes. --- activerecord/lib/active_record/migration.rb | 2 +- activerecord/lib/active_record/named_scope.rb | 11 +++++--- .../lib/active_record/nested_attributes.rb | 3 ++- activerecord/lib/active_record/observer.rb | 2 ++ activerecord/lib/active_record/persistence.rb | 29 ++++++++++++++-------- 5 files changed, 30 insertions(+), 17 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4a1d6ac758..b273c33e50 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -29,7 +29,7 @@ module ActiveRecord end end - # Active Record Migrations + # = Active Record Migrations # # Migrations can manage the evolution of a schema used by several physical # databases. It's a solution to the common problem of adding a field to make diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3d8f4a030b..096b9da402 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -4,11 +4,12 @@ require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/blank' module ActiveRecord + # = Active Record Named Scopes module NamedScope extend ActiveSupport::Concern module ClassMethods - # Returns an anonymous scope. + # Returns an anonymous \scope. # # posts = Post.scoped # posts.size # Fires "select count(*) from posts" and returns the count @@ -18,10 +19,12 @@ module ActiveRecord # fruits = fruits.where(:colour => 'red') if options[:red_only] # fruits = fruits.limit(10) if limited? # - # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing - # intermediate values (scopes) around as first-class objects is convenient. + # Anonymous scopes tend to be useful when procedurally generating complex + # queries, where passing intermediate values (scopes) around as first-class + # objects is convenient. # - # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. + # You can define a scope that applies to all finders using + # ActiveRecord::Base.default_scope. def scoped(options = {}, &block) if options.present? relation = scoped.apply_finder_options(options) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 767ec85432..99f8b431c8 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -15,7 +15,7 @@ module ActiveRecord self.nested_attributes_options = {} end - # == Nested Attributes + # = Active Record Nested Attributes # # Nested attributes allow you to save attributes on associated records # through the parent. By default nested attribute updating is turned off, @@ -25,6 +25,7 @@ module ActiveRecord # # The attribute writer is named after the association, which means that # in the following example, two new methods are added to your model: + # # author_attributes=(attributes) and # pages_attributes=(attributes). # diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 0ea7fe7365..9554dd8826 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/class/attribute' module ActiveRecord + # = Active Record Observer + # # Observer classes respond to lifecycle callbacks to implement trigger-like # behavior outside the original class. This is a great way to reduce the # clutter that normally comes when the model class is burdened with diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 10788630a5..9e28aa2a05 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -1,6 +1,8 @@ module ActiveRecord + # = Active Record Persistence module Persistence - # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false. + # Returns true if this object hasn't been saved yet -- that is, a record + # for the object doesn't exist in the data store yet; otherwise, returns false. def new_record? @new_record end @@ -10,7 +12,8 @@ module ActiveRecord @destroyed end - # Returns if the record is persisted, i.e. it's not a new record and it was not destroyed. + # Returns if the record is persisted, i.e. it's not a new record and it was + # not destroyed. def persisted? !(new_record? || destroyed?) end @@ -69,8 +72,8 @@ module ActiveRecord freeze end - # Deletes the record in the database and freezes this instance to reflect that no changes should - # be made (since they can't be persisted). + # Deletes the record in the database and freezes this instance to reflect + # that no changes should be made (since they can't be persisted). def destroy if persisted? self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all @@ -80,10 +83,13 @@ module ActiveRecord freeze end - # Returns an instance of the specified +klass+ with the attributes of the current record. This is mostly useful in relation to - # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record - # identification in Action Pack to allow, say, Client < Company to do something like render :partial => @client.becomes(Company) - # to render that instance using the companies/company partial instead of clients/client. + # Returns an instance of the specified +klass+ with the attributes of the + # current record. This is mostly useful in relation to single-table + # inheritance structures where you want a subclass to appear as the + # superclass. This can be used along with record identification in + # Action Pack to allow, say, Client < Company to do something + # like render :partial => @client.becomes(Company) to render that + # instance using the companies/company partial instead of clients/client. # # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either # instance will affect the other. @@ -104,14 +110,15 @@ module ActiveRecord save(:validate => false) end - # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will - # fail and false will be returned. + # Updates all the attributes from the passed-in Hash and saves the record. + # If the object is invalid, the saving will fail and false will be returned. def update_attributes(attributes) self.attributes = attributes save end - # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid. + # Updates an object just like Base.update_attributes but calls save! instead + # of save so an exception is raised if the record is invalid. def update_attributes!(attributes) self.attributes = attributes save! -- cgit v1.2.3 From 2b7f08428c5e2a866f65ab0ba2838da5857c471b Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 22:15:04 +0430 Subject: Adds basic description and title. --- activerecord/lib/active_record/query_cache.rb | 1 + activerecord/lib/active_record/railtie.rb | 1 + activerecord/lib/active_record/reflection.rb | 45 ++++++++++++++++++--------- activerecord/lib/active_record/relation.rb | 8 +++-- 4 files changed, 38 insertions(+), 17 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index 093c6c1e55..d9f85a4e5e 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/object/blank' module ActiveRecord + # = Active Record Query Cache class QueryCache module ClassMethods # Enable the query cache within the block if Active Record is configured. diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index a32fb7d399..37f1ec11a6 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -9,6 +9,7 @@ require "active_model/railtie" require "action_controller/railtie" module ActiveRecord + # = Active Record Railtie class Railtie < Rails::Railtie config.active_record = ActiveSupport::OrderedOptions.new diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index d4af3d7d15..a82e5d7ed1 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,12 +1,16 @@ module ActiveRecord + # = Active Record Reflection module Reflection # :nodoc: extend ActiveSupport::Concern - # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations. - # This information can, for example, be used in a form builder that took an Active Record object and created input - # fields for all of the attributes depending on their type and displayed the associations to other objects. + # Reflection allows you to interrogate Active Record classes and objects + # about their associations and aggregations. This information can, + # for example, be used in a form builder that took an Active Record object + # and created input fields for all of the attributes depending on their type + # and displayed the associations to other objects. # - # You can find the interface for the AggregateReflection and AssociationReflection classes in the abstract MacroReflection class. + # You can find the interface for the AggregateReflection and AssociationReflection + # classes in the abstract MacroReflection class. module ClassMethods def create_reflection(macro, name, options, active_record) case macro @@ -43,8 +47,11 @@ module ActiveRecord reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil end - # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a - # certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter. + # Returns an array of AssociationReflection objects for all the + # associations in the class. If you only want to reflect on a certain + # association type, pass in the symbol (:has_many, :has_one, + # :belongs_to) for that as the first parameter. + # # Example: # # Account.reflect_on_all_associations # returns an array of all associations @@ -71,8 +78,9 @@ module ActiveRecord end - # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of - # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. + # Abstract base class for AggregateReflection and AssociationReflection that + # describes the interface available for both of those classes. Objects of + # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. class MacroReflection attr_reader :active_record @@ -80,13 +88,15 @@ module ActiveRecord @macro, @name, @options, @active_record = macro, name, options, active_record end - # Returns the name of the macro. For example, composed_of :balance, :class_name => 'Money' will return - # :balance or for has_many :clients it will return :clients. + # Returns the name of the macro. For example, composed_of :balance, + # :class_name => 'Money' will return :balance or for + # has_many :clients it will return :clients. def name @name end - # Returns the macro type. For example, composed_of :balance, :class_name => 'Money' will return :composed_of + # Returns the macro type. For example, + # composed_of :balance, :class_name => 'Money' will return :composed_of # or for has_many :clients will return :has_many. def macro @macro @@ -132,11 +142,13 @@ module ActiveRecord end - # Holds all the meta-data about an aggregation as it was specified in the Active Record class. + # Holds all the meta-data about an aggregation as it was specified in the + # Active Record class. class AggregateReflection < MacroReflection #:nodoc: end - # Holds all the meta-data about an association as it was specified in the Active Record class. + # Holds all the meta-data about an association as it was specified in the + # Active Record class. class AssociationReflection < MacroReflection #:nodoc: # Returns the target association's class: # @@ -306,9 +318,12 @@ module ActiveRecord end end - # Holds all the meta-data about a :through association as it was specified in the Active Record class. + # Holds all the meta-data about a :through association as it was specified + # in the Active Record class. class ThroughReflection < AssociationReflection #:nodoc: - # Gets the source of the through reflection. It checks both a singularized and pluralized form for :belongs_to or :has_many. + # Gets the source of the through reflection. It checks both a singularized + # and pluralized form for :belongs_to or :has_many. + # # (The :tags association on Tagging below.) # # class Post < ActiveRecord::Base diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 5d99a15eda..66970a5ea1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/object/blank' module ActiveRecord + # = Active Record Relation class Relation JoinOperation = Struct.new(:relation, :join_class, :on) ASSOCIATION_METHODS = [:includes, :eager_load, :preload] @@ -75,10 +76,12 @@ module ActiveRecord @records end + # Returns size of the records. def size loaded? ? @records.length : count end + # Returns true if there are no records. def empty? loaded? ? @records.empty? : count.zero? end @@ -240,8 +243,9 @@ module ActiveRecord # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')") # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else']) # - # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent - # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. + # Both calls delete the affected posts all at once with a single DELETE statement. + # If you need to destroy dependent associations or call your before_* or + # +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) conditions ? where(conditions).delete_all : arel.delete.tap { reset } end -- cgit v1.2.3 From 0ebdc26caeb755df388ad31ea004960a64ba14c3 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 22:17:05 +0430 Subject: Adds title where needed. --- activerecord/lib/active_record/schema.rb | 2 ++ activerecord/lib/active_record/schema_dumper.rb | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index a833356d15..e2783087ec 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/object/blank' module ActiveRecord + # = Active Record Schema + # # Allows programmers to programmatically define a schema in a portable # DSL. This means you can define tables, indexes, etc. without using SQL # directly, so your applications can more easily support multiple diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 71ef249ee1..a4757773d8 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -2,6 +2,8 @@ require 'stringio' require 'active_support/core_ext/big_decimal' module ActiveRecord + # = Active Record Schema Dumper + # # This class is used to dump the database schema for some connection to some # output format (i.e., ActiveRecord::Schema). class SchemaDumper #:nodoc: @@ -39,13 +41,14 @@ module ActiveRecord define_params = @version ? ":version => #{@version}" : "" stream.puts <
Date: Wed, 16 Jun 2010 22:22:35 +0430 Subject: Adds title and proper formatting to docs. --- activerecord/lib/active_record/serialization.rb | 2 ++ activerecord/lib/active_record/session_store.rb | 8 ++++++++ activerecord/lib/active_record/test_case.rb | 3 +++ activerecord/lib/active_record/timestamp.rb | 18 ++++++++++++------ 4 files changed, 25 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index b49471f7ab..2d8bd184f3 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -1,4 +1,5 @@ module ActiveRecord #:nodoc: + # = Active Record Serialization module Serialization extend ActiveSupport::Concern include ActiveModel::Serializers::JSON @@ -22,6 +23,7 @@ module ActiveRecord #:nodoc: private # Add associations specified via the :includes option. + # # Expects a block that takes as arguments: # +association+ - name of the association # +records+ - the association record(s) to be serialized diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 931872eded..f712a2c94f 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -1,4 +1,6 @@ module ActiveRecord + # = Active Record Session Store + # # A session store backed by an Active Record class. A default class is # provided, but any object duck-typing to an Active Record Session class # with text +session_id+ and +data+ attributes is sufficient. @@ -7,6 +9,7 @@ module ActiveRecord # +id+ (numeric primary key), # +session_id+ (text, or longtext if your session data exceeds 65K), and # +data+ (text or longtext; careful if your session data exceeds 65KB). + # # The +session_id+ column should always be indexed for speedy lookups. # Session data is marshaled to the +data+ column in Base64 format. # If the data you write is larger than the column's size limit, @@ -14,9 +17,11 @@ module ActiveRecord # # You may configure the table name, primary key, and data column. # For example, at the end of config/environment.rb: + # # ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table' # ActiveRecord::SessionStore::Session.primary_key = 'session_id' # ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data' + # # Note that setting the primary key to the +session_id+ frees you from # having a separate +id+ column if you don't want it. However, you must # set session.model.id = session.session_id by hand! A before filter @@ -29,8 +34,11 @@ module ActiveRecord # You may provide your own session class implementation, whether a # feature-packed Active Record or a bare-metal high-performance SQL # store, by setting + # # ActiveRecord::SessionStore.session_class = MySessionClass + # # You must implement these methods: + # # self.find_by_session_id(session_id) # initialize(hash_of_session_id_and_data) # attr_reader :session_id diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index 0a77ad5fd7..e61a378d17 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -1,4 +1,7 @@ module ActiveRecord + # = Active Record Test Case + # + # Defines some test assertions to test against SQL queries. class TestCase < ActiveSupport::TestCase #:nodoc: def assert_date_from_db(expected, actual, message = nil) # SybaseAdapter doesn't have a separate column type just for dates, diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 9fba8f0aca..ffd12d2082 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,11 +1,16 @@ module ActiveRecord - # Active Record automatically timestamps create and update operations if the table has fields - # named created_at/created_on or updated_at/updated_on. + # = Active Record Timestamp + # + # Active Record automatically timestamps create and update operations if the + # table has fields named created_at/created_on or + # updated_at/updated_on. + # + # Timestamping can be turned off by setting: # - # Timestamping can be turned off by setting # ActiveRecord::Base.record_timestamps = false # - # Timestamps are in the local timezone by default but you can use UTC by setting + # Timestamps are in the local timezone by default but you can use UTC by setting: + # # ActiveRecord::Base.default_timezone = :utc module Timestamp extend ActiveSupport::Concern @@ -16,8 +21,9 @@ module ActiveRecord end # Saves the record with the updated_at/on attributes set to the current time. - # If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised. - # If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes. + # If the save fails because of validation errors, an + # ActiveRecord::RecordInvalid exception is raised. If an attribute name is passed, + # that attribute is used for the touch instead of the updated_at/on attributes. # # Examples: # -- cgit v1.2.3 From 6445441253711d9131fa859115dd8224ea31eeee Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 22:25:15 +0430 Subject: Adds title to the rest of the files in activerecord/lib --- activerecord/lib/active_record/transactions.rb | 6 ++++-- activerecord/lib/active_record/validations.rb | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 620758f5af..b4b146994d 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -11,7 +11,8 @@ module ActiveRecord included do define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name] end - + # = Active Record Transactions + # # Transactions are protective blocks where SQL statements are only permanent # if they can all succeed as one atomic action. The classic example is a # transfer between two accounts where you can only have a deposit if the @@ -19,7 +20,8 @@ module ActiveRecord # the database and guard the data against program errors or database # break-downs. So basically you should use transaction blocks whenever you # have a number of statements that must be executed together or not at all. - # Example: + # + # For example: # # ActiveRecord::Base.transaction do # david.withdrawal(100) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index be64e00bd1..6b511e83db 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,6 +1,9 @@ module ActiveRecord + # = Active Record Validations + # # Raised by save! and create! when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. + # # begin # complex_operation_that_calls_save!_internally # rescue ActiveRecord::RecordInvalid => invalid -- cgit v1.2.3 From fde95048475c1ffc89978650411b2e9034ff6a35 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 22:28:20 +0430 Subject: Adds title to activerecord/lib/active_record/associations/* --- activerecord/lib/active_record/associations/association_collection.rb | 2 ++ activerecord/lib/active_record/associations/association_proxy.rb | 2 ++ activerecord/lib/active_record/associations/belongs_to_association.rb | 1 + .../active_record/associations/belongs_to_polymorphic_association.rb | 1 + .../active_record/associations/has_and_belongs_to_many_association.rb | 1 + activerecord/lib/active_record/associations/has_many_association.rb | 1 + .../lib/active_record/associations/has_many_through_association.rb | 1 + activerecord/lib/active_record/associations/has_one_association.rb | 1 + .../lib/active_record/associations/has_one_through_association.rb | 1 + .../lib/active_record/associations/through_association_scope.rb | 1 + 10 files changed, 12 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index d9903243ce..5b5094bcab 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -3,6 +3,8 @@ require 'active_support/core_ext/array/wrap' module ActiveRecord module Associations + # = Active Record Association Collection + # # AssociationCollection is an abstract class that provides common stuff to # ease the implementation of association proxies that represent # collections. See the class hierarchy in AssociationProxy. diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index e88618d278..f333f4d603 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -2,6 +2,8 @@ require 'active_support/core_ext/array/wrap' module ActiveRecord module Associations + # = Active Record Associations + # # This is the root class of all association proxies: # # AssociationProxy diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index d2f2267e5c..c2a6495db5 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Belongs To Associations module Associations class BelongsToAssociation < AssociationProxy #:nodoc: def create(attributes = {}) diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index f6edd6383c..38454ec242 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Belongs To Polymorphic Association module Associations class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc: def replace(record) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 7f39a189e4..c989c3536d 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Has And Belongs To Many Association module Associations class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc: def create(attributes = {}) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 0464e8ceea..92c6b3e770 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Has Many Association module Associations # This is the proxy that handles a has many association. # diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 5338bb099d..17f850756f 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -2,6 +2,7 @@ require "active_record/associations/through_association_scope" require 'active_support/core_ext/object/blank' module ActiveRecord + # = Active Record Has Many Through Association module Associations class HasManyThroughAssociation < HasManyAssociation #:nodoc: include ThroughAssociationScope diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index ea769fd48b..68b8b792ad 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Belongs To Has One Association module Associations class HasOneAssociation < AssociationProxy #:nodoc: def initialize(owner, reflection) diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index a79bf943d1..fba0a2bfcc 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -1,6 +1,7 @@ require "active_record/associations/through_association_scope" module ActiveRecord + # = Active Record Has One Through Association module Associations class HasOneThroughAssociation < HasOneAssociation include ThroughAssociationScope diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 93bd6e3185..22e1033a9d 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -1,4 +1,5 @@ module ActiveRecord + # = Active Record Through Association Scope module Associations module ThroughAssociationScope -- cgit v1.2.3 From 35e3f72af29a33189a46a4b5ada84768b87e0ef2 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Wed, 16 Jun 2010 23:53:10 +0430 Subject: Adds backslash to scope for cross-references. --- activerecord/lib/active_record/named_scope.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 096b9da402..47eef9535e 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -19,7 +19,7 @@ module ActiveRecord # fruits = fruits.where(:colour => 'red') if options[:red_only] # fruits = fruits.limit(10) if limited? # - # Anonymous scopes tend to be useful when procedurally generating complex + # Anonymous \scopes tend to be useful when procedurally generating complex # queries, where passing intermediate values (scopes) around as first-class # objects is convenient. # -- cgit v1.2.3 From 1910c9239531ae98af523da81c53eded14e2909f Mon Sep 17 00:00:00 2001 From: Malcolm Locke Date: Thu, 17 Jun 2010 14:26:13 +1200 Subject: Missing quote and indentation wrong --- activerecord/lib/active_record/nested_attributes.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 99f8b431c8..12a75f5d16 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -133,7 +133,7 @@ module ActiveRecord # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!' # member.posts.second.title # => 'The egalitarian assumption of the modern citizen' # - # Alternatively, :reject_if also accepts a symbol for using methods: + # Alternatively, :reject_if also accepts a symbol for using methods: # # class Member < ActiveRecord::Base # has_many :posts @@ -145,7 +145,7 @@ module ActiveRecord # accepts_nested_attributes_for :posts, :reject_if => :reject_posts # # def reject_posts(attributed) - # attributed['title].blank? + # attributed['title'].blank? # end # end # -- cgit v1.2.3 From 237165feb3e5f7b117b05353bd64d756b9f18f74 Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Fri, 18 Jun 2010 16:59:33 -0500 Subject: Fix bug with rolling back frozen attributes. [#2991] Signed-off-by: Jeremy Kemper --- .../connection_adapters/abstract/database_statements.rb | 4 ++-- activerecord/lib/active_record/transactions.rb | 1 + activerecord/test/cases/autosave_association_test.rb | 1 - activerecord/test/cases/transaction_callbacks_test.rb | 14 +++++++++++--- 4 files changed, 14 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index b9fb452eee..25432e9985 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -304,7 +304,7 @@ module ActiveRecord begin record.rolledback!(rollback) rescue Exception => e - record.logger.error(e) if record.respond_to?(:logger) + record.logger.error(e) if record.respond_to?(:logger) && record.logger end end end @@ -319,7 +319,7 @@ module ActiveRecord begin record.committed! rescue Exception => e - record.logger.error(e) if record.respond_to?(:logger) + record.logger.error(e) if record.respond_to?(:logger) && record.logger end end end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 620758f5af..775d9f0fb8 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -320,6 +320,7 @@ module ActiveRecord if @_start_transaction_state[:level] < 1 restore_state = remove_instance_variable(:@_start_transaction_state) if restore_state + @attributes = @attributes.dup if @attributes.frozen? @new_record = restore_state[:new_record] @destroyed = restore_state[:destroyed] if restore_state[:id] diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 4e4f9c385c..3b89c12a3f 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -712,7 +712,6 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end assert_raise(RuntimeError) { assert !@pirate.save } - assert before.first.frozen? # the first child was indeed destroyed assert_equal before, @pirate.reload.send(association_name) end diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index ebc16653cb..df123c9de8 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -221,20 +221,28 @@ class TransactionCallbacksTest < ActiveRecord::TestCase assert_equal 2, @first.rollbacks end - def test_after_transaction_callbacks_should_not_raise_errors + def test_after_transaction_callbacks_should_prevent_callbacks_from_being_called def @first.last_after_transaction_error=(e); @last_transaction_error = e; end def @first.last_after_transaction_error; @last_transaction_error; end @first.after_commit_block{|r| r.last_after_transaction_error = :commit; raise "fail!";} @first.after_rollback_block{|r| r.last_after_transaction_error = :rollback; raise "fail!";} + @second.after_commit_block{|r| r.history << :after_commit} + @second.after_rollback_block{|r| r.history << :after_rollback} - @first.save! + Topic.transaction do + @first.save! + @second.save! + end assert_equal :commit, @first.last_after_transaction_error + assert_equal [:after_commit], @second.history + @second.history.clear Topic.transaction do @first.save! + @second.save! raise ActiveRecord::Rollback end - assert_equal :rollback, @first.last_after_transaction_error + assert_equal [:after_rollback], @second.history end end -- cgit v1.2.3 From a7cae64104008b124c834b839887bbbbe148219b Mon Sep 17 00:00:00 2001 From: Master Lambaster Date: Tue, 25 May 2010 16:11:23 +0300 Subject: Fix test which prevents connection reset on failing and remove hardcoded connection [#4689 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/test/cases/adapter_test.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index fc08c2178a..646aa88d80 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -65,15 +65,14 @@ class AdapterTest < ActiveRecord::TestCase end def test_not_specifying_database_name_for_cross_database_selects - assert_nothing_raised do - ActiveRecord::Base.establish_connection({ - :adapter => 'mysql', - :username => 'rails' - }) - ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses" + begin + assert_nothing_raised do + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database)) + ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses" + end + ensure + ActiveRecord::Base.establish_connection 'arunit' end - - ActiveRecord::Base.establish_connection 'arunit' end end -- cgit v1.2.3 From 033e0a041f10ef4d4aa8ebb576560df20b971026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 19 Jun 2010 17:15:21 +0200 Subject: ActiveRecord and ActionPack now use the new descendants implementation. --- activerecord/lib/active_record/associations.rb | 2 +- activerecord/lib/active_record/base.rb | 36 +++----------------------- activerecord/lib/active_record/observer.rb | 3 ++- activerecord/test/cases/base_test.rb | 4 --- 4 files changed, 7 insertions(+), 38 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9b59266bbc..567218e6fb 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2066,7 +2066,7 @@ module ActiveRecord unless klass.descends_from_active_record? sti_column = aliased_table[klass.inheritance_column] sti_condition = sti_column.eq(klass.sti_name) - klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) } + klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) } @join << sti_condition end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3b6ffa46f2..def0fdaa2f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2,6 +2,7 @@ require 'yaml' require 'set' require 'active_support/benchmarkable' require 'active_support/dependencies' +require 'active_support/descendants_tracker' require 'active_support/time' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' @@ -274,28 +275,6 @@ module ActiveRecord #:nodoc: # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+. cattr_accessor :logger, :instance_writer => false - def self.inherited(child) #:nodoc: - @@subclasses[self] ||= [] - @@subclasses[self] << child - super - end - - def self.reset_subclasses #:nodoc: - nonreloadables = [] - subclasses.each do |klass| - unless ActiveSupport::Dependencies.autoloaded? klass - nonreloadables << klass - next - end - klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) } - klass.instance_methods(false).each { |m| klass.send :undef_method, m } - end - @@subclasses = {} - nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass } - end - - @@subclasses = {} - ## # :singleton-method: # Contains the database configuration - as is typically stored in config/database.yml - @@ -810,7 +789,7 @@ module ActiveRecord #:nodoc: end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: - subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } + descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } end def attribute_method?(attribute) @@ -975,7 +954,7 @@ module ActiveRecord #:nodoc: def type_condition sti_column = arel_table[inheritance_column] condition = sti_column.eq(sti_name) - subclasses.each{|subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) } + descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) } condition end @@ -1165,14 +1144,6 @@ module ActiveRecord #:nodoc: with_scope(method_scoping, :overwrite, &block) end - # Returns a list of all subclasses of this class, meaning all descendants. - def subclasses - @@subclasses[self] ||= [] - @@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses } - end - - public :subclasses - # Sets the default options for the model. The format of the # options argument is the same as in find. # @@ -1900,6 +1871,7 @@ module ActiveRecord #:nodoc: extend ActiveModel::Naming extend QueryCache::ClassMethods extend ActiveSupport::Benchmarkable + extend ActiveSupport::DescendantsTracker include ActiveModel::Conversion include Validations diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 0ea7fe7365..fabbc33005 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -105,8 +105,9 @@ module ActiveRecord end protected + def observed_subclasses - observed_classes.sum([]) { |klass| klass.send(:subclasses) } + observed_classes.sum([]) { |klass| klass.send(:descendants) } end def observe_callbacks? diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5c175de6d4..7c74d87b61 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -2076,10 +2076,6 @@ class BasicsTest < ActiveRecord::TestCase assert !SubStiPost.descends_from_active_record? end - def test_base_subclasses_is_public_method - assert ActiveRecord::Base.public_methods.map(&:to_sym).include?(:subclasses) - end - def test_find_on_abstract_base_class_doesnt_use_type_condition old_class = LooseDescendant Object.send :remove_const, :LooseDescendant -- cgit v1.2.3 From 312f43324159fbcd8749cd331ed7d6500a714a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 19 Jun 2010 17:51:29 +0200 Subject: Clear DescendantsTracker on each request. --- activerecord/lib/active_record/railtie.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index a32fb7d399..0386fd4651 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -68,7 +68,6 @@ module ActiveRecord unless app.config.cache_classes ActiveSupport.on_load(:active_record) do ActionDispatch::Callbacks.after do - ActiveRecord::Base.reset_subclasses ActiveRecord::Base.clear_reloadable_connections! end end -- cgit v1.2.3 From 51739d3228d12907d60fb1b0a2b1ef96c55f66a3 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 16 Jun 2010 11:30:37 -0400 Subject: moving before_validation and after_validation functionality from ActiveRecord to ActiveModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4653 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 1 + activerecord/lib/active_record/callbacks.rb | 23 +++-------------------- activerecord/lib/active_record/validations.rb | 4 ++-- 3 files changed, 6 insertions(+), 22 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index def0fdaa2f..ef8dbdb6e5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1874,6 +1874,7 @@ module ActiveRecord #:nodoc: extend ActiveSupport::DescendantsTracker include ActiveModel::Conversion + include ActiveModel::Validations::Callbacks include Validations extend CounterCache include Locking::Optimistic, Locking::Pessimistic diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 44fee12001..42b56a3cf8 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -235,7 +235,7 @@ module ActiveRecord included do extend ActiveModel::Callbacks - define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name] + attr_accessor :validation_context define_model_callbacks :initialize, :find, :only => :after define_model_callbacks :save, :create, :update, :destroy @@ -250,28 +250,11 @@ module ActiveRecord end end - def before_validation(*args, &block) - options = args.last - if options.is_a?(Hash) && options[:on] - options[:if] = Array.wrap(options[:if]) - options[:if] << "@_on_validate == :#{options[:on]}" - end - set_callback(:validation, :before, *args, &block) - end - - def after_validation(*args, &block) - options = args.extract_options! - options[:prepend] = true - options[:if] = Array.wrap(options[:if]) - options[:if] << "!halted && value != false" - options[:if] << "@_on_validate == :#{options[:on]}" if options[:on] - set_callback(:validation, :after, *(args << options), &block) - end end def valid?(*) #:nodoc: - @_on_validate = new_record? ? :create : :update - _run_validation_callbacks { super } + self.validation_context = new_record? ? :create : :update + super end def destroy #:nodoc: diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index be64e00bd1..6ef9382b2a 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -49,12 +49,12 @@ module ActiveRecord # Runs all the specified validations and returns true if no errors were added otherwise false. def valid?(context = nil) context ||= (new_record? ? :create : :update) - super(context) + output = super(context) deprecated_callback_method(:validate) deprecated_callback_method(:"validate_on_#{context}") - errors.empty? + errors.empty? && output end protected -- cgit v1.2.3 From 0247995d05b3cd3ff5fe32d5fbd8fdd866646909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 19 Jun 2010 18:18:45 +0200 Subject: ActiveModel::Validations::Callbacks should not be required by default. --- activerecord/lib/active_record/base.rb | 1 - activerecord/lib/active_record/callbacks.rb | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ef8dbdb6e5..def0fdaa2f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1874,7 +1874,6 @@ module ActiveRecord #:nodoc: extend ActiveSupport::DescendantsTracker include ActiveModel::Conversion - include ActiveModel::Validations::Callbacks include Validations extend CounterCache include Locking::Optimistic, Locking::Pessimistic diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 42b56a3cf8..997c85ef8c 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -234,8 +234,7 @@ module ActiveRecord included do extend ActiveModel::Callbacks - - attr_accessor :validation_context + include ActiveModel::Validations::Callbacks define_model_callbacks :initialize, :find, :only => :after define_model_callbacks :save, :create, :update, :destroy @@ -249,12 +248,6 @@ module ActiveRecord send(meth.to_sym, meth.to_sym) end end - - end - - def valid?(*) #:nodoc: - self.validation_context = new_record? ? :create : :update - super end def destroy #:nodoc: @@ -269,6 +262,7 @@ module ActiveRecord end private + def create_or_update #:nodoc: _run_save_callbacks { super } end -- cgit v1.2.3 From f664c57fe854c230d25c448b941569b81860fa79 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Sat, 12 Jun 2010 11:55:31 +0100 Subject: Don't overwrite unsaved updates when loading an association but preserve the order of the loaded records. Reapplied from before but now allows already-saved records to be refreshed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4830 state:resolved] Signed-off-by: José Valim --- .../associations/association_collection.rb | 6 +++++- activerecord/test/cases/nested_attributes_test.rb | 21 +++++++++++++++++++++ activerecord/test/models/pirate.rb | 4 ++-- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index d9903243ce..bd67516518 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -388,7 +388,11 @@ module ActiveRecord begin if !loaded? if @target.is_a?(Array) && @target.any? - @target = find_target + @target.find_all {|t| t.new_record? } + @target = find_target.map do |f| + i = @target.index(f) + t = @target.delete_at(i) if i + (t && t.changed?) ? t : f + end + @target else @target = find_target end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 685b11cb03..65d6080ea5 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -466,6 +466,27 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Grace OMalley', @child_1.reload.name end + def test_should_not_overwrite_unsaved_updates_when_loading_association + @pirate.reload + @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) + assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name + end + + def test_should_preserve_order_when_not_overwriting_unsaved_updates + @pirate.reload + @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) + assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id + end + + def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates + @pirate.reload + record = @pirate.class.reflect_on_association(@association_name).klass.new(:name => 'Grace OMalley') + @pirate.send(@association_name) << record + record.save! + @pirate.send(@association_name).last.update_attributes!(:name => 'Polly') + assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name + end + def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models @child_1.stubs(:id).returns('ABC1X') @child_2.stubs(:id).returns('ABC2X') diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index f1dbe32c6e..d89c8cf381 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -1,7 +1,7 @@ class Pirate < ActiveRecord::Base belongs_to :parrot, :validate => true belongs_to :non_validated_parrot, :class_name => 'Parrot' - has_and_belongs_to_many :parrots, :validate => true + has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC' has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot' has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot", :before_add => :log_before_add, @@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base has_one :ship has_one :update_only_ship, :class_name => 'Ship' has_one :non_validated_ship, :class_name => 'Ship' - has_many :birds + has_many :birds, :order => 'birds.id ASC' has_many :birds_with_method_callbacks, :class_name => "Bird", :before_add => :log_before_add, :after_add => :log_after_add, -- cgit v1.2.3 From e061212e86ef0342fad1211fd980fc4b2195fe69 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 14 Jun 2010 13:39:58 -0300 Subject: Refactor build_arel: move joins out and simplify havings. [#4860 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/relation/query_methods.rb | 92 +++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 7a48a6596a..50e94134f5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -116,45 +116,7 @@ module ActiveRecord def build_arel arel = table - joined_associations = [] - association_joins = [] - - joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - - joins.each do |join| - association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) - end - - stashed_association_joins = joins.select {|j| j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)} - - non_association_joins = (joins - association_joins - stashed_association_joins).reject {|j| j.blank?} - custom_joins = custom_join_sql(*non_association_joins) - - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) - - join_dependency.graft(*stashed_association_joins) - - @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? - - to_join = [] - - join_dependency.join_associations.each do |association| - if (association_relation = association.relation).is_a?(Array) - to_join << [association_relation.first, association.join_class, association.association_join.first] - to_join << [association_relation.last, association.join_class, association.association_join.last] - else - to_join << [association_relation, association.join_class, association.association_join] - end - end - - to_join.each do |tj| - unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] && ja[2] == tj[2] } - joined_associations << tj - arel = arel.join(tj[0], tj[1]).on(*tj[2]) - end - end - - arel = arel.join(custom_joins) + arel = build_joins(arel, @joins_values) if @joins_values.present? @where_values.uniq.each do |where| next if where.blank? @@ -168,9 +130,7 @@ module ActiveRecord end end - @having_values.uniq.each do |h| - arel = h.is_a?(String) ? arel.having(h) : arel.having(*h) - end + arel = arel.having(*@having_values.uniq.select{|h| h.present?}) arel = arel.take(@limit_value) if @limit_value.present? arel = arel.skip(@offset_value) if @offset_value.present? @@ -181,18 +141,16 @@ module ActiveRecord selects = @select_values.uniq - quoted_table_name = @klass.quoted_table_name - if selects.present? selects.each do |s| @implicit_readonly = false arel = arel.project(s) if s.present? end else - arel = arel.project(quoted_table_name + '.*') + arel = arel.project(@klass.quoted_table_name + '.*') end - arel = @from_value.present? ? arel.from(@from_value) : arel.from(quoted_table_name) + arel = @from_value.present? ? arel.from(@from_value) : arel.from(@klass.quoted_table_name) case @lock_value when TrueClass @@ -221,6 +179,48 @@ module ActiveRecord private + def build_joins(relation, joins) + joined_associations = [] + association_joins = [] + + joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq + + joins.each do |join| + association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) + end + + stashed_association_joins = joins.select {|j| j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)} + + non_association_joins = (joins - association_joins - stashed_association_joins).reject {|j| j.blank?} + custom_joins = custom_join_sql(*non_association_joins) + + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) + + join_dependency.graft(*stashed_association_joins) + + @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? + + to_join = [] + + join_dependency.join_associations.each do |association| + if (association_relation = association.relation).is_a?(Array) + to_join << [association_relation.first, association.join_class, association.association_join.first] + to_join << [association_relation.last, association.join_class, association.association_join.last] + else + to_join << [association_relation, association.join_class, association.association_join] + end + end + + to_join.each do |tj| + unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] && ja[2] == tj[2] } + joined_associations << tj + relation = relation.join(tj[0], tj[1]).on(*tj[2]) + end + end + + relation.join(custom_joins) + end + def apply_modules(modules) values = Array.wrap(modules) @extensions += values if values.present? -- cgit v1.2.3 From 95a8f252c028c94b70cce4888bce42b7e9e30786 Mon Sep 17 00:00:00 2001 From: rohit Date: Wed, 16 Jun 2010 18:50:50 +0530 Subject: remove executable permission from files that don't need it. [#4802 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/examples/performance.rb | 0 activerecord/lib/active_record/associations.rb | 0 activerecord/lib/active_record/base.rb | 0 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 0 activerecord/test/cases/base_test.rb | 0 activerecord/test/cases/counter_cache_test.rb | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 activerecord/examples/performance.rb mode change 100755 => 100644 activerecord/lib/active_record/associations.rb mode change 100755 => 100644 activerecord/lib/active_record/base.rb mode change 100755 => 100644 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb mode change 100755 => 100644 activerecord/test/cases/base_test.rb mode change 100755 => 100644 activerecord/test/cases/counter_cache_test.rb (limited to 'activerecord') diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb old mode 100755 new mode 100644 -- cgit v1.2.3 From 451594784552bd6955687d8ff617c484ec26946b Mon Sep 17 00:00:00 2001 From: Steve Abatangle Date: Fri, 11 Jun 2010 13:15:14 -0700 Subject: Fix problem with migrations template that can cause bogus code to be created MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/migration/templates/migration.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'activerecord') 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 bbb7c53d86..d6ab3257a0 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -1,11 +1,15 @@ class <%= migration_class_name %> < ActiveRecord::Migration def self.up<% attributes.each do |attribute| %> - <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%> - <%- end %> + <%- if migration_action -%> + <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %> + <%- end -%> + <%- end -%> end def self.down<% attributes.reverse.each do |attribute| %> - <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%> - <%- end %> + <%- if migration_action -%> + <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %> + <%- end -%> + <%- end -%> end end -- cgit v1.2.3 From 3ef1d63a78494d0b7e7e82f7240b1977e3b386dd Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Mon, 21 Jun 2010 00:45:34 +0430 Subject: All scopes are now cross-referenced. --- activerecord/lib/active_record/named_scope.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 47eef9535e..ec0a98c6df 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -4,7 +4,7 @@ require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/blank' module ActiveRecord - # = Active Record Named Scopes + # = Active Record Named \Scopes module NamedScope extend ActiveSupport::Concern @@ -20,10 +20,10 @@ module ActiveRecord # fruits = fruits.limit(10) if limited? # # Anonymous \scopes tend to be useful when procedurally generating complex - # queries, where passing intermediate values (scopes) around as first-class + # queries, where passing intermediate values (\scopes) around as first-class # objects is convenient. # - # You can define a scope that applies to all finders using + # You can define a \scope that applies to all finders using # ActiveRecord::Base.default_scope. def scoped(options = {}, &block) if options.present? @@ -38,7 +38,7 @@ module ActiveRecord read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) end - # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, + # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query, # such as where(:color => :red).select('shirts.*').includes(:washing_instructions). # # class Shirt < ActiveRecord::Base -- cgit v1.2.3 From 7b7cedcb8d53110492e7d51405986f3e8e899fa4 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 20 Jun 2010 15:51:49 -0300 Subject: Don't waste time building relations if there are no values presents. [#4860 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 50e94134f5..60fa839df2 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -130,14 +130,14 @@ 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?}.map(&:to_s)) if @order_values.present? selects = @select_values.uniq @@ -150,7 +150,7 @@ module ActiveRecord 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 -- cgit v1.2.3 From 26392c4ac57e27c63984d47c6326c13f502d5786 Mon Sep 17 00:00:00 2001 From: Jeroen van Dijk Date: Wed, 19 May 2010 15:25:46 +0100 Subject: Make ActiveModel::Errors#add_on_blank and #add_on_empty accept an options hash and make various Validators pass their (filtered) options. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it possible to pass additional options through Validators to message generation. E.g. plugin authors want to add validates_presence_of :foo, :format => "some format". Also, cleanup the :default vs :message options confusion in ActiveModel validation message generation. Also, deprecate ActiveModel::Errors#add_on_blank(attributes, custom_message) in favor of ActiveModel::Errors#add_on_blank(attributes, options). Also, refactoring of ActiveModel and ActiveRecord Validation tests. Test are a lot more DRY now. Better test coverage as well now. The first four points were reapplied from an older patch of Sven Fuchs which didn't apply cleanly anymore and was not complete yet. Signed-off-by: José Valim --- .../lib/active_record/validations/associated.rb | 2 +- .../lib/active_record/validations/uniqueness.rb | 2 +- .../i18n_generate_message_validation_test.rb | 12 +++--- .../test/cases/validations/i18n_validation_test.rb | 50 ++++++++++++---------- 4 files changed, 36 insertions(+), 30 deletions(-) (limited to 'activerecord') 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/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 -- cgit v1.2.3 From df595b577edf807c3b9315feb882d12d9b249af9 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:29:59 -0300 Subject: Use the new query API in AR performance script. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/examples/performance.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') 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 = { -- cgit v1.2.3 From 7df105b1e604846f5dfe184128f99499da477bbe Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:46:12 -0300 Subject: Fix order method to accept relation attributes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/test/cases/relations_test.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 60fa839df2..43032ba9d8 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -137,7 +137,7 @@ module ActiveRecord 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)) if @order_values.present? + arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? selects = @select_values.uniq diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 43519db976..ece287d771 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -503,13 +503,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 @@ -593,4 +593,8 @@ 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 end -- cgit v1.2.3 From 12a9e8479958bcada73b3df6b254ce69cb02305c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:51:09 -0300 Subject: Clean CHANGELOG whitespace. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/CHANGELOG | 508 ++++++++++++++++++++++++------------------------- 1 file changed, 254 insertions(+), 254 deletions(-) (limited to 'activerecord') 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 ] -* 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 ] -* 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 ] @@ -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 ] -* 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 ] -* 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] 1 50 - + * 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 ] +* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] * 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: - + The First Topic @@ -3673,13 +3673,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio 2004-04-15 - + ...and you can configure with: - + topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ]) - + ...that'll return: - + The First Topic David @@ -3689,13 +3689,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio 2004-04-15 - + You can even do load first-level associations as part of the document: - + firm.to_xml :include => [ :account, :clients ] - + ...that'll return something like: - + 1 @@ -3715,7 +3715,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio 1 50 - + * 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 ] +* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] * 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 integer is false) or applying it to the regular expression /^[\+\-]?\d+$/ (if integer is set to true). - + class Person < ActiveRecord::Base validates_numericality_of :value, :on => :create end - + Configuration options: * message - A custom error message (default is: "is not a number") * on Specifies when this validation is active (default is :save, other options :create, :update) * only_integer 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 lock_version 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 find_by_, so you get finders like Person.find_by_user_name, Payment.find_by_transaction_id. So instead of writing Person.find_first(["user_name = ?", user_name]), you just do Person.find_by_user_name(user_name). - + It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like Person.find_by_user_name_and_password or even Payment.find_by_purchaser_and_state_and_country. So instead of writing - Person.find_first(["user_name = ? AND password = ?", user_name, password]), you just do + Person.find_first(["user_name = ? AND password = ?", user_name, password]), you just do Person.find_by_user_name_and_password(user_name, password). - 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 Payment.find_all_by_amount(50) that is turned into Payment.find_all(["amount = ?", 50]). 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 -- cgit v1.2.3 From 9dda440c38bd9878b0aa7ceb854bf4f1beeabd68 Mon Sep 17 00:00:00 2001 From: Edgars Beigarts Date: Tue, 22 Jun 2010 17:49:59 +0300 Subject: Improve documentation for assocations :autosave option. --- activerecord/lib/active_record/associations.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 399c1bf58a..4caa434fc0 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -886,7 +886,9 @@ module ActiveRecord # [:validate] # If false, don't validate the associated objects when saving the parent object. true by default. # [:autosave] - # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default. + # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. + # If false, never save or destroy the associated objects. + # By default, only save associated objects that are new records. # [:inverse_of] # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_many # association. Does not work in combination with :through or :as options. @@ -1001,7 +1003,9 @@ module ActiveRecord # [:validate] # If false, don't validate the associated object when saving the parent object. +false+ by default. # [:autosave] - # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default. + # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. + # If false, never save or destroy the associated object. + # By default, only save the associated object if it's a new record. # [:inverse_of] # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_one # association. Does not work in combination with :through or :as options. @@ -1103,7 +1107,9 @@ module ActiveRecord # [:validate] # If false, don't validate the associated objects when saving the parent object. +false+ by default. # [:autosave] - # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default. + # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. + # If false, never save or destroy the associated object. + # By default, only save the associated object if it's a new record. # [:touch] # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute. @@ -1290,7 +1296,9 @@ module ActiveRecord # [:validate] # If false, don't validate the associated objects when saving the parent object. +true+ by default. # [:autosave] - # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default. + # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. + # If false, never save or destroy the associated objects. + # By default, only save associated objects that are new records. # # Option examples: # has_and_belongs_to_many :projects -- cgit v1.2.3 From 7033be5f72a2b68223eff5d3946aba51e80b4c0c Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 21 Jun 2010 16:40:45 -0400 Subject: test for #4917 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/relations_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ece287d771..165b2e1e6c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -16,6 +16,13 @@ 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 -- cgit v1.2.3 From d0df7f1196ee633a0cea7b5b8cf8b65a8269df19 Mon Sep 17 00:00:00 2001 From: James Harton Date: Mon, 21 Jun 2010 14:21:54 +1200 Subject: Fix small bug where ActiveRecord::PredicateBuilder#build_from_hash didn't test for Arel::Relation as right hand value. [#4917 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/predicate_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') 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) -- cgit v1.2.3 From f61ac899479543859cab67e4ad833284dbf982e9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 Jun 2010 16:00:02 -0300 Subject: Avoid method redefined warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/active_schema_test_postgresql.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb index 4f04c6735c..f106e14319 100644 --- a/activerecord/test/cases/active_schema_test_postgresql.rb +++ b/activerecord/test/cases/active_schema_test_postgresql.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 -- cgit v1.2.3 From 62c4e4d3856b38ee9869f4ad6342e712788c8635 Mon Sep 17 00:00:00 2001 From: Edgars Beigarts Date: Tue, 22 Jun 2010 11:48:11 +0300 Subject: Fix connection reloading in development mode. [#4929 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/connection_adapters/abstract/connection_pool.rb | 4 +++- .../lib/active_record/connection_adapters/abstract_adapter.rb | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'activerecord') 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_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4ee9fee4a9..4567539566 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -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). -- cgit v1.2.3 From e639536ea80e94f5d72493267c8aec21d305cf74 Mon Sep 17 00:00:00 2001 From: Jeff Dean Date: Wed, 9 Jun 2010 00:13:24 -0400 Subject: remove_column should raise an ArgumentError when no columns are passed [#4803 state:resolved] Signed-off-by: Michael Koziarski --- .../active_record/connection_adapters/abstract/schema_statements.rb | 1 + activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 1 + activerecord/test/cases/migration_test.rb | 4 ++++ 3 files changed, 6 insertions(+) (limited to 'activerecord') 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..638e5d7236 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -205,6 +205,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 diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index deb62e3802..ad6314c530 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -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]) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6fe3b01281..9ece2fbab1 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -748,6 +748,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 -- cgit v1.2.3 From d132dd33520ba61f7bfa9ba6fdd1b7b2bebd27f3 Mon Sep 17 00:00:00 2001 From: Paul Gillard Date: Fri, 18 Jun 2010 00:02:31 +0100 Subject: Don't clone associations [#4894 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloning an active record object should be shallow in that it should copy attributes but not associations. This was no longer true as a result of #3164. Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 1 + activerecord/test/cases/base_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3f81ca7555..3a7db97a6a 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1433,6 +1433,7 @@ module ActiveRecord #:nodoc: end clear_aggregation_cache + clear_association_cache @attributes_cache = {} @new_record = true ensure_proper_type diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 7c74d87b61..c24a494c85 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 } -- cgit v1.2.3 From 7008911222826eef07a338bf4cab27b83fe90ce1 Mon Sep 17 00:00:00 2001 From: "Mohammed Siddick.E" Date: Wed, 23 Jun 2010 12:33:34 +0530 Subject: Patch for Namespace problem in Scaffold. [#4763 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/rails/generators/active_record/model/model_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') 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 -- cgit v1.2.3 From a3ce6fcc567836c99bb93cbe880b2c333a511593 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 07:38:51 -0400 Subject: adding column named 'group' to ensure that nothing breaks given that 'group' is sql reserved word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4945 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/base_test.rb | 2 +- activerecord/test/cases/reflection_test.rb | 10 +++++----- activerecord/test/schema/schema.rb | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c24a494c85..f8b90d8877 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -2243,7 +2243,7 @@ class BasicsTest < ActiveRecord::TestCase def test_inspect_instance topic = topics(:first) - assert_equal %(#), topic.inspect + assert_equal %(#), topic.inspect end def test_inspect_new_instance 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/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| -- cgit v1.2.3 From eb04408a20628a49296e0859425940b39a83ec63 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 10:05:30 -0400 Subject: ActiveRecord's relation object should respond to to_json and to_yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4547 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relations_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 66970a5ea1..a342b1fe6f 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_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel attr_reader :table, :klass diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 165b2e1e6c..f2216a1923 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -29,6 +29,16 @@ class RelationTest < ActiveRecord::TestCase 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_scoped_all topics = Topic.scoped.all assert_kind_of Array, topics -- cgit v1.2.3 From 0bf3baa6b3d216c6340f8d3b5d0a3ebc093e969a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 11:54:38 -0400 Subject: adding fix for to_xml for ActiveRecord relation object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relations_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a342b1fe6f..a51b317dc3 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 :to_json, :to_yaml, :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 diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index f2216a1923..4316aa98d4 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -39,6 +39,11 @@ class RelationTest < ActiveRecord::TestCase 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 -- cgit v1.2.3 From 49f52c3d910c8f183afc3a54ea2ae9667f23085e Mon Sep 17 00:00:00 2001 From: Michael Lovitt Date: Tue, 22 Jun 2010 09:55:50 -0400 Subject: Sessions should not be created until written to and session data should be destroyed on reset. [#4938] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/session_store.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord') 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) -- cgit v1.2.3 From 4a0a640d33e1c729d38c6091bb1394fbda059b5c Mon Sep 17 00:00:00 2001 From: Paul Gillard Date: Wed, 23 Jun 2010 21:36:02 +0100 Subject: Remove incorrect comment regarding #initialize_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comment added in #3164 is incorrect after reading http://dev.rubyonrails.org/ticket/7191. Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3a7db97a6a..7a262ad465 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1413,14 +1413,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) -- cgit v1.2.3 From 41ed4db560bf50e0ccb99915a03c37d39bff135b Mon Sep 17 00:00:00 2001 From: rohit Date: Thu, 24 Jun 2010 09:41:44 +0530 Subject: Guides: Add :references to supported column types. --- activerecord/lib/active_record/migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b273c33e50..b23bb62001 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -101,7 +101,7 @@ module ActiveRecord # named +column_name+ specified to be one of the following types: # :string, :text, :integer, :float, # :decimal, :datetime, :timestamp, :time, - # :date, :binary, :boolean. A default value can be + # :date, :binary, :boolean and :references. A default value can be # specified by passing an +options+ hash like { :default => 11 }. # Other options include :limit and :null (e.g. # { :limit => 50, :null => false }) -- see -- cgit v1.2.3 From 0758d49300ea4c19c813ffd90854719379f6771c Mon Sep 17 00:00:00 2001 From: rohit Date: Thu, 24 Jun 2010 09:55:49 +0530 Subject: Revert "Guides: Add :references to supported column types." This reverts commit 41ed4db560bf50e0ccb99915a03c37d39bff135b. --- activerecord/lib/active_record/migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b23bb62001..b273c33e50 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -101,7 +101,7 @@ module ActiveRecord # named +column_name+ specified to be one of the following types: # :string, :text, :integer, :float, # :decimal, :datetime, :timestamp, :time, - # :date, :binary, :boolean and :references. A default value can be + # :date, :binary, :boolean. A default value can be # specified by passing an +options+ hash like { :default => 11 }. # Other options include :limit and :null (e.g. # { :limit => 50, :null => false }) -- see -- cgit v1.2.3 From 5441e082f9478ddb3c676c681b09786f1391483c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 23 Jun 2010 13:10:53 -0300 Subject: Add tests for relation count. Fix other tests that were conceptually wrong. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4882 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/calculations_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'activerecord') 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/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4316aa98d4..abf43cea98 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -483,7 +483,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 @@ -619,4 +619,8 @@ class RelationTest < ActiveRecord::TestCase 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 -- cgit v1.2.3 From 6788db824ab732b13493a9d702dd8fb89fa153c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 13:23:43 +0200 Subject: Move Rails::LogSubscriber to ActiveSupport::LogSubscriber, allowing frameworks like ActiveRecord and ActiveResource to log outsude Rails::Application [#4816 state:resolved] --- activerecord/lib/active_record/base.rb | 1 + activerecord/lib/active_record/log_subscriber.rb | 32 ++++++++++++++++++++++ activerecord/lib/active_record/railtie.rb | 3 -- .../lib/active_record/railties/log_subscriber.rb | 32 ---------------------- activerecord/test/cases/log_subscriber_test.rb | 9 +++--- 5 files changed, 37 insertions(+), 40 deletions(-) create mode 100644 activerecord/lib/active_record/log_subscriber.rb delete mode 100644 activerecord/lib/active_record/railties/log_subscriber.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7a262ad465..16cf501bd5 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 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/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/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 -- cgit v1.2.3 From fdb7f84eb10c5e59490764a1a259aa00a1fcfe5f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 11:17:05 -0300 Subject: Remove deprecated block usage in composed_of. --- activerecord/lib/active_record/aggregations.rb | 6 ++---- activerecord/test/cases/aggregations_test.rb | 28 -------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) (limited to 'activerecord') 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/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 -- cgit v1.2.3 From d9f199e1238a723432a2005c405fc5ae22dea24b Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 11:40:23 -0300 Subject: Don't define block, just yield if block is given. --- .../active_record/associations/association_collection.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') 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" -- cgit v1.2.3 From 4086ecea24446904bac4c69812f219ce7cbfbbba Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 12:02:00 -0300 Subject: Remove block definition from method, is not needed since yield is used inside. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 +- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4567539566..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 &block. - def disable_referential_integrity(&block) + def disable_referential_integrity yield end 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..6fa4c50d6a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -372,7 +372,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 -- cgit v1.2.3 From 2c203a94136d5b8681e2b2b55783ef6dde54405f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 13:07:39 -0300 Subject: Remove block definitions in finder methods. --- activerecord/lib/active_record/relation/finder_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') 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? -- cgit v1.2.3 From 4b5f417e63e4cdd7fd6837c10ebaa9dd4860f55e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 13:17:24 -0300 Subject: Only yield block if given. --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 43032ba9d8..e8d0f215f6 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -22,9 +22,9 @@ module ActiveRecord end class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def select(*args, &block) + def select(*args) if block_given? - to_a.select(&block) + to_a.select { |*block_args| yield(*block_args) } else new_relation = clone value = Array.wrap(args.flatten).reject {|x| x.blank? } -- cgit v1.2.3 From 497a0c3b00cc225215a8744bbf2a7e04c7297b7c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 23 Jun 2010 21:12:51 -0300 Subject: quotes are not necessary here MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 16cf501bd5..e7b52287a5 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -917,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) -- cgit v1.2.3 From 7a7c608a26f03abb1245ff83d4e25040ad09cb44 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 23 Jun 2010 21:44:27 -0300 Subject: Your original TIME ZONE value on PostgreSQL is correctly restored now, after going through options :utc and then going back to :local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4950 state:committed] Signed-off-by: José Valim --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e84242601b..851e6d3718 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? @@ -929,9 +932,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 -- cgit v1.2.3 From 518b16d9aedf4534b11aab8043d4efd0361fa1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Fri, 25 Jun 2010 10:51:43 +0200 Subject: Line break in migration template and nicer code indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/migration/templates/migration.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'activerecord') 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..edc6a18d5d 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 -- cgit v1.2.3 From 3344c011da17cb59f4452101c8e65067d7d30cce Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 13:07:17 -0300 Subject: Avoid a blank line before the add/remove columns --- .../rails/generators/active_record/migration/templates/migration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') 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 edc6a18d5d..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,6 +1,6 @@ class <%= migration_class_name %> < ActiveRecord::Migration def self.up -<% attributes.each do |attribute| %> +<% attributes.each do |attribute| -%> <%- if migration_action -%> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %> <%- end -%> @@ -8,7 +8,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration end def self.down -<% attributes.reverse.each do |attribute| %> +<% 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 -%> -- cgit v1.2.3 From 82dc16b7d2b6313117a42f7bf69a097b5ab79d1f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 24 Jun 2010 20:34:01 -0300 Subject: Metaprogramming not needed here --- .../lib/active_record/relation/query_methods.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e8d0f215f6..adc56fbef0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -21,18 +21,16 @@ module ActiveRecord CEVAL end - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def select(*args) - if block_given? - to_a.select { |*block_args| yield(*block_args) } - else - new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.select_values += value if value.present? - new_relation - end + def select(*args) + if block_given? + to_a.select { |*block_args| yield(*block_args) } + else + new_relation = clone + value = Array.wrap(args.flatten).reject {|x| x.blank? } + new_relation.select_values += value if value.present? + new_relation end - CEVAL + end [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 -- cgit v1.2.3 From 3d8ccb924084ecd341b2d9644e6e0b66903d8432 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 24 Jun 2010 20:36:01 -0300 Subject: Makes a build_select for the select part of build_arel --- .../lib/active_record/relation/query_methods.rb | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index adc56fbef0..1e570a569b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -137,16 +137,7 @@ module ActiveRecord arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? - selects = @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 = build_select(arel, @select_values.uniq) arel = arel.from(@from_value) if @from_value.present? @@ -219,6 +210,18 @@ module ActiveRecord relation.join(custom_joins) end + def build_select(arel, selects) + if selects.present? + @implicit_readonly = false + selects.each do |s| + arel = arel.project(s) if s.present? + end + else + arel = arel.project(@klass.quoted_table_name + '.*') + end + arel + end + def apply_modules(modules) values = Array.wrap(modules) @extensions += values if values.present? -- cgit v1.2.3 From 0ebb5bf6590b8ac62c53538ade7095676baec3d4 Mon Sep 17 00:00:00 2001 From: Neeraj Singh and Santiago Pastorino Date: Thu, 24 Jun 2010 22:52:15 -0300 Subject: Support for multiple selects added [#4841 state:committed] --- activerecord/lib/active_record/relation/query_methods.rb | 11 +++++++---- activerecord/test/cases/method_scoping_test.rb | 8 +++++--- activerecord/test/cases/relations_test.rb | 6 ++++++ 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1e570a569b..4dbb30c777 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -213,13 +213,16 @@ module ActiveRecord def build_select(arel, selects) if selects.present? @implicit_readonly = false - selects.each do |s| - arel = arel.project(s) if s.present? + # 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 = arel.project(@klass.quoted_table_name + '.*') + arel.project(@klass.quoted_table_name + '.*') end - arel end def apply_modules(modules) 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/relations_test.rb b/activerecord/test/cases/relations_test.rb index abf43cea98..5b1c6b8f22 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -491,6 +491,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 -- cgit v1.2.3 From efbd0eb9f7508187259208caf6b51eec206cbac9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 16:31:42 -0300 Subject: Order is not guaranteed here, ordering by id of people table makes it work --- activerecord/test/cases/associations/cascaded_eager_loading_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') 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 -- cgit v1.2.3 From 5e2b473b478d93d6a1aa627d688b2b2ce05fa9ef Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 13:29:17 -0700 Subject: reorganizing adapter specific tests. [#4974 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/Rakefile | 5 +- .../test/cases/active_schema_test_mysql.rb | 124 ----------- .../test/cases/active_schema_test_postgresql.rb | 28 --- .../cases/adapters/firebird/connection_test.rb | 8 + .../test/cases/adapters/firebird/default_test.rb | 16 ++ .../test/cases/adapters/firebird/migration_test.rb | 124 +++++++++++ .../cases/adapters/mysql/active_schema_test.rb | 124 +++++++++++ .../test/cases/adapters/mysql/connection_test.rb | 64 ++++++ .../cases/adapters/mysql/reserved_word_test.rb | 176 +++++++++++++++ .../test/cases/adapters/oracle/synonym_test.rb | 17 ++ .../adapters/postgresql/active_schema_test.rb | 28 +++ .../cases/adapters/postgresql/datatype_test.rb | 243 +++++++++++++++++++++ .../postgresql/schema_authorization_test.rb | 84 +++++++ .../test/cases/adapters/postgresql/schema_test.rb | 193 ++++++++++++++++ .../test/cases/adapters/sqlite/copy_table_test.rb | 80 +++++++ .../test/cases/connection_test_firebird.rb | 8 - activerecord/test/cases/connection_test_mysql.rb | 64 ------ activerecord/test/cases/copy_table_test_sqlite.rb | 80 ------- .../test/cases/datatype_test_postgresql.rb | 243 --------------------- activerecord/test/cases/default_test_firebird.rb | 16 -- activerecord/test/cases/migration_test_firebird.rb | 124 ----------- .../test/cases/reserved_word_test_mysql.rb | 176 --------------- .../cases/schema_authorization_test_postgresql.rb | 84 ------- activerecord/test/cases/schema_test_postgresql.rb | 193 ---------------- activerecord/test/cases/synonym_test_oracle.rb | 17 -- 25 files changed, 1161 insertions(+), 1158 deletions(-) delete mode 100644 activerecord/test/cases/active_schema_test_mysql.rb delete mode 100644 activerecord/test/cases/active_schema_test_postgresql.rb create mode 100644 activerecord/test/cases/adapters/firebird/connection_test.rb create mode 100644 activerecord/test/cases/adapters/firebird/default_test.rb create mode 100644 activerecord/test/cases/adapters/firebird/migration_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/active_schema_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/connection_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/reserved_word_test.rb create mode 100644 activerecord/test/cases/adapters/oracle/synonym_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/active_schema_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/datatype_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/schema_test.rb create mode 100644 activerecord/test/cases/adapters/sqlite/copy_table_test.rb delete mode 100644 activerecord/test/cases/connection_test_firebird.rb delete mode 100644 activerecord/test/cases/connection_test_mysql.rb delete mode 100644 activerecord/test/cases/copy_table_test_sqlite.rb delete mode 100644 activerecord/test/cases/datatype_test_postgresql.rb delete mode 100644 activerecord/test/cases/default_test_firebird.rb delete mode 100644 activerecord/test/cases/migration_test_firebird.rb delete mode 100644 activerecord/test/cases/reserved_word_test_mysql.rb delete mode 100644 activerecord/test/cases/schema_authorization_test_postgresql.rb delete mode 100644 activerecord/test/cases/schema_test_postgresql.rb delete mode 100644 activerecord/test/cases/synonym_test_oracle.rb (limited to 'activerecord') 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/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb deleted file mode 100644 index d7431e5158..0000000000 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ /dev/null @@ -1,124 +0,0 @@ -require "cases/helper" - -class ActiveSchemaTest < ActiveRecord::TestCase - def setup - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_without_stub, :execute - remove_method :execute - def execute(sql, name = nil) return sql end - end - end - - def teardown - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - remove_method :execute - alias_method :execute, :execute_without_stub - end - 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 |*| - false - end - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" - assert_equal expected, add_index(:people, :last_name, :length => nil) - - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" - assert_equal expected, add_index(:people, :last_name, :length => 10) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) - - 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?) - end - - def test_drop_table - assert_equal "DROP TABLE `people`", drop_table(:people) - end - - if current_adapter?(:MysqlAdapter) - def test_create_mysql_database_with_encoding - assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) - assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) - end - - def test_recreate_mysql_database_with_encoding - create_database(:luca, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) - end - end - - def test_add_column - assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string) - end - - def test_add_column_with_limit - assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32) - end - - def test_drop_table_with_specific_database - assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people') - end - - def test_add_timestamps - with_real_execute do - begin - ActiveRecord::Base.connection.create_table :delete_me do |t| - end - ActiveRecord::Base.connection.add_timestamps :delete_me - assert column_present?('delete_me', 'updated_at', 'datetime') - assert column_present?('delete_me', 'created_at', 'datetime') - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - end - end - end - - def test_remove_timestamps - with_real_execute do - begin - ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps - end - ActiveRecord::Base.connection.remove_timestamps :delete_me - assert !column_present?('delete_me', 'updated_at', 'datetime') - assert !column_present?('delete_me', 'created_at', 'datetime') - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - end - end - end - - private - def with_real_execute - #we need to actually modify some data, so we make execute point to the original method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_with_stub, :execute - alias_method :execute, :execute_without_stub - end - yield - ensure - #before finishing, we restore the alias to the mock-up method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - remove_method :execute - alias_method :execute, :execute_with_stub - end - end - - - def method_missing(method_symbol, *arguments) - ActiveRecord::Base.connection.send(method_symbol, *arguments) - end - - def column_present?(table_name, column_name, type) - results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") - results.first && results.first['Type'] == type - end -end diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb deleted file mode 100644 index f106e14319..0000000000 --- a/activerecord/test/cases/active_schema_test_postgresql.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'cases/helper' - -class PostgresqlActiveSchemaTest < Test::Unit::TestCase - def setup - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do - alias_method :real_execute, :execute - remove_method :execute - def execute(sql, name = nil) sql end - end - end - - def teardown - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do - remove_method :execute - alias_method :execute, :real_execute - end - end - - def test_create_database_with_encoding - assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt) - assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1) - end - - private - def method_missing(method_symbol, *arguments) - ActiveRecord::Base.connection.send(method_symbol, *arguments) - end -end diff --git a/activerecord/test/cases/adapters/firebird/connection_test.rb b/activerecord/test/cases/adapters/firebird/connection_test.rb new file mode 100644 index 0000000000..f57ea686a5 --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/connection_test.rb @@ -0,0 +1,8 @@ +require "cases/helper" + +class FirebirdConnectionTest < ActiveRecord::TestCase + def test_charset_properly_set + fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection) + assert_equal 'UTF8', fb_conn.database.character_set + end +end diff --git a/activerecord/test/cases/adapters/firebird/default_test.rb b/activerecord/test/cases/adapters/firebird/default_test.rb new file mode 100644 index 0000000000..713c7e11bf --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/default_test.rb @@ -0,0 +1,16 @@ +require "cases/helper" +require 'models/default' + +class DefaultTest < ActiveRecord::TestCase + def test_default_timestamp + default = Default.new + assert_instance_of(Time, default.default_timestamp) + assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type) + + # Variance should be small; increase if required -- e.g., if test db is on + # remote host and clocks aren't synchronized. + t1 = Time.new + accepted_variance = 1.0 + assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance) + end +end diff --git a/activerecord/test/cases/adapters/firebird/migration_test.rb b/activerecord/test/cases/adapters/firebird/migration_test.rb new file mode 100644 index 0000000000..710661b9bd --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/migration_test.rb @@ -0,0 +1,124 @@ +require "cases/helper" +require 'models/course' + +class FirebirdMigrationTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + # using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain + @connection = Course.connection + @fireruby_connection = @connection.instance_variable_get(:@connection) + end + + def teardown + @connection.drop_table :foo rescue nil + @connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil + end + + def test_create_table_with_custom_sequence_name + assert_nothing_raised do + @connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert sequence_exists?('foo_custom_seq') + + assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') } + assert !sequence_exists?('foo_custom_seq') + ensure + FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil + end + + def test_create_table_without_sequence + assert_nothing_raised do + @connection.create_table(:foo, :sequence => false) do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert_nothing_raised { @connection.drop_table :foo } + + assert_nothing_raised do + @connection.create_table(:foo, :id => false) do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert_nothing_raised { @connection.drop_table :foo } + end + + def test_create_table_with_boolean_column + assert !boolean_domain_exists? + assert_nothing_raised do + @connection.create_table :foo do |f| + f.column :bar, :string + f.column :baz, :boolean + end + end + assert boolean_domain_exists? + end + + def test_add_boolean_column + assert !boolean_domain_exists? + @connection.create_table :foo do |f| + f.column :bar, :string + end + + assert_nothing_raised { @connection.add_column :foo, :baz, :boolean } + assert boolean_domain_exists? + assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type + end + + def test_change_column_to_boolean + assert !boolean_domain_exists? + # Manually create table with a SMALLINT column, which can be changed to a BOOLEAN + @connection.execute "CREATE TABLE foo (bar SMALLINT)" + assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type + + assert_nothing_raised { @connection.change_column :foo, :bar, :boolean } + assert boolean_domain_exists? + assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type + end + + def test_rename_table_with_data_and_index + @connection.create_table :foo do |f| + f.column :baz, :string, :limit => 50 + end + 100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" } + @connection.add_index :foo, :baz + + assert_nothing_raised { @connection.rename_table :foo, :bar } + assert !@connection.tables.include?("foo") + assert @connection.tables.include?("bar") + assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name + assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last + assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"] + ensure + @connection.drop_table :bar rescue nil + end + + def test_renaming_table_with_fk_constraint_raises_error + @connection.create_table :parent do |p| + p.column :name, :string + end + @connection.create_table :child do |c| + c.column :parent_id, :integer + end + @connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)" + assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant } + ensure + @connection.drop_table :child rescue nil + @connection.drop_table :descendant rescue nil + @connection.drop_table :parent rescue nil + end + + private + def boolean_domain_exists? + !@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil? + end + + def sequence_exists?(sequence_name) + FireRuby::Generator.exists?(sequence_name, @fireruby_connection) + end +end diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb new file mode 100644 index 0000000000..d7431e5158 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -0,0 +1,124 @@ +require "cases/helper" + +class ActiveSchemaTest < ActiveRecord::TestCase + def setup + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute_without_stub, :execute + remove_method :execute + def execute(sql, name = nil) return sql end + end + end + + def teardown + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + remove_method :execute + alias_method :execute, :execute_without_stub + end + 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 |*| + false + end + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" + assert_equal expected, add_index(:people, :last_name, :length => nil) + + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" + assert_equal expected, add_index(:people, :last_name, :length => 10) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) + + 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?) + end + + def test_drop_table + assert_equal "DROP TABLE `people`", drop_table(:people) + end + + if current_adapter?(:MysqlAdapter) + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end + + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) + end + end + + def test_add_column + assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string) + end + + def test_add_column_with_limit + assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32) + end + + def test_drop_table_with_specific_database + assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people') + end + + def test_add_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + end + ActiveRecord::Base.connection.add_timestamps :delete_me + assert column_present?('delete_me', 'updated_at', 'datetime') + assert column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + def test_remove_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + t.timestamps + end + ActiveRecord::Base.connection.remove_timestamps :delete_me + assert !column_present?('delete_me', 'updated_at', 'datetime') + assert !column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + private + def with_real_execute + #we need to actually modify some data, so we make execute point to the original method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute_with_stub, :execute + alias_method :execute, :execute_without_stub + end + yield + ensure + #before finishing, we restore the alias to the mock-up method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + remove_method :execute + alias_method :execute, :execute_with_stub + end + end + + + def method_missing(method_symbol, *arguments) + ActiveRecord::Base.connection.send(method_symbol, *arguments) + end + + def column_present?(table_name, column_name, type) + results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") + results.first && results.first['Type'] == type + end +end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb new file mode 100644 index 0000000000..8e4842a1b6 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -0,0 +1,64 @@ +require "cases/helper" + +class MysqlConnectionTest < ActiveRecord::TestCase + def setup + super + @connection = ActiveRecord::Base.connection + end + + def test_mysql_reconnect_attribute_after_connection_with_reconnect_true + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) + assert ActiveRecord::Base.connection.raw_connection.reconnect + end + end + + def test_mysql_reconnect_attribute_after_connection_with_reconnect_false + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) + assert !ActiveRecord::Base.connection.raw_connection.reconnect + end + end + + def test_no_automatic_reconnection_after_timeout + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + assert !@connection.active? + end + + def test_successful_reconnection_after_timeout_with_manual_reconnect + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.reconnect! + assert @connection.active? + end + + def test_successful_reconnection_after_timeout_with_verify + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.verify! + assert @connection.active? + end + + # Test that MySQL allows multiple results for stored procedures + if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) + def test_multi_results + rows = ActiveRecord::Base.connection.select_rows('CALL ten();') + assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" + end + end + + private + + def run_without_connection + original_connection = ActiveRecord::Base.remove_connection + begin + yield original_connection + ensure + ActiveRecord::Base.establish_connection(original_connection) + end + end +end diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb new file mode 100644 index 0000000000..90d8b0d923 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -0,0 +1,176 @@ +require "cases/helper" + +class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select, :class_name => 'Select' + has_one :values +end + +class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups +end + +class Values < ActiveRecord::Base + Values.table_name = 'values' +end + +class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups +end + +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class MysqlReservedWordTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + + # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table() + # will fail with these table names if these test cases fail + + create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int', + 'select'=>'id int auto_increment primary key', + 'values'=>'id int auto_increment primary key, group_id int', + 'distinct'=>'id int auto_increment primary key', + 'distincts_selects'=>'distinct_id int, select_id int' + end + + def teardown + drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order'] + end + + # create tables with reserved-word names and columns + def test_create_tables + assert_nothing_raised { + @connection.create_table :order do |t| + t.column :group, :string + end + } + end + + # rename tables with reserved-word names + def test_rename_tables + assert_nothing_raised { @connection.rename_table(:group, :order) } + end + + # alter column with a reserved-word name in a table with a reserved-word name + def test_change_columns + assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') } + #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter + assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) } + assert_nothing_raised { @connection.rename_column(:group, :order, :values) } + end + + # dump structure of table with reserved word name + def test_structure_dump + assert_nothing_raised { @connection.structure_dump } + end + + # introspect table with reserved word name + def test_introspect + assert_nothing_raised { @connection.columns(:group) } + assert_nothing_raised { @connection.indexes(:group) } + end + + #fixtures + self.use_instantiated_fixtures = true + self.use_transactional_fixtures = false + + #fixtures :group + + def test_fixtures + f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + + assert_nothing_raised { + f.each do |x| + x.delete_existing_fixtures + end + } + + assert_nothing_raised { + f.each do |x| + x.insert_fixtures + end + } + end + + #activerecord model class with reserved-word table name + def test_activerecord_model + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + x = nil + assert_nothing_raised { x = Group.new } + x.order = 'x' + assert_nothing_raised { x.save } + x.order = 'y' + assert_nothing_raised { x.save } + assert_nothing_raised { y = Group.find_by_order('y') } + assert_nothing_raised { y = Group.find(1) } + x = Group.find(1) + end + + # has_one association with reserved-word table name + def test_has_one_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + v = nil + assert_nothing_raised { v = Group.find(1).values } + assert_equal 2, v.id + end + + # belongs_to association with reserved-word table name + def test_belongs_to_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + gs = nil + assert_nothing_raised { gs = Select.find(2).groups } + assert_equal gs.length, 2 + assert(gs.collect{|x| x.id}.sort == [2, 3]) + end + + # has_and_belongs_to_many with reserved-word table name + def test_has_and_belongs_to_many + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + s = nil + assert_nothing_raised { s = Distinct.find(1).selects } + assert_equal s.length, 2 + assert(s.collect{|x|x.id}.sort == [1, 2]) + end + + # activerecord model introspection with reserved-word table and column names + def test_activerecord_introspection + assert_nothing_raised { Group.table_exists? } + assert_nothing_raised { Group.columns } + end + + # Calculations + def test_calculations_work_with_reserved_words + assert_nothing_raised { Group.count } + end + + def test_associations_work_with_reserved_words + assert_nothing_raised { Select.find(:all, :include => [:groups]) } + end + + #the following functions were added to DRY test cases + + private + # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path + def create_test_fixtures(*fixture_names) + Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) + end + + # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name + def drop_tables_directly(table_names, connection = @connection) + table_names.each do |name| + connection.execute("DROP TABLE IF EXISTS `#{name}`") + end + end + + # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns + def create_tables_directly (tables, connection = @connection) + tables.each do |table_name, column_properties| + connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )") + end + end + +end diff --git a/activerecord/test/cases/adapters/oracle/synonym_test.rb b/activerecord/test/cases/adapters/oracle/synonym_test.rb new file mode 100644 index 0000000000..b9a422a6ca --- /dev/null +++ b/activerecord/test/cases/adapters/oracle/synonym_test.rb @@ -0,0 +1,17 @@ +require "cases/helper" +require 'models/topic' +require 'models/subject' + +# confirm that synonyms work just like tables; in this case +# the "subjects" table in Oracle (defined in oci.sql) is just +# a synonym to the "topics" table + +class TestOracleSynonym < ActiveRecord::TestCase + + def test_oracle_synonym + topic = Topic.new + subject = Subject.new + assert_equal(topic.attributes, subject.attributes) + end + +end diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb new file mode 100644 index 0000000000..f106e14319 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -0,0 +1,28 @@ +require 'cases/helper' + +class PostgresqlActiveSchemaTest < Test::Unit::TestCase + def setup + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do + alias_method :real_execute, :execute + remove_method :execute + def execute(sql, name = nil) sql end + end + end + + def teardown + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do + remove_method :execute + alias_method :execute, :real_execute + end + end + + def test_create_database_with_encoding + assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt) + assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1) + end + + private + def method_missing(method_symbol, *arguments) + ActiveRecord::Base.connection.send(method_symbol, *arguments) + end +end diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb new file mode 100644 index 0000000000..5bb8fa2f93 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -0,0 +1,243 @@ +require "cases/helper" + +class PostgresqlArray < ActiveRecord::Base +end + +class PostgresqlMoney < ActiveRecord::Base +end + +class PostgresqlNumber < ActiveRecord::Base +end + +class PostgresqlTime < ActiveRecord::Base +end + +class PostgresqlNetworkAddress < ActiveRecord::Base +end + +class PostgresqlBitString < ActiveRecord::Base +end + +class PostgresqlOid < ActiveRecord::Base +end + +class PostgresqlTimestampWithZone < ActiveRecord::Base +end + +class PostgresqlDataTypeTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute("set lc_monetary = 'C'") + + @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") + @first_array = PostgresqlArray.find(1) + + @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)") + @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)") + @first_money = PostgresqlMoney.find(1) + @second_money = PostgresqlMoney.find(2) + + @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)") + @first_number = PostgresqlNumber.find(1) + + @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')") + @first_time = PostgresqlTime.find(1) + + @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") + @first_network_address = PostgresqlNetworkAddress.find(1) + + @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')") + @first_bit_string = PostgresqlBitString.find(1) + + @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 + + def test_data_type_of_array_types + assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type + assert_equal :string, @first_array.column_for_attribute(:nicknames).type + end + + def test_data_type_of_money_types + assert_equal :decimal, @first_money.column_for_attribute(:wealth).type + end + + def test_data_type_of_number_types + assert_equal :float, @first_number.column_for_attribute(:single).type + assert_equal :float, @first_number.column_for_attribute(:double).type + end + + def test_data_type_of_time_types + assert_equal :string, @first_time.column_for_attribute(:time_interval).type + end + + def test_data_type_of_network_address_types + assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type + assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type + assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type + end + + def test_data_type_of_bit_string_types + assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type + assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type + end + + def test_data_type_of_oid_types + assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type + end + + def test_array_values + assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter + assert_equal '{foo,bar,baz}', @first_array.nicknames + end + + def test_money_values + assert_equal 567.89, @first_money.wealth + assert_equal(-567.89, @second_money.wealth) + end + + def test_number_values + assert_equal 123.456, @first_number.single + assert_equal 123456.789, @first_number.double + end + + def test_time_values + assert_equal '-1 years -2 days', @first_time.time_interval + end + + def test_network_address_values + assert_equal '192.168.0.0/24', @first_network_address.cidr_address + assert_equal '172.16.1.254', @first_network_address.inet_address + assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address + end + + def test_bit_string_values + assert_equal '00010101', @first_bit_string.bit_string + assert_equal '00010101', @first_bit_string.bit_string_varying + end + + def test_oid_values + assert_equal 1234, @first_oid.obj_id + end + + def test_update_integer_array + new_value = '{32800,95000,29350,17000}' + assert @first_array.commission_by_quarter = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.commission_by_quarter, new_value + assert @first_array.commission_by_quarter = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.commission_by_quarter, new_value + end + + def test_update_text_array + new_value = '{robby,robert,rob,robbie}' + assert @first_array.nicknames = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.nicknames, new_value + assert @first_array.nicknames = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.nicknames, new_value + end + + def test_update_money + new_value = BigDecimal.new('123.45') + assert @first_money.wealth = new_value + assert @first_money.save + assert @first_money.reload + assert_equal new_value, @first_money.wealth + end + + def test_update_number + new_single = 789.012 + new_double = 789012.345 + assert @first_number.single = new_single + assert @first_number.double = new_double + assert @first_number.save + assert @first_number.reload + assert_equal @first_number.single, new_single + assert_equal @first_number.double, new_double + end + + def test_update_time + assert @first_time.time_interval = '2 years 3 minutes' + assert @first_time.save + assert @first_time.reload + assert_equal @first_time.time_interval, '2 years 00:03:00' + end + + def test_update_network_address + new_cidr_address = '10.1.2.3/32' + new_inet_address = '10.0.0.0/8' + new_mac_address = 'bc:de:f0:12:34:56' + assert @first_network_address.cidr_address = new_cidr_address + assert @first_network_address.inet_address = new_inet_address + assert @first_network_address.mac_address = new_mac_address + assert @first_network_address.save + assert @first_network_address.reload + assert_equal @first_network_address.cidr_address, new_cidr_address + assert_equal @first_network_address.inet_address, new_inet_address + assert_equal @first_network_address.mac_address, new_mac_address + end + + def test_update_bit_string + new_bit_string = '11111111' + new_bit_string_varying = 'FF' + assert @first_bit_string.bit_string = new_bit_string + assert @first_bit_string.bit_string_varying = new_bit_string_varying + assert @first_bit_string.save + assert @first_bit_string.reload + assert_equal @first_bit_string.bit_string, new_bit_string + assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying + end + + def test_update_oid + new_value = 567890 + assert @first_oid.obj_id = new_value + assert @first_oid.save + assert @first_oid.reload + assert_equal @first_oid.obj_id, new_value + end + + def test_timestamp_with_zone_values_with_rails_time_zone_support + old_tz = ActiveRecord::Base.time_zone_aware_attributes + old_default_tz = ActiveRecord::Base.default_timezone + + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + + @connection.reconnect! + + @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) + assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + ensure + ActiveRecord::Base.default_timezone = old_default_tz + ActiveRecord::Base.time_zone_aware_attributes = old_tz + @connection.reconnect! + end + + def test_timestamp_with_zone_values_without_rails_time_zone_support + old_tz = ActiveRecord::Base.time_zone_aware_attributes + old_default_tz = ActiveRecord::Base.default_timezone + + ActiveRecord::Base.time_zone_aware_attributes = false + ActiveRecord::Base.default_timezone = :local + + @connection.reconnect! + + @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) + assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + ensure + ActiveRecord::Base.default_timezone = old_default_tz + ActiveRecord::Base.time_zone_aware_attributes = old_tz + @connection.reconnect! + end +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb new file mode 100644 index 0000000000..6f372edc38 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb @@ -0,0 +1,84 @@ +require "cases/helper" + +class SchemaThing < ActiveRecord::Base +end + +class SchemaAuthorizationTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + TABLE_NAME = 'schema_things' + COLUMNS = [ + 'id serial primary key', + 'name character varying(50)' + ] + USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2'] + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute "SET search_path TO '$user',public" + set_session_auth + USERS.each do |u| + @connection.execute "CREATE USER #{u}" rescue nil + @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil + set_session_auth u + @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')" + set_session_auth + end + end + + def teardown + set_session_auth + @connection.execute "RESET search_path" + USERS.each do |u| + @connection.execute "DROP SCHEMA #{u} CASCADE" + @connection.execute "DROP USER #{u}" + end + end + + def test_schema_invisible + assert_raise(ActiveRecord::StatementInvalid) do + set_session_auth + @connection.execute "SELECT * FROM #{TABLE_NAME}" + end + end + + def test_schema_uniqueness + assert_nothing_raised do + set_session_auth + USERS.each do |u| + set_session_auth u + assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1") + set_session_auth + end + end + end + + def test_sequence_schema_caching + assert_nothing_raised do + USERS.each do |u| + set_session_auth u + st = SchemaThing.new :name => 'TEST1' + st.save! + st = SchemaThing.new :id => 5, :name => 'TEST2' + st.save! + set_session_auth + end + end + end + + def test_tables_in_current_schemas + assert !@connection.tables.include?(TABLE_NAME) + USERS.each do |u| + set_session_auth u + assert @connection.tables.include?(TABLE_NAME) + set_session_auth + end + end + + private + def set_session_auth auth = nil + @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}" + end + +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb new file mode 100644 index 0000000000..3ed9b1974d --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -0,0 +1,193 @@ +require "cases/helper" + +class SchemaTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + SCHEMA_NAME = 'test_schema' + SCHEMA2_NAME = 'test_schema2' + TABLE_NAME = 'things' + 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_A_COLUMN = 'name' + INDEX_B_COLUMN_S1 = 'email' + INDEX_B_COLUMN_S2 = 'moment' + COLUMNS = [ + 'id integer', + 'name character varying(50)', + 'email character varying(50)', + 'moment timestamp without time zone default now()' + ] + + class Thing1 < ActiveRecord::Base + set_table_name "test_schema.things" + end + + class Thing2 < ActiveRecord::Base + set_table_name "test_schema2.things" + end + + class Thing3 < ActiveRecord::Base + set_table_name 'test_schema."things.table"' + end + + class Thing4 < ActiveRecord::Base + set_table_name 'test_schema."Things"' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" + @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});" + end + + def teardown + @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE" + @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" + end + + def test_table_exists? + [Thing1, Thing2, Thing3, Thing4].each do |klass| + name = klass.table_name + assert @connection.table_exists?(name), "'#{name}' table should exist" + end + end + + def test_table_exists_wrong_schema + assert(!@connection.table_exists?("foo.things"), "table should not exist") + end + + def test_table_exists_quoted_table + assert(@connection.table_exists?('"things.table"'), "table should exist") + end + + def test_with_schema_prefixed_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") + end + end + + def test_with_schema_prefixed_capitalized_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") + end + end + + def test_with_schema_search_path + assert_nothing_raised do + with_schema_search_path(SCHEMA_NAME) do + assert_equal COLUMNS, columns(TABLE_NAME) + end + end + end + + + def test_proper_encoding_of_table_name + assert_equal '"table_name"', @connection.quote_table_name('table_name') + assert_equal '"table.name"', @connection.quote_table_name('"table.name"') + assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name') + assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"') + assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name') + assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"') + end + + def test_classes_with_qualified_schema_name + assert_equal 0, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 0, Thing4.count + + Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 1, Thing4.count + end + + def test_raise_on_unquoted_schema_name + assert_raise(ActiveRecord::StatementInvalid) do + with_schema_search_path '$user,public' + end + end + + def test_without_schema_search_path + assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } + end + + def test_ignore_nil_schema_search_path + assert_nothing_raised { with_schema_search_path nil } + end + + def test_dump_indexes_for_schema_one + do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1) + end + + def test_dump_indexes_for_schema_two + do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) + end + + def test_with_uppercase_index_name + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} + + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"} + ActiveRecord::Base.connection.schema_search_path = "public" + end + + private + def columns(table_name) + @connection.send(:column_definitions, table_name).map do |name, type, default| + "#{name} #{type}" + (default ? " default #{default}" : '') + end + end + + def with_schema_search_path(schema_search_path) + @connection.schema_search_path = schema_search_path + yield if block_given? + ensure + @connection.schema_search_path = "'$user', public" + end + + def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name) + with_schema_search_path(this_schema_name) do + indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name} + assert_equal 2,indexes.size + + do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name) + do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name) + end + end + + def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column) + assert_equal TABLE_NAME, this_index.table + assert_equal 1, this_index.columns.size + assert_equal this_index_column, this_index.columns[0] + assert_equal this_index_name, this_index.name + end +end diff --git a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb new file mode 100644 index 0000000000..575b4806c1 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb @@ -0,0 +1,80 @@ +require "cases/helper" + +class CopyTableTest < ActiveRecord::TestCase + fixtures :customers, :companies, :comments + + def setup + @connection = ActiveRecord::Base.connection + class << @connection + public :copy_table, :table_structure, :indexes + end + end + + def test_copy_table(from = 'customers', to = 'customers2', options = {}) + assert_nothing_raised {copy_table(from, to, options)} + assert_equal row_count(from), row_count(to) + + if block_given? + yield from, to, options + else + assert_equal column_names(from), column_names(to) + end + + @connection.drop_table(to) rescue nil + end + + def test_copy_table_renaming_column + test_copy_table('customers', 'customers2', + :rename => {'name' => 'person_name'}) do |from, to, options| + expected = column_values(from, 'name') + assert_equal expected, column_values(to, 'person_name') + assert expected.any?, "No values in table: #{expected.inspect}" + end + end + + def test_copy_table_with_index + test_copy_table('comments', 'comments_with_index') do + @connection.add_index('comments_with_index', ['post_id', 'type']) + test_copy_table('comments_with_index', 'comments_with_index2') do + assert_equal table_indexes_without_name('comments_with_index'), + table_indexes_without_name('comments_with_index2') + end + end + end + + def test_copy_table_without_primary_key + test_copy_table('developers_projects', 'programmers_projects') + end + + def test_copy_table_with_id_col_that_is_not_primary_key + test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| + original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } + copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } + assert_equal original_id.type, copied_id.type + assert_equal original_id.sql_type, copied_id.sql_type + assert_equal original_id.limit, copied_id.limit + assert_equal original_id.primary, copied_id.primary + end + end + +protected + def copy_table(from, to, options = {}) + @connection.copy_table(from, to, {:temporary => true}.merge(options)) + end + + def column_names(table) + @connection.table_structure(table).map {|column| column['name']} + end + + def column_values(table, column) + @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} + end + + def table_indexes_without_name(table) + @connection.indexes('comments_with_index').delete(:name) + end + + def row_count(table) + @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] + end +end diff --git a/activerecord/test/cases/connection_test_firebird.rb b/activerecord/test/cases/connection_test_firebird.rb deleted file mode 100644 index f57ea686a5..0000000000 --- a/activerecord/test/cases/connection_test_firebird.rb +++ /dev/null @@ -1,8 +0,0 @@ -require "cases/helper" - -class FirebirdConnectionTest < ActiveRecord::TestCase - def test_charset_properly_set - fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection) - assert_equal 'UTF8', fb_conn.database.character_set - end -end diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb deleted file mode 100644 index 8e4842a1b6..0000000000 --- a/activerecord/test/cases/connection_test_mysql.rb +++ /dev/null @@ -1,64 +0,0 @@ -require "cases/helper" - -class MysqlConnectionTest < ActiveRecord::TestCase - def setup - super - @connection = ActiveRecord::Base.connection - end - - def test_mysql_reconnect_attribute_after_connection_with_reconnect_true - run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) - assert ActiveRecord::Base.connection.raw_connection.reconnect - end - end - - def test_mysql_reconnect_attribute_after_connection_with_reconnect_false - run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) - assert !ActiveRecord::Base.connection.raw_connection.reconnect - end - end - - def test_no_automatic_reconnection_after_timeout - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - assert !@connection.active? - end - - def test_successful_reconnection_after_timeout_with_manual_reconnect - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - @connection.reconnect! - assert @connection.active? - end - - def test_successful_reconnection_after_timeout_with_verify - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - @connection.verify! - assert @connection.active? - end - - # Test that MySQL allows multiple results for stored procedures - if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) - def test_multi_results - rows = ActiveRecord::Base.connection.select_rows('CALL ten();') - assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" - end - end - - private - - def run_without_connection - original_connection = ActiveRecord::Base.remove_connection - begin - yield original_connection - ensure - ActiveRecord::Base.establish_connection(original_connection) - end - end -end diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/copy_table_test_sqlite.rb deleted file mode 100644 index 575b4806c1..0000000000 --- a/activerecord/test/cases/copy_table_test_sqlite.rb +++ /dev/null @@ -1,80 +0,0 @@ -require "cases/helper" - -class CopyTableTest < ActiveRecord::TestCase - fixtures :customers, :companies, :comments - - def setup - @connection = ActiveRecord::Base.connection - class << @connection - public :copy_table, :table_structure, :indexes - end - end - - def test_copy_table(from = 'customers', to = 'customers2', options = {}) - assert_nothing_raised {copy_table(from, to, options)} - assert_equal row_count(from), row_count(to) - - if block_given? - yield from, to, options - else - assert_equal column_names(from), column_names(to) - end - - @connection.drop_table(to) rescue nil - end - - def test_copy_table_renaming_column - test_copy_table('customers', 'customers2', - :rename => {'name' => 'person_name'}) do |from, to, options| - expected = column_values(from, 'name') - assert_equal expected, column_values(to, 'person_name') - assert expected.any?, "No values in table: #{expected.inspect}" - end - end - - def test_copy_table_with_index - test_copy_table('comments', 'comments_with_index') do - @connection.add_index('comments_with_index', ['post_id', 'type']) - test_copy_table('comments_with_index', 'comments_with_index2') do - assert_equal table_indexes_without_name('comments_with_index'), - table_indexes_without_name('comments_with_index2') - end - end - end - - def test_copy_table_without_primary_key - test_copy_table('developers_projects', 'programmers_projects') - end - - def test_copy_table_with_id_col_that_is_not_primary_key - test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| - original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } - copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } - assert_equal original_id.type, copied_id.type - assert_equal original_id.sql_type, copied_id.sql_type - assert_equal original_id.limit, copied_id.limit - assert_equal original_id.primary, copied_id.primary - end - end - -protected - def copy_table(from, to, options = {}) - @connection.copy_table(from, to, {:temporary => true}.merge(options)) - end - - def column_names(table) - @connection.table_structure(table).map {|column| column['name']} - end - - def column_values(table, column) - @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} - end - - def table_indexes_without_name(table) - @connection.indexes('comments_with_index').delete(:name) - end - - def row_count(table) - @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] - end -end diff --git a/activerecord/test/cases/datatype_test_postgresql.rb b/activerecord/test/cases/datatype_test_postgresql.rb deleted file mode 100644 index 3c2d9fb7bd..0000000000 --- a/activerecord/test/cases/datatype_test_postgresql.rb +++ /dev/null @@ -1,243 +0,0 @@ -require "cases/helper" - -class PostgresqlArray < ActiveRecord::Base -end - -class PostgresqlMoney < ActiveRecord::Base -end - -class PostgresqlNumber < ActiveRecord::Base -end - -class PostgresqlTime < ActiveRecord::Base -end - -class PostgresqlNetworkAddress < ActiveRecord::Base -end - -class PostgresqlBitString < ActiveRecord::Base -end - -class PostgresqlOid < ActiveRecord::Base -end - -class PostgresqlTimestampWithZone < ActiveRecord::Base -end - -class PostgresqlDataTypeTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute("set lc_monetary = 'C'") - - @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") - @first_array = PostgresqlArray.find(1) - - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)") - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)") - @first_money = PostgresqlMoney.find(1) - @second_money = PostgresqlMoney.find(2) - - @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)") - @first_number = PostgresqlNumber.find(1) - - @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')") - @first_time = PostgresqlTime.find(1) - - @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") - @first_network_address = PostgresqlNetworkAddress.find(1) - - @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')") - @first_bit_string = PostgresqlBitString.find(1) - - @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 - - def test_data_type_of_array_types - assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type - assert_equal :string, @first_array.column_for_attribute(:nicknames).type - end - - def test_data_type_of_money_types - assert_equal :decimal, @first_money.column_for_attribute(:wealth).type - end - - def test_data_type_of_number_types - assert_equal :float, @first_number.column_for_attribute(:single).type - assert_equal :float, @first_number.column_for_attribute(:double).type - end - - def test_data_type_of_time_types - assert_equal :string, @first_time.column_for_attribute(:time_interval).type - end - - def test_data_type_of_network_address_types - assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type - assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type - assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type - end - - def test_data_type_of_bit_string_types - assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type - assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type - end - - def test_data_type_of_oid_types - assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type - end - - def test_array_values - assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter - assert_equal '{foo,bar,baz}', @first_array.nicknames - end - - def test_money_values - assert_equal 567.89, @first_money.wealth - assert_equal(-567.89, @second_money.wealth) - end - - def test_number_values - assert_equal 123.456, @first_number.single - assert_equal 123456.789, @first_number.double - end - - def test_time_values - assert_equal '-1 years -2 days', @first_time.time_interval - end - - def test_network_address_values - assert_equal '192.168.0.0/24', @first_network_address.cidr_address - assert_equal '172.16.1.254', @first_network_address.inet_address - assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address - end - - def test_bit_string_values - assert_equal '00010101', @first_bit_string.bit_string - assert_equal '00010101', @first_bit_string.bit_string_varying - end - - def test_oid_values - assert_equal 1234, @first_oid.obj_id - end - - def test_update_integer_array - new_value = '{32800,95000,29350,17000}' - assert @first_array.commission_by_quarter = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.commission_by_quarter, new_value - assert @first_array.commission_by_quarter = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.commission_by_quarter, new_value - end - - def test_update_text_array - new_value = '{robby,robert,rob,robbie}' - assert @first_array.nicknames = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.nicknames, new_value - assert @first_array.nicknames = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.nicknames, new_value - end - - def test_update_money - new_value = BigDecimal.new('123.45') - assert @first_money.wealth = new_value - assert @first_money.save - assert @first_money.reload - assert_equal new_value, @first_money.wealth - end - - def test_update_number - new_single = 789.012 - new_double = 789012.345 - assert @first_number.single = new_single - assert @first_number.double = new_double - assert @first_number.save - assert @first_number.reload - assert_equal @first_number.single, new_single - assert_equal @first_number.double, new_double - end - - def test_update_time - assert @first_time.time_interval = '2 years 3 minutes' - assert @first_time.save - assert @first_time.reload - assert_equal @first_time.time_interval, '2 years 00:03:00' - end - - def test_update_network_address - new_cidr_address = '10.1.2.3/32' - new_inet_address = '10.0.0.0/8' - new_mac_address = 'bc:de:f0:12:34:56' - assert @first_network_address.cidr_address = new_cidr_address - assert @first_network_address.inet_address = new_inet_address - assert @first_network_address.mac_address = new_mac_address - assert @first_network_address.save - assert @first_network_address.reload - assert_equal @first_network_address.cidr_address, new_cidr_address - assert_equal @first_network_address.inet_address, new_inet_address - assert_equal @first_network_address.mac_address, new_mac_address - end - - def test_update_bit_string - new_bit_string = '11111111' - new_bit_string_varying = 'FF' - assert @first_bit_string.bit_string = new_bit_string - assert @first_bit_string.bit_string_varying = new_bit_string_varying - assert @first_bit_string.save - assert @first_bit_string.reload - assert_equal @first_bit_string.bit_string, new_bit_string - assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying - end - - def test_update_oid - new_value = 567890 - assert @first_oid.obj_id = new_value - assert @first_oid.save - assert @first_oid.reload - assert_equal @first_oid.obj_id, new_value - end - - def test_timestamp_with_zone_values_with_rails_time_zone_support - old_tz = ActiveRecord::Base.time_zone_aware_attributes - old_default_tz = ActiveRecord::Base.default_timezone - - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - - @connection.reconnect! - - @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) - assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time - ensure - ActiveRecord::Base.default_timezone = old_default_tz - ActiveRecord::Base.time_zone_aware_attributes = old_tz - @connection.reconnect! - end - - def test_timestamp_with_zone_values_without_rails_time_zone_support - old_tz = ActiveRecord::Base.time_zone_aware_attributes - old_default_tz = ActiveRecord::Base.default_timezone - - ActiveRecord::Base.time_zone_aware_attributes = false - ActiveRecord::Base.default_timezone = :local - - @connection.reconnect! - - @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) - assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time - ensure - ActiveRecord::Base.default_timezone = old_default_tz - ActiveRecord::Base.time_zone_aware_attributes = old_tz - @connection.reconnect! - end -end diff --git a/activerecord/test/cases/default_test_firebird.rb b/activerecord/test/cases/default_test_firebird.rb deleted file mode 100644 index 713c7e11bf..0000000000 --- a/activerecord/test/cases/default_test_firebird.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "cases/helper" -require 'models/default' - -class DefaultTest < ActiveRecord::TestCase - def test_default_timestamp - default = Default.new - assert_instance_of(Time, default.default_timestamp) - assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type) - - # Variance should be small; increase if required -- e.g., if test db is on - # remote host and clocks aren't synchronized. - t1 = Time.new - accepted_variance = 1.0 - assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance) - end -end diff --git a/activerecord/test/cases/migration_test_firebird.rb b/activerecord/test/cases/migration_test_firebird.rb deleted file mode 100644 index 710661b9bd..0000000000 --- a/activerecord/test/cases/migration_test_firebird.rb +++ /dev/null @@ -1,124 +0,0 @@ -require "cases/helper" -require 'models/course' - -class FirebirdMigrationTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def setup - # using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain - @connection = Course.connection - @fireruby_connection = @connection.instance_variable_get(:@connection) - end - - def teardown - @connection.drop_table :foo rescue nil - @connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil - end - - def test_create_table_with_custom_sequence_name - assert_nothing_raised do - @connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert sequence_exists?('foo_custom_seq') - - assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') } - assert !sequence_exists?('foo_custom_seq') - ensure - FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil - end - - def test_create_table_without_sequence - assert_nothing_raised do - @connection.create_table(:foo, :sequence => false) do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert_nothing_raised { @connection.drop_table :foo } - - assert_nothing_raised do - @connection.create_table(:foo, :id => false) do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert_nothing_raised { @connection.drop_table :foo } - end - - def test_create_table_with_boolean_column - assert !boolean_domain_exists? - assert_nothing_raised do - @connection.create_table :foo do |f| - f.column :bar, :string - f.column :baz, :boolean - end - end - assert boolean_domain_exists? - end - - def test_add_boolean_column - assert !boolean_domain_exists? - @connection.create_table :foo do |f| - f.column :bar, :string - end - - assert_nothing_raised { @connection.add_column :foo, :baz, :boolean } - assert boolean_domain_exists? - assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type - end - - def test_change_column_to_boolean - assert !boolean_domain_exists? - # Manually create table with a SMALLINT column, which can be changed to a BOOLEAN - @connection.execute "CREATE TABLE foo (bar SMALLINT)" - assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type - - assert_nothing_raised { @connection.change_column :foo, :bar, :boolean } - assert boolean_domain_exists? - assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type - end - - def test_rename_table_with_data_and_index - @connection.create_table :foo do |f| - f.column :baz, :string, :limit => 50 - end - 100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" } - @connection.add_index :foo, :baz - - assert_nothing_raised { @connection.rename_table :foo, :bar } - assert !@connection.tables.include?("foo") - assert @connection.tables.include?("bar") - assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name - assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last - assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"] - ensure - @connection.drop_table :bar rescue nil - end - - def test_renaming_table_with_fk_constraint_raises_error - @connection.create_table :parent do |p| - p.column :name, :string - end - @connection.create_table :child do |c| - c.column :parent_id, :integer - end - @connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)" - assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant } - ensure - @connection.drop_table :child rescue nil - @connection.drop_table :descendant rescue nil - @connection.drop_table :parent rescue nil - end - - private - def boolean_domain_exists? - !@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil? - end - - def sequence_exists?(sequence_name) - FireRuby::Generator.exists?(sequence_name, @fireruby_connection) - end -end diff --git a/activerecord/test/cases/reserved_word_test_mysql.rb b/activerecord/test/cases/reserved_word_test_mysql.rb deleted file mode 100644 index 90d8b0d923..0000000000 --- a/activerecord/test/cases/reserved_word_test_mysql.rb +++ /dev/null @@ -1,176 +0,0 @@ -require "cases/helper" - -class Group < ActiveRecord::Base - Group.table_name = 'group' - belongs_to :select, :class_name => 'Select' - has_one :values -end - -class Select < ActiveRecord::Base - Select.table_name = 'select' - has_many :groups -end - -class Values < ActiveRecord::Base - Values.table_name = 'values' -end - -class Distinct < ActiveRecord::Base - Distinct.table_name = 'distinct' - has_and_belongs_to_many :selects - has_many :values, :through => :groups -end - -# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with -# reserved word names (ie: group, order, values, etc...) -class MysqlReservedWordTest < ActiveRecord::TestCase - def setup - @connection = ActiveRecord::Base.connection - - # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table() - # will fail with these table names if these test cases fail - - create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int', - 'select'=>'id int auto_increment primary key', - 'values'=>'id int auto_increment primary key, group_id int', - 'distinct'=>'id int auto_increment primary key', - 'distincts_selects'=>'distinct_id int, select_id int' - end - - def teardown - drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order'] - end - - # create tables with reserved-word names and columns - def test_create_tables - assert_nothing_raised { - @connection.create_table :order do |t| - t.column :group, :string - end - } - end - - # rename tables with reserved-word names - def test_rename_tables - assert_nothing_raised { @connection.rename_table(:group, :order) } - end - - # alter column with a reserved-word name in a table with a reserved-word name - def test_change_columns - assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') } - #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter - assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) } - assert_nothing_raised { @connection.rename_column(:group, :order, :values) } - end - - # dump structure of table with reserved word name - def test_structure_dump - assert_nothing_raised { @connection.structure_dump } - end - - # introspect table with reserved word name - def test_introspect - assert_nothing_raised { @connection.columns(:group) } - assert_nothing_raised { @connection.indexes(:group) } - end - - #fixtures - self.use_instantiated_fixtures = true - self.use_transactional_fixtures = false - - #fixtures :group - - def test_fixtures - f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - - assert_nothing_raised { - f.each do |x| - x.delete_existing_fixtures - end - } - - assert_nothing_raised { - f.each do |x| - x.insert_fixtures - end - } - end - - #activerecord model class with reserved-word table name - def test_activerecord_model - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - x = nil - assert_nothing_raised { x = Group.new } - x.order = 'x' - assert_nothing_raised { x.save } - x.order = 'y' - assert_nothing_raised { x.save } - assert_nothing_raised { y = Group.find_by_order('y') } - assert_nothing_raised { y = Group.find(1) } - x = Group.find(1) - end - - # has_one association with reserved-word table name - def test_has_one_associations - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - v = nil - assert_nothing_raised { v = Group.find(1).values } - assert_equal 2, v.id - end - - # belongs_to association with reserved-word table name - def test_belongs_to_associations - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - gs = nil - assert_nothing_raised { gs = Select.find(2).groups } - assert_equal gs.length, 2 - assert(gs.collect{|x| x.id}.sort == [2, 3]) - end - - # has_and_belongs_to_many with reserved-word table name - def test_has_and_belongs_to_many - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - s = nil - assert_nothing_raised { s = Distinct.find(1).selects } - assert_equal s.length, 2 - assert(s.collect{|x|x.id}.sort == [1, 2]) - end - - # activerecord model introspection with reserved-word table and column names - def test_activerecord_introspection - assert_nothing_raised { Group.table_exists? } - assert_nothing_raised { Group.columns } - end - - # Calculations - def test_calculations_work_with_reserved_words - assert_nothing_raised { Group.count } - end - - def test_associations_work_with_reserved_words - assert_nothing_raised { Select.find(:all, :include => [:groups]) } - end - - #the following functions were added to DRY test cases - - private - # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path - def create_test_fixtures(*fixture_names) - Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) - end - - # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name - def drop_tables_directly(table_names, connection = @connection) - table_names.each do |name| - connection.execute("DROP TABLE IF EXISTS `#{name}`") - end - end - - # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns - def create_tables_directly (tables, connection = @connection) - tables.each do |table_name, column_properties| - connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )") - end - end - -end diff --git a/activerecord/test/cases/schema_authorization_test_postgresql.rb b/activerecord/test/cases/schema_authorization_test_postgresql.rb deleted file mode 100644 index 2860f1ad48..0000000000 --- a/activerecord/test/cases/schema_authorization_test_postgresql.rb +++ /dev/null @@ -1,84 +0,0 @@ -require "cases/helper" - -class SchemaThing < ActiveRecord::Base -end - -class SchemaAuthorizationTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - TABLE_NAME = 'schema_things' - COLUMNS = [ - 'id serial primary key', - 'name character varying(50)' - ] - USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2'] - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute "SET search_path TO '$user',public" - set_session_auth - USERS.each do |u| - @connection.execute "CREATE USER #{u}" rescue nil - @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil - set_session_auth u - @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')" - set_session_auth - end - end - - def teardown - set_session_auth - @connection.execute "RESET search_path" - USERS.each do |u| - @connection.execute "DROP SCHEMA #{u} CASCADE" - @connection.execute "DROP USER #{u}" - end - end - - def test_schema_invisible - assert_raise(ActiveRecord::StatementInvalid) do - set_session_auth - @connection.execute "SELECT * FROM #{TABLE_NAME}" - end - end - - def test_schema_uniqueness - assert_nothing_raised do - set_session_auth - USERS.each do |u| - set_session_auth u - assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1") - set_session_auth - end - end - end - - def test_sequence_schema_caching - assert_nothing_raised do - USERS.each do |u| - set_session_auth u - st = SchemaThing.new :name => 'TEST1' - st.save! - st = SchemaThing.new :id => 5, :name => 'TEST2' - st.save! - set_session_auth - end - end - end - - def test_tables_in_current_schemas - assert !@connection.tables.include?(TABLE_NAME) - USERS.each do |u| - set_session_auth u - assert @connection.tables.include?(TABLE_NAME) - set_session_auth - end - end - - private - def set_session_auth auth = nil - @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}" - end - -end diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb deleted file mode 100644 index 3ed9b1974d..0000000000 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ /dev/null @@ -1,193 +0,0 @@ -require "cases/helper" - -class SchemaTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - SCHEMA_NAME = 'test_schema' - SCHEMA2_NAME = 'test_schema2' - TABLE_NAME = 'things' - 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_A_COLUMN = 'name' - INDEX_B_COLUMN_S1 = 'email' - INDEX_B_COLUMN_S2 = 'moment' - COLUMNS = [ - 'id integer', - 'name character varying(50)', - 'email character varying(50)', - 'moment timestamp without time zone default now()' - ] - - class Thing1 < ActiveRecord::Base - set_table_name "test_schema.things" - end - - class Thing2 < ActiveRecord::Base - set_table_name "test_schema2.things" - end - - class Thing3 < ActiveRecord::Base - set_table_name 'test_schema."things.table"' - end - - class Thing4 < ActiveRecord::Base - set_table_name 'test_schema."Things"' - end - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" - @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" - @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" - @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});" - end - - def teardown - @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE" - @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" - end - - def test_table_exists? - [Thing1, Thing2, Thing3, Thing4].each do |klass| - name = klass.table_name - assert @connection.table_exists?(name), "'#{name}' table should exist" - end - end - - def test_table_exists_wrong_schema - assert(!@connection.table_exists?("foo.things"), "table should not exist") - end - - def test_table_exists_quoted_table - assert(@connection.table_exists?('"things.table"'), "table should exist") - end - - def test_with_schema_prefixed_table_name - assert_nothing_raised do - assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") - end - end - - def test_with_schema_prefixed_capitalized_table_name - assert_nothing_raised do - assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") - end - end - - def test_with_schema_search_path - assert_nothing_raised do - with_schema_search_path(SCHEMA_NAME) do - assert_equal COLUMNS, columns(TABLE_NAME) - end - end - end - - - def test_proper_encoding_of_table_name - assert_equal '"table_name"', @connection.quote_table_name('table_name') - assert_equal '"table.name"', @connection.quote_table_name('"table.name"') - assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name') - assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"') - assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name') - assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"') - end - - def test_classes_with_qualified_schema_name - assert_equal 0, Thing1.count - assert_equal 0, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 0, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 1, Thing3.count - assert_equal 0, Thing4.count - - Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 1, Thing3.count - assert_equal 1, Thing4.count - end - - def test_raise_on_unquoted_schema_name - assert_raise(ActiveRecord::StatementInvalid) do - with_schema_search_path '$user,public' - end - end - - def test_without_schema_search_path - assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } - end - - def test_ignore_nil_schema_search_path - assert_nothing_raised { with_schema_search_path nil } - end - - def test_dump_indexes_for_schema_one - do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1) - end - - def test_dump_indexes_for_schema_two - do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) - end - - def test_with_uppercase_index_name - ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} - - ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME - assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"} - ActiveRecord::Base.connection.schema_search_path = "public" - end - - private - def columns(table_name) - @connection.send(:column_definitions, table_name).map do |name, type, default| - "#{name} #{type}" + (default ? " default #{default}" : '') - end - end - - def with_schema_search_path(schema_search_path) - @connection.schema_search_path = schema_search_path - yield if block_given? - ensure - @connection.schema_search_path = "'$user', public" - end - - def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name) - with_schema_search_path(this_schema_name) do - indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name} - assert_equal 2,indexes.size - - do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name) - do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name) - end - end - - def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column) - assert_equal TABLE_NAME, this_index.table - assert_equal 1, this_index.columns.size - assert_equal this_index_column, this_index.columns[0] - assert_equal this_index_name, this_index.name - end -end diff --git a/activerecord/test/cases/synonym_test_oracle.rb b/activerecord/test/cases/synonym_test_oracle.rb deleted file mode 100644 index b9a422a6ca..0000000000 --- a/activerecord/test/cases/synonym_test_oracle.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "cases/helper" -require 'models/topic' -require 'models/subject' - -# confirm that synonyms work just like tables; in this case -# the "subjects" table in Oracle (defined in oci.sql) is just -# a synonym to the "topics" table - -class TestOracleSynonym < ActiveRecord::TestCase - - def test_oracle_synonym - topic = Topic.new - subject = Subject.new - assert_equal(topic.attributes, subject.attributes) - end - -end -- cgit v1.2.3 From a2513aea07255877b66b846fcf7ebb35629199df Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 19:22:45 -0300 Subject: Test for concatenated orders added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4972] Signed-off-by: José Valim --- activerecord/test/cases/relations_test.rb | 6 ++++++ activerecord/test/fixtures/topics.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5b1c6b8f22..821b4585a7 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -110,6 +110,12 @@ 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_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a 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 -- cgit v1.2.3 From 65aa6a7db16b7387d5a3bf1ad7f5a602804a2f21 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 19:34:51 -0300 Subject: reorder method added to ActiveRelation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4972 state:committed] Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++++++ activerecord/test/cases/relations_test.rb | 6 ++++++ 2 files changed, 14 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4dbb30c777..f7c4c7991b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -21,6 +21,14 @@ module ActiveRecord CEVAL end + def reorder(*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.order_values = value if value.present? + new_relation + end + def select(*args) if block_given? to_a.select { |*block_args| yield(*block_args) } diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 821b4585a7..d37f7bd09c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -116,6 +116,12 @@ class RelationTest < ActiveRecord::TestCase 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 -- cgit v1.2.3 From 4464d10e68045e6723d4eb62734213c4296ef339 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 15:49:15 -0700 Subject: index dump should not include full text indexes. Thanks Ken Mayer for the original patch! [#4949 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/connection_adapters/postgresql_adapter.rb | 11 +++-------- activerecord/test/cases/adapters/postgresql/schema_test.rb | 4 ++++ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ef58a32074..26d88158d3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -609,9 +609,7 @@ 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(" ") @@ -625,11 +623,8 @@ module ActiveRecord SQL column_names = indkey.map {|attnum| columns[attnum] } - IndexDefinition.new(table_name, index_name, unique, column_names) - - end - - indexes + column_names.compact.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) + end.compact end # Returns the list of all column definitions for a table. diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 3ed9b1974d..a5c3e69af9 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.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 -- cgit v1.2.3 From 11ff3da5f4fe71a8d93180ab9fa69c6190a2e26e Mon Sep 17 00:00:00 2001 From: Andrew White Date: Thu, 18 Mar 2010 16:49:23 +0000 Subject: Add column and index query methods to ActiveRecord::Schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4219 state:resolved] Signed-off-by: José Valim --- .../abstract/schema_definitions.rb | 10 ++ .../abstract/schema_statements.rb | 57 ++++++++++- .../test/cases/active_schema_test_mysql.rb | 6 +- activerecord/test/cases/migration_test.rb | 108 ++++++++++++++++++++- 4 files changed, 170 insertions(+), 11 deletions(-) (limited to 'activerecord') 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 638e5d7236..5625dba45f 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,59 @@ 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 = nil) + column_name = column_name.to_s + if type + if options + sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale]) + columns(table_name).any?{ |c| c.name == column_name && c.sql_type == sql_type } + else + columns(table_name).any?{ |c| c.name == column_name && c.type == type } + end + else + columns(table_name).any?{ |c| c.name == column_name } + end + end + # Creates a new table with the name +table_name+. +table_name+ may either # be a String or a Symbol. # @@ -293,7 +342,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 @@ -314,7 +363,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 @@ -351,11 +400,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/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index d7431e5158..6e6645511c 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.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/migration_test.rb b/activerecord/test/cases/migration_test.rb index 9ece2fbab1..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 @@ -974,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? @@ -1684,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, {}) -- cgit v1.2.3 From 47134a04bbce216f9c31473b1a0c1a077d624692 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 20:31:10 -0300 Subject: blocks removed from all the ActiveRelation query_methods, extend method added instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 9 +++++++++ activerecord/lib/active_record/relation/query_methods.rb | 12 ++++-------- activerecord/test/cases/relations_test.rb | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a51b317dc3..fd0660a138 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -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/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f7c4c7991b..ff5bce9d8b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,9 +11,8 @@ module ActiveRecord next if [:where, :having, :select].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) + def #{query_method}(*args) 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 @@ -21,9 +20,8 @@ module ActiveRecord CEVAL end - def reorder(*args, &block) + def reorder(*args) 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.order_values = value if value.present? new_relation @@ -42,9 +40,8 @@ module ActiveRecord [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) + def #{query_method}(*args) 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 @@ -56,9 +53,8 @@ module ActiveRecord attr_accessor :"#{query_method}_value" class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(value = true, &block) + def #{query_method}(value = true) new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? new_relation.#{query_method}_value = value new_relation end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index d37f7bd09c..d27a69ea1c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -618,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 -- cgit v1.2.3 From 6e655732226eef6fa04d8fc0c4ee1f0436688c49 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 16:32:09 -0700 Subject: refactoring the postgres adapter index method to avoid inject and use values_at. [#4976 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 26d88158d3..2fe2ae7136 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -615,15 +615,15 @@ module ActiveRecord 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] } - column_names.compact.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) + column_names = columns.values_at(*indkey).compact + column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) end.compact end -- cgit v1.2.3 From 5b91c97763f1183f131e46e51ed33129c6b1edcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 22:37:12 +0200 Subject: Create a little bit less objects in ARel. --- activerecord/lib/active_record/relation/query_methods.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index ff5bce9d8b..caa55929f4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -12,18 +12,20 @@ module ActiveRecord next if [:where, :having, :select].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.#{query_method}_values += value if value.present? + new_relation.#{query_method}_values += args if args.present? new_relation end CEVAL end def reorder(*args) + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.order_values = value if value.present? + new_relation.order_values = args if args.present? new_relation end @@ -31,9 +33,10 @@ module ActiveRecord if block_given? to_a.select { |*block_args| yield(*block_args) } else + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.select_values += value if value.present? + new_relation.select_values += args if args.present? new_relation end end -- cgit v1.2.3 From 100d2282e306f68d8ce0324128da1506eafc43a2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Jun 2010 13:34:50 -0700 Subject: adding adapter tests, avoiding private apis, fixing code in 1.9 [#4986 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../connection_adapters/sqlite3_adapter.rb | 10 ++-- .../cases/adapters/sqlite/sqlite3_adapter_test.rb | 56 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb (limited to 'activerecord') 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/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 -- cgit v1.2.3 From a39f2657b1ae8d61c6018766f8d250b1401beace Mon Sep 17 00:00:00 2001 From: Evgeniy Dolzhenko Date: Thu, 27 May 2010 03:22:45 -0700 Subject: Add module_eval missing file_name and line_number args [#4712 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') 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 -- cgit v1.2.3 From 64fee27a554151eb87b2350c3834bbf8812520c8 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 17:43:40 -0300 Subject: Removes useless flatten --- activerecord/lib/active_record/relation/query_methods.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index caa55929f4..201a4799a4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -9,10 +9,9 @@ module ActiveRecord (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) + next if [:where, :having, :select, :joins].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.#{query_method}_values += args if args.present? @@ -22,7 +21,6 @@ module ActiveRecord end def reorder(*args) - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.order_values = args if args.present? @@ -33,7 +31,6 @@ module ActiveRecord if block_given? to_a.select { |*block_args| yield(*block_args) } else - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.select_values += args if args.present? @@ -41,6 +38,14 @@ module ActiveRecord end end + def joins(*args) + args.flatten! + args.reject! { |a| a.blank? } + new_relation = clone + new_relation.joins_values += args if args.present? + new_relation + end + [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) -- cgit v1.2.3 From 8f358f397f11a87c649f76644690461f0017cbff Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 20:12:23 -0300 Subject: Refactor of column_exists? method and this works with PostgreSQL --- .../connection_adapters/abstract/schema_statements.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'activerecord') 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 5625dba45f..0216a8f4ac 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -63,18 +63,12 @@ module ActiveRecord # # # 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 = nil) - column_name = column_name.to_s - if type - if options - sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale]) - columns(table_name).any?{ |c| c.name == column_name && c.sql_type == sql_type } - else - columns(table_name).any?{ |c| c.name == column_name && c.type == type } - end - else - columns(table_name).any?{ |c| c.name == column_name } - end + 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 -- cgit v1.2.3 From 8d9545389f6a53dcd1ebae28c8b966c296da95f2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 23:30:43 -0300 Subject: Refactor: metaprogramming here it's confusing and make use of tap Signed-off-by: Jeremy Kemper --- .../lib/active_record/relation/query_methods.rb | 137 +++++++++++---------- 1 file changed, 73 insertions(+), 64 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 201a4799a4..015ca8c24c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,89 +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, :joins].include?(query_method) - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args) - args.reject! { |a| a.blank? } - new_relation = clone - new_relation.#{query_method}_values += args if args.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 - def reorder(*args) - args.reject! { |a| a.blank? } - new_relation = clone - new_relation.order_values = args if args.present? - new_relation - end + def includes(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.includes_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? } - new_relation = clone - new_relation.select_values += args if args.present? - new_relation - end - end + def eager_load(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.eager_load_values += args if args.present? } + end - def joins(*args) - args.flatten! + 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? } - new_relation = clone - new_relation.joins_values += args if args.present? - new_relation + clone.tap { |r| r.select_values += args if args.present? } end + end - [:where, :having].each do |query_method| - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args) - new_relation = clone - value = build_where(*args) - new_relation.#{query_method}_values += Array.wrap(value) if value.present? - new_relation - end - CEVAL - end + def group(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.group_values += args if args.present? } + end - ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method| - attr_accessor :"#{query_method}_value" + def order(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values += args if args.present? } + end - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(value = true) - new_relation = clone - new_relation.#{query_method}_value = value - new_relation - end - CEVAL - end + def reorder(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values = args if args.present? } end - def extending(*modules) - new_relation = clone - new_relation.send :apply_modules, *modules - new_relation + def joins(*args) + args.flatten! + args.reject! { |a| a.blank? } + clone.tap { |r| r.joins_values += args if args.present? } end - def lock(locks = true, &block) - relation = clone - relation.send(:apply_modules, Module.new(&block)) if block_given? + def where(*args) + value = build_where(*args) + clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } + end + + 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) -- cgit v1.2.3 From abd568bf1c48e9082c0be7407eca1155c5fe0599 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Jun 2010 17:06:48 -0700 Subject: removing useless code. [#4988 state:resolved] Signed-off-by: Jeremy Kemper --- .../connection_adapters/sqlite_adapter.rb | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index ad6314c530..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 ======================================== @@ -391,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 -- cgit v1.2.3 From 0c0b0aa0f223523331afdc157fb3992a121bf497 Mon Sep 17 00:00:00 2001 From: George Montana Harkin Date: Sun, 27 Jun 2010 19:05:51 +0530 Subject: Fixes #2415 by creating a new instance of the Model when saving attributes to that model and the associated attributes already exist. Tests included. [#2415 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/nested_attributes.rb | 19 ++++++++------- activerecord/test/cases/nested_attributes_test.rb | 28 ++++++++++++---------- 2 files changed, 26 insertions(+), 21 deletions(-) (limited to 'activerecord') 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 :reject_if 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/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 -- cgit v1.2.3 From 6c28959e86222a6932f9cc62bf21aad7a2f9c067 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 27 Jun 2010 20:02:48 -0300 Subject: Move sqlite to sqlite3 for this tests to be run only on sqlite3 adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../test/cases/adapters/sqlite/copy_table_test.rb | 80 ---------------------- .../cases/adapters/sqlite/sqlite3_adapter_test.rb | 56 --------------- .../test/cases/adapters/sqlite3/copy_table_test.rb | 80 ++++++++++++++++++++++ .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 56 +++++++++++++++ 4 files changed, 136 insertions(+), 136 deletions(-) delete mode 100644 activerecord/test/cases/adapters/sqlite/copy_table_test.rb delete mode 100644 activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb create mode 100644 activerecord/test/cases/adapters/sqlite3/copy_table_test.rb create mode 100644 activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb deleted file mode 100644 index 575b4806c1..0000000000 --- a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb +++ /dev/null @@ -1,80 +0,0 @@ -require "cases/helper" - -class CopyTableTest < ActiveRecord::TestCase - fixtures :customers, :companies, :comments - - def setup - @connection = ActiveRecord::Base.connection - class << @connection - public :copy_table, :table_structure, :indexes - end - end - - def test_copy_table(from = 'customers', to = 'customers2', options = {}) - assert_nothing_raised {copy_table(from, to, options)} - assert_equal row_count(from), row_count(to) - - if block_given? - yield from, to, options - else - assert_equal column_names(from), column_names(to) - end - - @connection.drop_table(to) rescue nil - end - - def test_copy_table_renaming_column - test_copy_table('customers', 'customers2', - :rename => {'name' => 'person_name'}) do |from, to, options| - expected = column_values(from, 'name') - assert_equal expected, column_values(to, 'person_name') - assert expected.any?, "No values in table: #{expected.inspect}" - end - end - - def test_copy_table_with_index - test_copy_table('comments', 'comments_with_index') do - @connection.add_index('comments_with_index', ['post_id', 'type']) - test_copy_table('comments_with_index', 'comments_with_index2') do - assert_equal table_indexes_without_name('comments_with_index'), - table_indexes_without_name('comments_with_index2') - end - end - end - - def test_copy_table_without_primary_key - test_copy_table('developers_projects', 'programmers_projects') - end - - def test_copy_table_with_id_col_that_is_not_primary_key - test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| - original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } - copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } - assert_equal original_id.type, copied_id.type - assert_equal original_id.sql_type, copied_id.sql_type - assert_equal original_id.limit, copied_id.limit - assert_equal original_id.primary, copied_id.primary - end - end - -protected - def copy_table(from, to, options = {}) - @connection.copy_table(from, to, {:temporary => true}.merge(options)) - end - - def column_names(table) - @connection.table_structure(table).map {|column| column['name']} - end - - def column_values(table, column) - @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} - end - - def table_indexes_without_name(table) - @connection.indexes('comments_with_index').delete(:name) - end - - def row_count(table) - @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] - end -end diff --git a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb deleted file mode 100644 index 934cf72f72..0000000000 --- a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb +++ /dev/null @@ -1,56 +0,0 @@ -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/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb new file mode 100644 index 0000000000..575b4806c1 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -0,0 +1,80 @@ +require "cases/helper" + +class CopyTableTest < ActiveRecord::TestCase + fixtures :customers, :companies, :comments + + def setup + @connection = ActiveRecord::Base.connection + class << @connection + public :copy_table, :table_structure, :indexes + end + end + + def test_copy_table(from = 'customers', to = 'customers2', options = {}) + assert_nothing_raised {copy_table(from, to, options)} + assert_equal row_count(from), row_count(to) + + if block_given? + yield from, to, options + else + assert_equal column_names(from), column_names(to) + end + + @connection.drop_table(to) rescue nil + end + + def test_copy_table_renaming_column + test_copy_table('customers', 'customers2', + :rename => {'name' => 'person_name'}) do |from, to, options| + expected = column_values(from, 'name') + assert_equal expected, column_values(to, 'person_name') + assert expected.any?, "No values in table: #{expected.inspect}" + end + end + + def test_copy_table_with_index + test_copy_table('comments', 'comments_with_index') do + @connection.add_index('comments_with_index', ['post_id', 'type']) + test_copy_table('comments_with_index', 'comments_with_index2') do + assert_equal table_indexes_without_name('comments_with_index'), + table_indexes_without_name('comments_with_index2') + end + end + end + + def test_copy_table_without_primary_key + test_copy_table('developers_projects', 'programmers_projects') + end + + def test_copy_table_with_id_col_that_is_not_primary_key + test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| + original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } + copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } + assert_equal original_id.type, copied_id.type + assert_equal original_id.sql_type, copied_id.sql_type + assert_equal original_id.limit, copied_id.limit + assert_equal original_id.primary, copied_id.primary + end + end + +protected + def copy_table(from, to, options = {}) + @connection.copy_table(from, to, {:temporary => true}.merge(options)) + end + + def column_names(table) + @connection.table_structure(table).map {|column| column['name']} + end + + def column_values(table, column) + @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} + end + + def table_indexes_without_name(table) + @connection.indexes('comments_with_index').delete(:name) + end + + def row_count(table) + @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb new file mode 100644 index 0000000000..934cf72f72 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/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 -- cgit v1.2.3 From fb7715b2491380becbaa111a4c5a211bac662e97 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 27 Jun 2010 18:33:34 -0700 Subject: Warn that ActiveRecord::Base.reset_subclasses is gone in Rails 3 final. --- activerecord/lib/active_record/base.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e7b52287a5..a4a5faad77 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -278,6 +278,18 @@ module ActiveRecord #:nodoc: # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+. cattr_accessor :logger, :instance_writer => false + class << self + def reset_subclasses #:nodoc: + ActiveSupport::Deprecation.warn 'ActiveRecord::Base.reset_subclasses no longer does anything in Rails 3. It will be removed in the final release; please update your apps and plugins.', caller + end + + def subclasses + descendants + end + + deprecate :subclasses => :descendants + end + ## # :singleton-method: # Contains the database configuration - as is typically stored in config/database.yml - -- cgit v1.2.3 From 8e3e117dbe58fd4fedbb78a3d8e398ab0e415634 Mon Sep 17 00:00:00 2001 From: Kevin Skoglund Date: Wed, 23 Jun 2010 12:45:23 -0400 Subject: rake db:migrate:status displays status of migrations [#4947 state:resolved] Signed-off-by: Michael Koziarski --- .../lib/active_record/railties/databases.rake | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 006e64b115..80218d6ff9 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -171,6 +171,31 @@ namespace :db do ActiveRecord::Migrator.run(:down, "db/migrate/", version) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end + + desc "Display status of migrations" + task :status => :environment do + config = ActiveRecord::Base.configurations[Rails.env || 'development'] + db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM schema_migrations") + file_list = [] + Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| + # only files matching "20091231235959_some_name.rb" pattern + if match_data = /(\d{14})_(.+)\.rb/.match(file) + status = db_list.delete(match_data[1]) ? 'up' : 'down' + file_list << [status, match_data[1], match_data[2]] + end + end + # output + puts "\ndatabase: #{config['database']}\n\n" + puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name" + puts "-" * 50 + file_list.each do |file| + puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2].humanize}" + end + db_list.each do |version| + puts "#{'up'.center(8)} #{version.ljust(14)} *** NO FILE ***" + end + puts + end end desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' -- cgit v1.2.3 From 06b0d6e5cdcfab8d49bcf559008f1753f3e7853c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 28 Jun 2010 00:30:36 -0300 Subject: Add missing require, Base use deprecate method --- activerecord/lib/active_record/base.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a4a5faad77..fa239bafc6 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -15,6 +15,7 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/blank' -- cgit v1.2.3 From 0e5d7c6f6458d279c59d34f1e37289e338784e7b Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 28 Jun 2010 12:08:29 +0900 Subject: Use ActiveRecord::Migrator.schema_migrations_table_name instead of hardcoding "schema_migrations" Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 80218d6ff9..3cb6d63727 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -175,7 +175,7 @@ namespace :db do desc "Display status of migrations" task :status => :environment do config = ActiveRecord::Base.configurations[Rails.env || 'development'] - db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM schema_migrations") + db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}") file_list = [] Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| # only files matching "20091231235959_some_name.rb" pattern -- cgit v1.2.3 From 4f74d449eee1e3d1621ed032532076492a1bf0b3 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 28 Jun 2010 12:12:36 +0900 Subject: Avoid "no such table" exception when schema migrations table does not exist [#4990 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/railties/databases.rake | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 3cb6d63727..cfa84cbb76 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -175,6 +175,11 @@ namespace :db do desc "Display status of migrations" task :status => :environment do config = ActiveRecord::Base.configurations[Rails.env || 'development'] + ActiveRecord::Base.establish_connection(config) + unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + puts 'Schema migrations table does not exist yet.' + next # means "return" for rake task + end db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}") file_list = [] Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| -- cgit v1.2.3 From ab96c71a52b7b950fba6090e6a091a1f951eaa44 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 28 Jun 2010 01:19:24 -0300 Subject: Add this rule to run common tests and specifics ones from adapters dir --- activerecord/Rakefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 22a17a62af..392b717e0a 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -62,7 +62,9 @@ end adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] puts [adapter, adapter_short, connection_path].inspect ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - Dir["test/cases/**/*_test{,_#{adapter_short}}.rb"].all? do |file| + (Dir["test/cases/**/*_test.rb"].reject { + |x| x =~ /\/adapters\// + } + Dir["test/cases/adapters/#{adapter_short}/**/*_test.rb"]).all? do |file| system(ruby, "-Ilib:test:#{connection_path}", file) end or raise "Failures" end -- cgit v1.2.3 From be994e2c50af6c7cb31602fd45f4e718f011b165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Mon, 28 Jun 2010 11:15:47 +0200 Subject: Information about new rake task in CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/CHANGELOG | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 09f897eaac..6c07467535 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,7 @@ *Rails 3.0.0 [RC1] (unreleased)* * PostgreSQL: ensure the database time zone matches Ruby's time zone. #4895 [Aaron Patterson] +* New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund] *Rails 3.0.0 [beta 4] (June 8th, 2010)* -- cgit v1.2.3 From dd8b7417a9b793b7780bf5ca5e04b1c05ac3a8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 28 Jun 2010 11:37:14 +0200 Subject: Update CHANGELOGs. --- activerecord/CHANGELOG | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 6c07467535..60739fbd91 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,8 +1,11 @@ *Rails 3.0.0 [RC1] (unreleased)* -* PostgreSQL: ensure the database time zone matches Ruby's time zone. #4895 [Aaron Patterson] * New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund] +* select and order for ActiveRecord now always concatenate nested calls. Use reorder if you want the original order to be overwritten [Santiago Pastorino] + +* PostgreSQL: ensure the database time zone matches Ruby's time zone #4895 [Aaron Patterson] + *Rails 3.0.0 [beta 4] (June 8th, 2010)* -- cgit v1.2.3 From 40e87ac66912b06c31c98b057a49c64f5555dd99 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 28 Jun 2010 17:17:56 -0400 Subject: with_exclusive_scope does not work properly if ARel is passed. It does work nicely if hash is passed. Blow up if user is attempting it pass ARel to with_exclusive_scope. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#3838 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 13 +++++++++++++ activerecord/test/cases/method_scoping_test.rb | 6 ++++++ activerecord/test/models/developer.rb | 8 +++++++- 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index fa239bafc6..8c10f86486 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1156,7 +1156,20 @@ module ActiveRecord #:nodoc: end # Works like with_scope, but discards any nested properties. + # TODO : this method should be deprecated in favor of standard query API def with_exclusive_scope(method_scoping = {}, &block) + if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) } + msg =<<-MSG + ARel can not be used with_exclusive_scope. You can either specify hash style conditions to with_exclusive_scope like this: + User.with_exclusive_scope {:find => :conditions => {:active => true} } do + end + + Or you can use unscoped method instead of with_exclusive_scope like this: + User.unscoped.where(:active => true) do + end + MSG + raise ArgumentError.new(msg) + end with_scope(method_scoping, :overwrite, &block) end diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 6cd42ff936..178562eb9b 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -283,6 +283,12 @@ class NestedScopingTest < ActiveRecord::TestCase end end + def test_with_exclusive_scope_with_relation + assert_raise(ArgumentError) do + Developer.all_johns + end + end + def test_append_conditions Developer.send(:with_scope, :find => { :conditions => "name = 'David'" }) do Developer.send(:with_scope, :find => { :conditions => 'salary = 80000' }) do diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index e35de3b9b0..de68fd7f24 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -55,6 +55,12 @@ class Developer < ActiveRecord::Base def log=(message) audit_logs.build :message => message end + + def self.all_johns + self.with_exclusive_scope :find => where(:name => 'John') do + self.all + end + end end class AuditLog < ActiveRecord::Base @@ -103,4 +109,4 @@ end class PoorDeveloperCalledJamis < ActiveRecord::Base self.table_name = 'developers' default_scope :conditions => { :name => 'Jamis', :salary => 50000 } -end \ No newline at end of file +end -- cgit v1.2.3 From 093c4eedd0296b6297ab4359d7d873cdf0daf43e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 28 Jun 2010 21:39:23 -0300 Subject: Remove unneeded reject --- activerecord/lib/active_record/relation/query_methods.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 015ca8c24c..83ae9c6a73 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -15,12 +15,10 @@ module ActiveRecord end def eager_load(*args) - args.reject! { |a| a.blank? } clone.tap { |r| r.eager_load_values += args if args.present? } end def preload(*args) - args.reject! { |a| a.blank? } clone.tap { |r| r.preload_values += args if args.present? } end @@ -28,29 +26,24 @@ module ActiveRecord 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 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 @@ -201,7 +194,7 @@ module ActiveRecord stashed_association_joins = joins.select {|j| j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)} - non_association_joins = (joins - association_joins - stashed_association_joins).reject {|j| j.blank?} + non_association_joins = (joins - association_joins - stashed_association_joins) custom_joins = custom_join_sql(*non_association_joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) -- cgit v1.2.3 From 8fbb5177a7a3618d7b40bccbb3b36876a10a8918 Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Tue, 29 Jun 2010 13:32:53 +1000 Subject: accepts_nested_attributes_for typo --- activerecord/lib/active_record/nested_attributes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index c0302136ea..cf8c5aaf84 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -213,7 +213,7 @@ module ActiveRecord # that will reject a record where all the attributes are blank. # [:limit] # Allows you to specify the maximum number of the associated records that - # can be processes with the nested attributes. If the size of the + # can be processed with the nested attributes. If the size of the # nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords # exception is raised. If omitted, any number associations can be processed. # Note that the :limit option is only applicable to one-to-many associations. -- cgit v1.2.3 From bd1666ad1de88598ed6f04ceffb8488a77be4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 29 Jun 2010 17:18:55 +0200 Subject: Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope. A few examples: * with_scope now should be scoping: Before: Comment.with_scope(:find => { :conditions => { :post_id => 1 } }) do Comment.first #=> SELECT * FROM comments WHERE post_id = 1 end After: Comment.where(:post_id => 1).scoping do Comment.first #=> SELECT * FROM comments WHERE post_id = 1 end * with_exclusive_scope now should be unscoped: class Post < ActiveRecord::Base default_scope :published => true end Post.all #=> SELECT * FROM posts WHERE published = true Before: Post.with_exclusive_scope do Post.all #=> SELECT * FROM posts end After: Post.unscoped do Post.all #=> SELECT * FROM posts end Notice you can also use unscoped without a block and it will return an anonymous scope with default_scope values: Post.unscoped.all #=> SELECT * FROM posts --- activerecord/lib/active_record/base.rb | 40 ++- activerecord/lib/active_record/named_scope.rb | 33 +- activerecord/lib/active_record/persistence.rb | 2 +- activerecord/lib/active_record/relation.rb | 43 +-- .../lib/active_record/relation/query_methods.rb | 5 +- activerecord/test/cases/inheritance_test.rb | 2 +- activerecord/test/cases/method_scoping_test.rb | 207 +---------- activerecord/test/cases/named_scope_test.rb | 4 +- activerecord/test/cases/relation_scoping_test.rb | 396 +++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 2 +- 10 files changed, 484 insertions(+), 250 deletions(-) create mode 100644 activerecord/test/cases/relation_scoping_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8c10f86486..c0ded7f558 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -398,7 +398,7 @@ module ActiveRecord #:nodoc: delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped - delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped # Executes a custom SQL query against your database and returns all the results. The results will @@ -801,7 +801,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @arel_engine = @unscoped = @arel_table = nil + @arel_engine = @relation = @arel_table = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -904,9 +904,9 @@ module ActiveRecord #:nodoc: store_full_sti_class ? name : name.demodulize end - def unscoped - @unscoped ||= Relation.new(self, arel_table) - finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped + def relation + @relation ||= Relation.new(self, arel_table) + finder_needs_type_condition? ? @relation.where(type_condition) : @relation end def arel_table @@ -923,6 +923,31 @@ module ActiveRecord #:nodoc: end end + # Returns a scope for this class without taking into account the default_scope. + # + # class Post < ActiveRecord::Base + # default_scope :published => true + # end + # + # Post.all # Fires "SELECT * FROM posts WHERE published = true" + # Post.unscoped.all # Fires "SELECT * FROM posts" + # + # This method also accepts a block meaning that all queries inside the block will + # not use the default_scope: + # + # Post.unscoped { + # limit(10) # Fires "SELECT * FROM posts LIMIT 10" + # } + # + def unscoped + block_given? ? relation.scoping { yield } : relation + end + + def scoped_methods #:nodoc: + key = :"#{self}_scoped_methods" + Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup + end + private # Finder methods must instantiate through this method to work with the # single-table inheritance model that makes it possible to create @@ -1183,11 +1208,6 @@ module ActiveRecord #:nodoc: self.default_scoping << construct_finder_arel(options, default_scoping.pop) end - def scoped_methods #:nodoc: - key = :"#{self}_scoped_methods" - Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup - end - def current_scoped_methods #:nodoc: scoped_methods.last end diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index ec0a98c6df..c010dac64e 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -25,10 +25,9 @@ module ActiveRecord # # You can define a \scope that applies to all finders using # ActiveRecord::Base.default_scope. - def scoped(options = {}, &block) + def scoped(options = nil) if options.present? - relation = scoped.apply_finder_options(options) - block_given? ? relation.extending(Module.new(&block)) : relation + scoped.apply_finder_options(options) else current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone end @@ -88,18 +87,22 @@ module ActiveRecord # end def scope(name, scope_options = {}, &block) name = name.to_sym + valid_scope_name?(name) - if !scopes[name] && respond_to?(name, true) - logger.warn "Creating scope :#{name}. " \ - "Overwriting existing method #{self.name}.#{name}." - end + extension = Module.new(&block) if block_given? scopes[name] = lambda do |*args| options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options - relation = scoped - relation = options.is_a?(Hash) ? relation.apply_finder_options(options) : scoped.merge(options) if options - block_given? ? relation.extending(Module.new(&block)) : relation + relation = if options.is_a?(Hash) + scoped.apply_finder_options(options) + elsif options + scoped.merge(options) + else + scoped + end + + extension ? relation.extending(extension) : relation end singleton_class.send :define_method, name, &scopes[name] @@ -109,7 +112,15 @@ module ActiveRecord ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller) scope(*args, &block) end - end + protected + + def valid_scope_name?(name) + if !scopes[name] && respond_to?(name, true) + logger.warn "Creating scope :#{name}. " \ + "Overwriting existing method #{self.name}.#{name}." + end + end + end end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 9e28aa2a05..50166c4385 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -182,7 +182,7 @@ module ActiveRecord def reload(options = nil) clear_aggregation_cache clear_association_cache - @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes')) + @attributes.update(self.class.unscoped { self.class.find(self.id, options) }.instance_variable_get('@attributes')) @attributes_cache = {} self end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index fd0660a138..3b24d4aafd 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -16,7 +16,7 @@ module ActiveRecord attr_reader :table, :klass attr_accessor :extensions - def initialize(klass, table, &block) + def initialize(klass, table) @klass, @table = klass, table @implicit_readonly = nil @@ -25,12 +25,10 @@ module ActiveRecord SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)} (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} @extensions = [] - - apply_modules(Module.new(&block)) if block_given? end def new(*args, &block) - with_create_scope { @klass.new(*args, &block) } + scoping { @klass.new(*args, &block) } end def initialize_copy(other) @@ -40,11 +38,11 @@ module ActiveRecord alias build new def create(*args, &block) - with_create_scope { @klass.create(*args, &block) } + scoping { @klass.create(*args, &block) } end def create!(*args, &block) - with_create_scope { @klass.create!(*args, &block) } + scoping { @klass.create!(*args, &block) } end def respond_to?(method, include_private = false) @@ -102,6 +100,25 @@ module ActiveRecord end end + # Scope all queries to the current scope. + # + # ==== Example + # + # Comment.where(:post_id => 1).scoping do + # Comment.first #=> SELECT * FROM comments WHERE post_id = 1 + # end + # + # Please check unscoped if you want to remove all previous scopes (including + # the default_scope) during the execution of a block. + def scoping + @klass.scoped_methods << self + begin + yield + ensure + @klass.scoped_methods.pop + end + end + # Updates all records with details given if they match a set of conditions supplied, limits and order can # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the # database. It does not instantiate the involved models and it does not trigger Active Record callbacks @@ -305,7 +322,6 @@ module ActiveRecord if where.is_a?(Arel::Predicates::Equality) hash[where.operand1.name] = where.operand2.respond_to?(:value) ? where.operand2.value : where.operand2 end - hash end end @@ -328,15 +344,6 @@ 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) @@ -364,10 +371,6 @@ module ActiveRecord private - def with_create_scope - @klass.send(:with_scope, :create => scope_for_create, :find => {}) { yield } - end - def references_eager_loaded_tables? # always convert table names to downcase as in Oracle quoted table names are in uppercase joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map(&:downcase).uniq diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 83ae9c6a73..4692271266 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -86,8 +86,9 @@ module ActiveRecord clone.tap { |r| r.from_value = value } end - def extending(*modules) - clone.tap { |r| r.send :apply_modules, *modules } + def extending(*modules, &block) + modules << Module.new(&block) if block_given? + clone.tap { |r| r.send(:apply_modules, *modules) } end def reverse_order diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index c1c8f01e46..8c09fc4d59 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -173,7 +173,7 @@ class InheritanceTest < ActiveRecord::TestCase def test_complex_inheritance very_special_client = VerySpecialClient.create("name" => "veryspecial") - assert_equal very_special_client, VerySpecialClient.find(:first, :conditions => "name = 'veryspecial'") + assert_equal very_special_client, VerySpecialClient.where("name = 'veryspecial'").first assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'") assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'") assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'") diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 178562eb9b..4e8ce1dac1 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -1,3 +1,7 @@ +# This file can be removed once with_exclusive_scope and with_scope are removed. +# All the tests were already ported to relation_scoping_test.rb when the new +# relation scoping API was added. + require "cases/helper" require 'models/post' require 'models/author' @@ -539,205 +543,4 @@ class NestedScopingTest < ActiveRecord::TestCase assert_equal 1, scoped_authors.size assert_equal authors(:david).attributes, scoped_authors.first.attributes end -end - -class HasManyScopingTest< ActiveRecord::TestCase - fixtures :comments, :posts - - def setup - @welcome = Post.find(1) - end - - def test_forwarding_of_static_methods - assert_equal 'a comment...', Comment.what_are_you - assert_equal 'a comment...', @welcome.comments.what_are_you - end - - def test_forwarding_to_scoped - assert_equal 4, Comment.search_by_type('Comment').size - assert_equal 2, @welcome.comments.search_by_type('Comment').size - end - - def test_forwarding_to_dynamic_finders - assert_equal 4, Comment.find_all_by_type('Comment').size - assert_equal 2, @welcome.comments.find_all_by_type('Comment').size - end - - def test_nested_scope - Comment.send(:with_scope, :find => { :conditions => '1=1' }) do - assert_equal 'a comment...', @welcome.comments.what_are_you - end - end -end - -class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase - fixtures :posts, :categories, :categories_posts - - def setup - @welcome = Post.find(1) - end - - def test_forwarding_of_static_methods - assert_equal 'a category...', Category.what_are_you - assert_equal 'a category...', @welcome.categories.what_are_you - end - - def test_forwarding_to_dynamic_finders - assert_equal 4, Category.find_all_by_type('SpecialCategory').size - assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size - assert_equal 2, @welcome.categories.find_all_by_type('Category').size - end - - def test_nested_scope - Category.send(:with_scope, :find => { :conditions => '1=1' }) do - assert_equal 'a comment...', @welcome.comments.what_are_you - end - end -end - -class DefaultScopingTest < ActiveRecord::TestCase - fixtures :developers, :posts - - def test_default_scope - expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - assert_equal expected, received - end - - def test_default_scope_with_conditions_string - assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.find(:all).map(&:id).sort - assert_equal nil, DeveloperCalledDavid.create!.name - end - - def test_default_scope_with_conditions_hash - assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.find(:all).map(&:id).sort - assert_equal 'Jamis', DeveloperCalledJamis.create!.name - end - - def test_default_scoping_with_threads - 2.times do - Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join - end - end - - def test_default_scoping_with_inheritance - # Inherit a class having a default scope and define a new default scope - klass = Class.new(DeveloperOrderedBySalary) - klass.send :default_scope, :limit => 1 - - # Scopes added on children should append to parent scope - assert_equal 1, klass.scoped.limit_value - assert_equal ['salary DESC'], klass.scoped.order_values - - # Parent should still have the original scope - assert_nil DeveloperOrderedBySalary.scoped.limit_value - assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values - end - - def test_default_scope_called_twice_merges_conditions - Developer.destroy_all - Developer.create!(:name => "David", :salary => 80000) - Developer.create!(:name => "David", :salary => 100000) - Developer.create!(:name => "Brian", :salary => 100000) - - klass = Class.new(Developer) - klass.__send__ :default_scope, :conditions => { :name => "David" } - klass.__send__ :default_scope, :conditions => { :salary => 100000 } - assert_equal 1, klass.count - assert_equal "David", klass.first.name - assert_equal 100000, klass.first.salary - end - def test_method_scope - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary } - assert_equal expected, received - end - - def test_nested_scope - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do - DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - end - assert_equal expected, received - end - - def test_named_scope_overwrites_default - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } - assert_equal expected, received - end - - def test_nested_exclusive_scope - expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do - DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - end - assert_equal expected, received - end - - def test_overwriting_default_scope - expected = Developer.find(:all, :order => 'salary').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary } - assert_equal expected, received - end - - def test_default_scope_using_relation - posts = PostWithComment.scoped - assert_equal 2, posts.count - assert_equal posts(:thinking), posts.first - end - - def test_create_attribute_overwrites_default_scoping - assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name - assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary - end - - def test_create_attribute_overwrites_default_values - assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary - assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary - end -end - -=begin -# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case - -class BelongsToScopingTest< ActiveRecord::TestCase - fixtures :comments, :posts - - def setup - @greetings = Comment.find(1) - end - - def test_forwarding_of_static_method - assert_equal 'a post...', Post.what_are_you - assert_equal 'a post...', @greetings.post.what_are_you - end - - def test_forwarding_to_dynamic_finders - assert_equal 4, Post.find_all_by_type('Post').size - assert_equal 1, @greetings.post.find_all_by_type('Post').size - end - -end - -class HasOneScopingTest< ActiveRecord::TestCase - fixtures :comments, :posts - - def setup - @sti_comments = Post.find(4) - end - - def test_forwarding_of_static_methods - assert_equal 'a comment...', Comment.what_are_you - assert_equal 'a very special comment...', @sti_comments.very_special_comment.what_are_you - end - - def test_forwarding_to_dynamic_finders - assert_equal 1, Comment.find_all_by_type('VerySpecialComment').size - assert_equal 1, @sti_comments.very_special_comment.find_all_by_type('VerySpecialComment').size - assert_equal 0, @sti_comments.very_special_comment.find_all_by_type('Comment').size - end - -end - -=end +end \ No newline at end of file diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 33ffb041c1..6574e4cfc0 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -54,8 +54,8 @@ class NamedScopeTest < ActiveRecord::TestCase end def test_respond_to_respects_include_private_parameter - assert !Topic.approved.respond_to?(:with_create_scope) - assert Topic.approved.respond_to?(:with_create_scope, true) + assert !Topic.approved.respond_to?(:tables_in_string) + assert Topic.approved.respond_to?(:tables_in_string, true) end def test_subclasses_inherit_scopes diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb new file mode 100644 index 0000000000..41dcdbcd37 --- /dev/null +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -0,0 +1,396 @@ +require "cases/helper" +require 'models/post' +require 'models/author' +require 'models/developer' +require 'models/project' +require 'models/comment' +require 'models/category' + +class RelationScopingTest < ActiveRecord::TestCase + fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects + + def test_scoped_find + Developer.where("name = 'David'").scoping do + assert_nothing_raised { Developer.find(1) } + end + end + + def test_scoped_find_first + developer = Developer.find(10) + Developer.where("salary = 100000").scoping do + assert_equal developer, Developer.order("name").first + end + end + + def test_scoped_find_last + highest_salary = Developer.order("salary DESC").first + + Developer.order("salary").scoping do + assert_equal highest_salary, Developer.last + end + end + + def test_scoped_find_last_preserves_scope + lowest_salary = Developer.first :order => "salary ASC" + highest_salary = Developer.first :order => "salary DESC" + + Developer.order("salary").scoping do + assert_equal highest_salary, Developer.last + assert_equal lowest_salary, Developer.first + end + end + + def test_scoped_find_combines_and_sanitizes_conditions + Developer.where("salary = 9000").scoping do + assert_equal developers(:poor_jamis), Developer.where("name = 'Jamis'").first + end + end + + def test_scoped_find_all + Developer.where("name = 'David'").scoping do + assert_equal [developers(:david)], Developer.all + end + end + + def test_scoped_find_select + Developer.select("id, name").scoping do + developer = Developer.where("name = 'David'").first + assert_equal "David", developer.name + assert !developer.has_attribute?(:salary) + end + end + + def test_scope_select_concatenates + Developer.select("id, name").scoping do + developer = Developer.select('id, salary').where("name = 'David'").first + assert_equal 80000, developer.salary + assert developer.has_attribute?(:id) + assert developer.has_attribute?(:name) + assert developer.has_attribute?(:salary) + end + end + + def test_scoped_count + Developer.where("name = 'David'").scoping do + assert_equal 1, Developer.count + end + + Developer.where('salary = 100000').scoping do + assert_equal 8, Developer.count + assert_equal 1, Developer.where("name LIKE 'fixture_1%'").count + end + end + + def test_scoped_find_include + # with the include, will retrieve only developers for the given project + scoped_developers = Developer.includes(:projects).scoping do + Developer.where('projects.id = 2').all + end + assert scoped_developers.include?(developers(:david)) + assert !scoped_developers.include?(developers(:jamis)) + assert_equal 1, scoped_developers.size + end + + def test_scoped_find_joins + scoped_developers = Developer.joins('JOIN developers_projects ON id = developer_id').scoping do + Developer.where('developers_projects.project_id = 2').all + end + + assert scoped_developers.include?(developers(:david)) + assert !scoped_developers.include?(developers(:jamis)) + assert_equal 1, scoped_developers.size + assert_equal developers(:david).attributes, scoped_developers.first.attributes + end + + def test_scoped_create_with_where + new_comment = VerySpecialComment.where(:post_id => 1).scoping do + VerySpecialComment.create :body => "Wonderful world" + end + + assert_equal 1, new_comment.post_id + assert Post.find(1).comments.include?(new_comment) + end + + def test_scoped_create_with_create_with + new_comment = VerySpecialComment.create_with(:post_id => 1).scoping do + VerySpecialComment.create :body => "Wonderful world" + end + + assert_equal 1, new_comment.post_id + assert Post.find(1).comments.include?(new_comment) + end + + def test_scoped_create_with_create_with_has_higher_priority + new_comment = VerySpecialComment.where(:post_id => 2).create_with(:post_id => 1).scoping do + VerySpecialComment.create :body => "Wonderful world" + end + + assert_equal 1, new_comment.post_id + assert Post.find(1).comments.include?(new_comment) + end + + def test_ensure_that_method_scoping_is_correctly_restored + scoped_methods = Developer.send(:current_scoped_methods) + + begin + Developer.where("name = 'Jamis'").scoping do + raise "an exception" + end + rescue + end + + assert_equal scoped_methods, Developer.send(:current_scoped_methods) + end +end + +class NestedRelationScopingTest < ActiveRecord::TestCase + fixtures :authors, :developers, :projects, :comments, :posts + + def test_merge_options + Developer.where('salary = 80000').scoping do + Developer.limit(10).scoping do + devs = Developer.scoped + assert_equal '(salary = 80000)', devs.arel.send(:where_clauses).join(' AND ') + assert_equal 10, devs.taken + end + end + end + + def test_merge_inner_scope_has_priority + Developer.limit(5).scoping do + Developer.limit(10).scoping do + assert_equal 10, Developer.count + end + end + end + + def test_replace_options + Developer.where(:name => 'David').scoping do + Developer.unscoped do + assert_equal 'Jamis', Developer.where(:name => 'Jamis').first[:name] + end + + assert_equal 'David', Developer.first[:name] + end + end + + def test_three_level_nested_exclusive_scoped_find + Developer.where("name = 'Jamis'").scoping do + assert_equal 'Jamis', Developer.first.name + + Developer.unscoped.where("name = 'David'") do + assert_equal 'David', Developer.first.name + + Developer.unscoped.where("name = 'Maiha'") do + assert_equal nil, Developer.first + end + + # ensure that scoping is restored + assert_equal 'David', Developer.first.name + end + + # ensure that scoping is restored + assert_equal 'Jamis', Developer.first.name + end + end + + def test_nested_scoped_create + comment = Comment.create_with(:post_id => 1).scoping do + Comment.create_with(:post_id => 2).scoping do + Comment.create :body => "Hey guys, nested scopes are broken. Please fix!" + end + end + + assert_equal 2, comment.post_id + end + + def test_nested_exclusive_scope_for_create + comment = Comment.create_with(:body => "Hey guys, nested scopes are broken. Please fix!").scoping do + Comment.unscoped.create_with(:post_id => 1).scoping do + assert Comment.new.body.blank? + Comment.create :body => "Hey guys" + end + end + + assert_equal 1, comment.post_id + assert_equal 'Hey guys', comment.body + end +end + +class HasManyScopingTest< ActiveRecord::TestCase + fixtures :comments, :posts + + def setup + @welcome = Post.find(1) + end + + def test_forwarding_of_static_methods + assert_equal 'a comment...', Comment.what_are_you + assert_equal 'a comment...', @welcome.comments.what_are_you + end + + def test_forwarding_to_scoped + assert_equal 4, Comment.search_by_type('Comment').size + assert_equal 2, @welcome.comments.search_by_type('Comment').size + end + + def test_forwarding_to_dynamic_finders + assert_equal 4, Comment.find_all_by_type('Comment').size + assert_equal 2, @welcome.comments.find_all_by_type('Comment').size + end + + def test_nested_scope_finder + Comment.where('1=0').scoping do + assert_equal 0, @welcome.comments.count + assert_equal 'a comment...', @welcome.comments.what_are_you + end + + Comment.where('1=1').scoping do + assert_equal 2, @welcome.comments.count + assert_equal 'a comment...', @welcome.comments.what_are_you + end + end +end + +class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase + fixtures :posts, :categories, :categories_posts + + def setup + @welcome = Post.find(1) + end + + def test_forwarding_of_static_methods + assert_equal 'a category...', Category.what_are_you + assert_equal 'a category...', @welcome.categories.what_are_you + end + + def test_forwarding_to_dynamic_finders + assert_equal 4, Category.find_all_by_type('SpecialCategory').size + assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size + assert_equal 2, @welcome.categories.find_all_by_type('Category').size + end + + def test_nested_scope_finder + Category.where('1=0').scoping do + assert_equal 0, @welcome.categories.count + assert_equal 'a category...', @welcome.categories.what_are_you + end + + Category.where('1=1').scoping do + assert_equal 2, @welcome.categories.count + assert_equal 'a category...', @welcome.categories.what_are_you + end + end +end + +class DefaultScopingTest < ActiveRecord::TestCase + fixtures :developers, :posts + + def test_default_scope + expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary } + received = DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } + assert_equal expected, received + end + + def test_default_scope_is_unscoped_on_find + assert_equal 1, DeveloperCalledDavid.count + assert_equal 11, DeveloperCalledDavid.unscoped.count + end + + def test_default_scope_is_unscoped_on_create + assert_nil DeveloperCalledJamis.unscoped.create!.name + end + + def test_default_scope_with_conditions_string + assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.find(:all).map(&:id).sort + assert_equal nil, DeveloperCalledDavid.create!.name + end + + def test_default_scope_with_conditions_hash + assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.find(:all).map(&:id).sort + assert_equal 'Jamis', DeveloperCalledJamis.create!.name + end + + def test_default_scoping_with_threads + 2.times do + Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join + end + end + + def test_default_scoping_with_inheritance + # Inherit a class having a default scope and define a new default scope + klass = Class.new(DeveloperOrderedBySalary) + klass.send :default_scope, :limit => 1 + + # Scopes added on children should append to parent scope + assert_equal 1, klass.scoped.limit_value + assert_equal ['salary DESC'], klass.scoped.order_values + + # Parent should still have the original scope + assert_nil DeveloperOrderedBySalary.scoped.limit_value + assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values + end + + def test_default_scope_called_twice_merges_conditions + Developer.destroy_all + Developer.create!(:name => "David", :salary => 80000) + Developer.create!(:name => "David", :salary => 100000) + Developer.create!(:name => "Brian", :salary => 100000) + + klass = Class.new(Developer) + klass.__send__ :default_scope, :conditions => { :name => "David" } + klass.__send__ :default_scope, :conditions => { :salary => 100000 } + assert_equal 1, klass.count + assert_equal "David", klass.first.name + assert_equal 100000, klass.first.salary + end + def test_method_scope + expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } + received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary } + assert_equal expected, received + end + + def test_nested_scope + expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } + received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do + DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } + end + assert_equal expected, received + end + + def test_named_scope_overwrites_default + expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_nested_exclusive_scope + expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary } + received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do + DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } + end + assert_equal expected, received + end + + def test_overwriting_default_scope + expected = Developer.find(:all, :order => 'salary').collect { |dev| dev.salary } + received = DeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary } + assert_equal expected, received + end + + def test_default_scope_using_relation + posts = PostWithComment.scoped + assert_equal 2, posts.count + assert_equal posts(:thinking), posts.first + end + + def test_create_attribute_overwrites_default_scoping + assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name + assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary + end + + def test_create_attribute_overwrites_default_values + assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary + assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary + end +end \ No newline at end of file diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index d27a69ea1c..ffde8daa07 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -618,7 +618,7 @@ class RelationTest < ActiveRecord::TestCase end def test_anonymous_extension - relation = Post.where(:author_id => 1).order('id ASC').extend do + relation = Post.where(:author_id => 1).order('id ASC').extending do def author 'lifo' end -- cgit v1.2.3 From 417125e7955e56159524992b0e95e92d2f7bf0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 29 Jun 2010 17:42:20 +0200 Subject: Tidy up deprecation message for with_exclusive_scope. --- activerecord/lib/active_record/base.rb | 21 +++++++++++---------- activerecord/lib/active_record/relation.rb | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c0ded7f558..b44158ec75 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1181,19 +1181,20 @@ module ActiveRecord #:nodoc: end # Works like with_scope, but discards any nested properties. - # TODO : this method should be deprecated in favor of standard query API def with_exclusive_scope(method_scoping = {}, &block) if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) } - msg =<<-MSG - ARel can not be used with_exclusive_scope. You can either specify hash style conditions to with_exclusive_scope like this: - User.with_exclusive_scope {:find => :conditions => {:active => true} } do - end + raise ArgumentError, <<-MSG +New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope: - Or you can use unscoped method instead of with_exclusive_scope like this: - User.unscoped.where(:active => true) do - end - MSG - raise ArgumentError.new(msg) + User.unscoped.where(:active => true) + +Or call unscoped with a block: + + User.unscoped do + User.where(:active => true).all + end + +MSG end with_scope(method_scoping, :overwrite, &block) end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3b24d4aafd..bc708b573f 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -352,7 +352,7 @@ module ActiveRecord elsif @klass.scopes[method] merge(@klass.send(method, *args, &block)) elsif @klass.respond_to?(method) - @klass.send(:with_scope, self) { @klass.send(method, *args, &block) } + scoping { @klass.send(method, *args, &block) } elsif arel.respond_to?(method) arel.send(method, *args, &block) elsif match = DynamicFinderMatch.match(method) -- cgit v1.2.3 From 735a4db6854e73e871e6b01ec003f0670cc5ee14 Mon Sep 17 00:00:00 2001 From: David Trasbo Date: Tue, 29 Jun 2010 16:54:05 +0200 Subject: Remove ActiveRecord::Base#class_name [#379 state:committed] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 8 -------- activerecord/test/cases/base_test.rb | 19 ------------------- 2 files changed, 27 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b44158ec75..c8795e4496 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -720,14 +720,6 @@ module ActiveRecord #:nodoc: end alias :sequence_name= :set_sequence_name - # Turns the +table_name+ back into a class name following the reverse rules of +table_name+. - def class_name(table_name = table_name) # :nodoc: - # remove any prefix and/or suffix from the table name - class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize - class_name = class_name.singularize if pluralize_table_names - class_name - end - # Indicates whether the table associated with this class exists def table_exists? connection.table_exists?(table_name) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index f8b90d8877..ba7db838ca 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -798,25 +798,6 @@ class BasicsTest < ActiveRecord::TestCase assert_raise(NoMethodError) { t.title2 } end - def test_class_name - assert_equal "Firm", ActiveRecord::Base.class_name("firms") - assert_equal "Category", ActiveRecord::Base.class_name("categories") - assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder") - - ActiveRecord::Base.pluralize_table_names = false - assert_equal "Firms", ActiveRecord::Base.class_name( "firms" ) - ActiveRecord::Base.pluralize_table_names = true - - ActiveRecord::Base.table_name_prefix = "test_" - assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" ) - ActiveRecord::Base.table_name_suffix = "_tests" - assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" ) - ActiveRecord::Base.table_name_prefix = "" - assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" ) - ActiveRecord::Base.table_name_suffix = "" - assert_equal "Firm", ActiveRecord::Base.class_name( "firms" ) - end - def test_null_fields assert_nil Topic.find(1).parent_id assert_nil Topic.create("title" => "Hey you").parent_id -- cgit v1.2.3 From e8f88a3298441c0421258a429376540870b8149e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 28 Jun 2010 13:50:28 -0400 Subject: splitting a really long line into multiple lines which is easy on eyes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/associations/has_many_association.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 92c6b3e770..d74fb7c702 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -110,7 +110,11 @@ module ActiveRecord create_scoping = {} set_belongs_to_association_for(create_scoping) { - :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]}, + :find => { :conditions => @finder_sql, + :readonly => false, + :order => @reflection.options[:order], + :limit => @reflection.options[:limit], + :include => @reflection.options[:include]}, :create => create_scoping } end -- cgit v1.2.3 From dabf74b4950f486075b380f2406ede9fa67606db Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 28 Jun 2010 22:33:10 -0400 Subject: array subtraction should be faster than iterating over all the elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/spawn_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index bb1f138f5b..7712ad2569 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -6,7 +6,7 @@ module ActiveRecord merged_relation = clone return merged_relation unless r - (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method| + ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method| value = r.send(:"#{method}_values") merged_relation.send(:"#{method}_values=", value) if value.present? end -- cgit v1.2.3 From 67582f08bf86ec71a27363554bc550e929a007f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 29 Jun 2010 19:47:04 +0200 Subject: Push a failing test for issues [#4994] and [#5003]. --- activerecord/test/cases/named_scope_test.rb | 6 ++++++ activerecord/test/models/without_table.rb | 3 +++ 2 files changed, 9 insertions(+) create mode 100644 activerecord/test/models/without_table.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 6574e4cfc0..dc85b395d3 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -450,6 +450,12 @@ class NamedScopeTest < ActiveRecord::TestCase assert before.object_id != post.comments.containing_the_letter_e.object_id, "AssociationCollection##{method} should reset the named scopes cache" end end + + def test_named_scoped_are_lazy_loaded_if_table_still_does_not_exist + assert_nothing_raised do + require "models/without_table" + end + end end class DynamicScopeMatchTest < ActiveRecord::TestCase diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb new file mode 100644 index 0000000000..87f80911e1 --- /dev/null +++ b/activerecord/test/models/without_table.rb @@ -0,0 +1,3 @@ +class WithoutTable < ActiveRecord::Base + default_scope where(:published => true) +end \ No newline at end of file -- cgit v1.2.3 From af6ec607fa9af96569fd2e143147f2dc0ecd583b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 29 Jun 2010 20:15:29 +0200 Subject: No need to check if the attribute exists (this is the same behavior as in 2.3) [#4994 state:resolved] and [#5003 state:resolved] --- activerecord/lib/active_record/relation/predicate_builder.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index d853fd63d1..5cea2328e8 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -20,9 +20,7 @@ module ActiveRecord table = Arel::Table.new(table_name, :engine => @engine) end - unless attribute = table[column] - raise StatementInvalid, "No attribute named `#{column}` exists for table `#{table.name}`" - end + attribute = table[column] || Arel::Attribute.new(table, column) case value when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation -- cgit v1.2.3 From ff22b9d451efc4251c28d11453f05fed95378af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Pablo=20Fern=C3=A1ndez?= Date: Sat, 12 Jun 2010 03:25:18 +0200 Subject: Fixed error when removing an index from a table name values, which is a reserved word, with test. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../connection_adapters/abstract/schema_statements.rb | 2 +- activerecord/test/cases/migration_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'activerecord') 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 0216a8f4ac..76b65bf219 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -365,7 +365,7 @@ module ActiveRecord end def remove_index!(table_name, index_name) #:nodoc: - execute "DROP INDEX #{quote_column_name(index_name)} ON #{table_name}" + execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end # Rename an index. diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 99a3a12a8b..a46d8b18a4 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1621,6 +1621,22 @@ if ActiveRecord::Base.connection.supports_migrations? end end + class ReservedWordsMigrationTest < ActiveRecord::TestCase + def test_drop_index_from_table_named_values + connection = Person.connection + connection.create_table :values, :force => true do |t| + t.integer :value + end + connection.add_index :values, :value + + # Just remove the index, it should not raise an exception + connection.remove_index :values, :column => :value + + connection.drop_table :values rescue nil + end + end + + class ChangeTableMigrationsTest < ActiveRecord::TestCase def setup @connection = Person.connection -- cgit v1.2.3 From 0baf83fa18a0683d024a0e8f73f99c190a6ab0f1 Mon Sep 17 00:00:00 2001 From: Paul Barry Date: Fri, 11 Jun 2010 23:03:47 -0400 Subject: Replaced statement in comment with an assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/migration_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index a46d8b18a4..8870efac98 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1629,8 +1629,9 @@ if ActiveRecord::Base.connection.supports_migrations? end connection.add_index :values, :value - # Just remove the index, it should not raise an exception - connection.remove_index :values, :column => :value + assert_nothing_raised do + connection.remove_index :values, :column => :value + end connection.drop_table :values rescue nil end -- cgit v1.2.3 From 21957b72ea394c679d9b17e75b570cc99596322d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Pablo=20Fern=C3=A1ndez?= Date: Tue, 15 Jun 2010 11:03:30 +0200 Subject: Test that adding an index also doesn't raise an exception. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4809 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/migration_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 8870efac98..4ce9bdb46d 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1627,9 +1627,9 @@ if ActiveRecord::Base.connection.supports_migrations? connection.create_table :values, :force => true do |t| t.integer :value end - connection.add_index :values, :value assert_nothing_raised do + connection.add_index :values, :value connection.remove_index :values, :column => :value end -- cgit v1.2.3 From d7c2e52c6c04dec4e4665c8f0f9988bfe913cb15 Mon Sep 17 00:00:00 2001 From: Tekin Date: Tue, 29 Jun 2010 20:59:52 +0100 Subject: migrations.rb requires active_support/core_ext/module/aliasing [#5008 state:committed] Signed-off-by: Xavier Noria --- activerecord/lib/active_record/migration.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b273c33e50..4c5e1ae218 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/kernel/singleton_class' +require 'active_support/core_ext/module/aliasing' module ActiveRecord # Exception that can be raised to stop migrations from going backwards. -- cgit v1.2.3 From 3f563f169612ec340816f0a549fe9db7ff95c238 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 29 Jun 2010 15:42:06 -0700 Subject: AssociationCollection#create_by_*, find_or_create_by_* work properly now. [#1108 state:resolved] Signed-off-by: Jeremy Kemper --- .../associations/association_collection.rb | 11 ++++ .../associations/has_many_associations_test.rb | 62 ++++++++++++++++++++++ 2 files changed, 73 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 186b531ffb..ddf4ce4058 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -409,6 +409,17 @@ module ActiveRecord end def method_missing(method, *args) + case method.to_s + when 'find_or_create' + return find(:first, :conditions => args.first) || create(args.first) + when /^find_or_create_by_(.*)$/ + rest = $1 + return send("find_by_#{rest}", *args) || + method_missing("create_by_#{rest}", *args) + when /^create_by_(.*)$/ + return create Hash[$1.split('_and_').zip(args)] + end + if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) if block_given? super { |*block_args| yield(*block_args) } diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 45c7498013..5e3ba778f3 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -21,6 +21,68 @@ class HasManyAssociationsTest < ActiveRecord::TestCase Client.destroyed_client_ids.clear end + def test_create_by + person = Person.create! :first_name => 'tenderlove' + post = Post.find :first + + assert_equal [], person.readers + assert_nil person.readers.find_by_post_id post.id + + reader = person.readers.create_by_post_id post.id + + assert_equal 1, person.readers.count + assert_equal 1, person.readers.length + assert_equal post, person.readers.first.post + assert_equal person, person.readers.first.person + end + + def test_create_by_multi + person = Person.create! :first_name => 'tenderlove' + post = Post.find :first + + assert_equal [], person.readers + + reader = person.readers.create_by_post_id_and_skimmer post.id, false + + assert_equal 1, person.readers.count + assert_equal 1, person.readers.length + assert_equal post, person.readers.first.post + assert_equal person, person.readers.first.person + end + + def test_find_or_create_by + person = Person.create! :first_name => 'tenderlove' + post = Post.find :first + + assert_equal [], person.readers + assert_nil person.readers.find_by_post_id post.id + + reader = person.readers.find_or_create_by_post_id post.id + + assert_equal 1, person.readers.count + assert_equal 1, person.readers.length + assert_equal post, person.readers.first.post + assert_equal person, person.readers.first.person + end + + def test_find_or_create + person = Person.create! :first_name => 'tenderlove' + post = Post.find :first + + assert_equal [], person.readers + assert_nil person.readers.find(:first, :conditions => { + :post_id => post.id + }) + + reader = person.readers.find_or_create :post_id => post.id + + assert_equal 1, person.readers.count + assert_equal 1, person.readers.length + assert_equal post, person.readers.first.post + assert_equal person, person.readers.first.person + end + + def force_signal37_to_load_all_clients_of_firm companies(:first_firm).clients_of_firm.each {|f| } end -- cgit v1.2.3 From 13a36902718d452b31d13f2f7aba5770e51844a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 30 Jun 2010 12:38:25 +0200 Subject: Add missing CHANGELOG items. --- activerecord/CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 60739fbd91..a1a82fdff5 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [RC1] (unreleased)* +* Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim] + * New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund] * select and order for ActiveRecord now always concatenate nested calls. Use reorder if you want the original order to be overwritten [Santiago Pastorino] -- cgit v1.2.3 From b07e6fdaa0aa07016d1425ada5b7f966142d0212 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 30 Jun 2010 10:38:37 -0700 Subject: Support any mysql-like adapter --- activerecord/lib/active_record/railties/databases.rake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index cfa84cbb76..7882f05d07 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -57,7 +57,7 @@ namespace :db do end rescue case config['adapter'] - when 'mysql' + when /mysql/ @charset = ENV['CHARSET'] || 'utf8' @collation = ENV['COLLATION'] || 'utf8_unicode_ci' creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)} @@ -224,7 +224,7 @@ namespace :db do task :charset => :environment do config = ActiveRecord::Base.configurations[Rails.env || 'development'] case config['adapter'] - when 'mysql' + when /mysql/ ActiveRecord::Base.establish_connection(config) puts ActiveRecord::Base.connection.charset when 'postgresql' @@ -242,7 +242,7 @@ namespace :db do task :collation => :environment do config = ActiveRecord::Base.configurations[Rails.env || 'development'] case config['adapter'] - when 'mysql' + when /mysql/ ActiveRecord::Base.establish_connection(config) puts ActiveRecord::Base.connection.collation else @@ -343,7 +343,7 @@ namespace :db do task :dump => :environment do abcs = ActiveRecord::Base.configurations case abcs[Rails.env]["adapter"] - when "mysql", "oci", "oracle" + when /mysql/, "oci", "oracle" ActiveRecord::Base.establish_connection(abcs[Rails.env]) File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } when "postgresql" @@ -391,7 +391,7 @@ namespace :db do task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when "mysql" + when /mysql/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table| @@ -425,7 +425,7 @@ namespace :db do task :purge => :environment do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when "mysql" + when /mysql/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"]) when "postgresql" @@ -481,7 +481,7 @@ task 'test:prepare' => 'db:test:prepare' def drop_database(config) case config['adapter'] - when 'mysql' + when /mysql/ ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection.drop_database config['database'] when /^sqlite/ -- cgit v1.2.3