aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorRizwan Reza <rizwanreza@gmail.com>2010-05-17 02:40:15 +0430
committerRizwan Reza <rizwanreza@gmail.com>2010-05-17 02:40:15 +0430
commitd148a6f6ba5f8ee65905f12cd2601fcc377d4852 (patch)
tree4bcb5e7ad47cfb9a9bb14ffe7c9e003d4646d64c /activerecord
parente1c773006969abea3c0619fbdc7e32c121b6085f (diff)
parent6b4e0cc526f55b5532cf99292c94f0a4db53b16f (diff)
downloadrails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.tar.gz
rails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.tar.bz2
rails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.zip
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG10
-rwxr-xr-xactiverecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb51
-rw-r--r--activerecord/lib/active_record/fixtures.rb1
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb11
-rw-r--r--activerecord/lib/active_record/named_scope.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb17
-rw-r--r--activerecord/lib/active_record/relation/batches.rb7
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb11
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb3
-rw-r--r--activerecord/lib/active_record/validations.rb53
-rw-r--r--activerecord/test/cases/active_schema_test_mysql.rb17
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/autosave_association_test.rb4
-rwxr-xr-xactiverecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/batches_test.rb14
-rw-r--r--activerecord/test/cases/calculations_test.rb3
-rw-r--r--activerecord/test/cases/dirty_test.rb9
-rw-r--r--activerecord/test/cases/finder_test.rb4
-rw-r--r--activerecord/test/cases/fixtures_test.rb5
-rw-r--r--activerecord/test/cases/method_scoping_test.rb31
-rw-r--r--activerecord/test/cases/migration_test.rb20
-rw-r--r--activerecord/test/cases/named_scope_test.rb7
-rw-r--r--activerecord/test/cases/validations_test.rb23
-rw-r--r--activerecord/test/models/post.rb1
-rw-r--r--activerecord/test/models/reply.rb5
34 files changed, 285 insertions, 110 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 3f42fa34ef..d8f68d7c28 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,15 @@
*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]
+
+ Example:
+
+ add_index(:accounts, :name, :name => 'by_name', :length => 10)
+ => CREATE INDEX by_name ON accounts(name(10))
+
+ add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
+ => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
+
* find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune]
* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 9d39a0c49b..f23d881c7b 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1399,7 +1399,7 @@ module ActiveRecord
primary_key = reflection.source_reflection.primary_key_name
send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
else
- send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id)
+ send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map!(&:id)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index dd44bd8d51..cf4594ad7f 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -8,7 +8,7 @@ module ActiveRecord
include AttributeMethods::Write
included do
- if self < Timestamp
+ if self < ::ActiveRecord::Timestamp
raise "You cannot include Dirty after Timestamp"
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 411330dda2..82d94b848a 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -6,7 +6,7 @@ module ActiveRecord
# Returns this record's primary key value wrapped in an Array
# or nil if the record is a new_record?
def to_key
- new_record? ? nil : [ send(self.class.primary_key) ]
+ new_record? ? nil : [ id ]
end
module ClassMethods
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 650a91b385..c02af328c1 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -668,7 +668,6 @@ module ActiveRecord #:nodoc:
name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
end
- @quoted_table_name = nil
set_table_name(name)
name
end
@@ -702,6 +701,7 @@ module ActiveRecord #:nodoc:
# set_table_name "project"
# end
def set_table_name(value = nil, &block)
+ @quoted_table_name = nil
define_attr_method :table_name, value, &block
end
alias :table_name= :set_table_name
@@ -1190,7 +1190,11 @@ module ActiveRecord #:nodoc:
# default_scope order('last_name, first_name')
# end
def default_scope(options = {})
- self.default_scoping << construct_finder_arel(options)
+ self.default_scoping << construct_finder_arel(options, default_scoping.pop)
+ end
+
+ def clear_default_scope
+ self.default_scoping.clear
end
def scoped_methods #:nodoc:
@@ -1463,6 +1467,7 @@ module ActiveRecord #:nodoc:
@attributes_cache = {}
@new_record = true
ensure_proper_type
+ @changed_attributes = other.changed_attributes.dup
if scope = self.class.send(:current_scoped_methods)
create_with = scope.scope_for_create
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 6c477e48ce..7d58bc2adf 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -258,7 +258,7 @@ module ActiveRecord
end
end
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
end
# Abstract representation of a column definition. Instances of this type
@@ -494,7 +494,7 @@ module ActiveRecord
end
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{column_type}(*args) # def string(*args)
options = args.extract_options! # options = args.extract_options!
column_names = args # column_names = args
@@ -694,7 +694,7 @@ module ActiveRecord
# t.string(:goat)
# t.string(:goat, :sheep)
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{column_type}(*args) # def string(*args)
options = args.extract_options! # options = args.extract_options!
column_names = args # column_names = args
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 e8cba1bd41..1255ef09be 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -256,18 +256,32 @@ module ActiveRecord
# name.
#
# ===== Examples
+ #
# ====== Creating a simple index
# add_index(:suppliers, :name)
# generates
# CREATE INDEX suppliers_name_index ON suppliers(name)
+ #
# ====== Creating a unique index
# add_index(:accounts, [:branch_id, :party_id], :unique => true)
# generates
# CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
+ #
# ====== Creating a named index
# add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
# generates
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
+ #
+ # ====== Creating an index with specific key length
+ # add_index(:accounts, :name, :name => 'by_name', :length => 10)
+ # generates
+ # CREATE INDEX by_name ON accounts(name(10))
+ #
+ # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
+ # generates
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
+ #
+ # Note: SQLite doesn't support index length
def add_index(table_name, column_name, options = {})
column_names = Array.wrap(column_name)
index_name = index_name(table_name, :column => column_names)
@@ -278,7 +292,9 @@ module ActiveRecord
else
index_type = options
end
- quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
+
+ quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
end
@@ -430,6 +446,11 @@ module ActiveRecord
end
protected
+ # Overridden by the mysql adapter for supporting index lengths
+ def quoted_columns_for_index(column_names, options = {})
+ column_names.map {|name| quote_column_name(name) }
+ end
+
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 55d9d20bb5..e12924e63f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -15,26 +15,26 @@ module MysqlCompat #:nodoc:
# Ruby driver has a version string and returns null values in each_hash
# C driver >= 2.7 returns null values in each_hash
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
- target.class_eval <<-'end_eval'
- def all_hashes # def all_hashes
- rows = [] # rows = []
- each_hash { |row| rows << row } # each_hash { |row| rows << row }
- rows # rows
- end # end
+ target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ each_hash { |row| rows << row } # each_hash { |row| rows << row }
+ rows # rows
+ end # end
end_eval
# adapters before 2.7 don't have a version constant
# and don't return null values in each_hash
else
- target.class_eval <<-'end_eval'
- def all_hashes # def all_hashes
- rows = [] # rows = []
- all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
- fields[f.name] = nil; fields # fields[f.name] = nil; fields
- } # }
- each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
- rows # rows
- end # end
+ target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
+ fields[f.name] = nil; fields # fields[f.name] = nil; fields
+ } # }
+ each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
+ rows # rows
+ end # end
end_eval
end
@@ -461,10 +461,11 @@ module ActiveRecord
if current_index != row[2]
next if row[2] == "PRIMARY" # skip the primary key
current_index = row[2]
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
end
indexes.last.columns << row[4]
+ indexes.last.lengths << row[7]
end
result.free
indexes
@@ -512,7 +513,7 @@ module ActiveRecord
def change_column(table_name, column_name, type, options = {}) #:nodoc:
column = column_for(table_name, column_name)
- unless options_include_default?(options)
+ if has_default?(type) && !options_include_default?(options)
options[:default] = column.default
end
@@ -594,6 +595,18 @@ module ActiveRecord
end
protected
+ def quoted_columns_for_index(column_names, options = {})
+ length = options[:length] if options.is_a?(Hash)
+
+ quoted_column_names = case length
+ when Hash
+ column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
+ when Fixnum
+ column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
+ else
+ column_names.map {|name| quote_column_name(name) }
+ end
+ end
def translate_exception(exception, message)
return super unless exception.respond_to?(:errno)
@@ -662,6 +675,10 @@ module ActiveRecord
end
column
end
+
+ def has_default?(sql_type)
+ sql_type =~ :binary || sql_type == :text #mysql forbids defaults on blob and text columns
+ end
end
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 4bf33c3856..8099aaa7f7 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -891,6 +891,7 @@ module ActiveRecord
instances.size == 1 ? instances.first : instances
end
+ private table_name
end
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 71057efa15..ceb0902fde 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -113,12 +113,11 @@ module ActiveRecord
lock_col = self.class.locking_column
previous_value = send(lock_col).to_i
- affected_rows = connection.delete(
- "DELETE FROM #{self.class.quoted_table_name} " +
- "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " +
- "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}",
- "#{self.class.name} Destroy"
- )
+ table = self.class.arel_table
+ predicate = table[self.class.primary_key].eq(id)
+ predicate = predicate.and(table[self.class.locking_column].eq(previous_value))
+
+ affected_rows = self.class.unscoped.where(predicate).delete_all
unless affected_rows == 1
raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}"
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index aeed52e72a..3d8f4a030b 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -99,11 +99,7 @@ module ActiveRecord
block_given? ? relation.extending(Module.new(&block)) : relation
end
- singleton_class.instance_eval do
- define_method name do |*args|
- scopes[name].call(*args)
- end
- end
+ singleton_class.send :define_method, name, &scopes[name]
end
def named_scope(*args, &block)
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 898df0a67a..a32fb7d399 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -15,6 +15,12 @@ module ActiveRecord
config.generators.orm :active_record, :migration => true,
:timestamps => true
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
+ "ActiveRecord::QueryCache"
+
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement"
+
rake_tasks do
load "active_record/railties/databases.rake"
end
@@ -58,16 +64,9 @@ module ActiveRecord
end
end
- # Setup database middleware after initializers have run
- initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app|
- middleware = app.config.middleware
- middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::QueryCache
- middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::ConnectionAdapters::ConnectionManagement
- end
-
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
- ActiveSupport.on_load(:active_record) do
- unless app.config.cache_classes
+ unless app.config.cache_classes
+ ActiveSupport.on_load(:active_record) do
ActionDispatch::Callbacks.after do
ActiveRecord::Base.reset_subclasses
ActiveRecord::Base.clear_reloadable_connections!
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 1c61e7d450..412be895c4 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -71,7 +71,12 @@ module ActiveRecord
yield records
break if records.size < batch_size
- records = relation.where(primary_key.gt(records.last.id)).all
+
+ if primary_key_offset = records.last.id
+ records = relation.where(primary_key.gt(primary_key_offset)).all
+ else
+ raise "Primary key not included in the custom select clause"
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 858d298470..44baeb6c84 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -45,9 +45,8 @@ module ActiveRecord
calculate(:count, column_name, options)
end
- # Calculates the average value on a given column. The value is returned as
- # a float, or +nil+ if there's no row. See +calculate+ for examples with
- # options.
+ # Calculates the average value on a given column. Returns +nil+ if there's
+ # no row. See +calculate+ for examples with options.
#
# Person.average('age') # => 35.8
def average(column_name, options = {})
@@ -241,9 +240,9 @@ module ActiveRecord
def type_cast_calculated_value(value, column, operation = nil)
if value.is_a?(String) || value.nil?
case operation
- when 'count' then value.to_i
- when 'sum' then type_cast_using_column(value || '0', column)
- when 'average' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d
+ when 'count' then value.to_i
+ when 'sum' then type_cast_using_column(value || '0', column)
+ when 'average' then value.try(:to_d)
else type_cast_using_column(value, column)
end
else
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8d8bb659e1..6782554854 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -10,7 +10,7 @@ module ActiveRecord
attr_accessor :"#{query_method}_values"
next if [:where, :having].include?(query_method)
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
@@ -22,7 +22,7 @@ module ActiveRecord
end
[:where, :having].each do |query_method|
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
@@ -36,7 +36,7 @@ module ActiveRecord
ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
attr_accessor :"#{query_method}_value"
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(value = true, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index c8e1b4f53a..cd54653581 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -178,6 +178,9 @@ HEADER
statment_parts << (':name => ' + index.name.inspect)
statment_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?
+
' ' + statment_parts.join(', ')
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 55c4236874..be64e00bd1 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -36,49 +36,40 @@ module ActiveRecord
# The validation process on save can be skipped by passing false. The regular Base#save method is
# replaced with this when the validations module is mixed in, which it is by default.
- def save(options=nil)
- return super if valid?(options)
- false
- end
-
- def save_without_validation!
- save!(:validate => false)
+ def save(options={})
+ perform_validations(options) ? super : false
end
# Attempts to save the record just like Base#save but will raise a RecordInvalid exception instead of returning false
# if the record is not valid.
- def save!(options = nil)
- return super if valid?(options)
- raise RecordInvalid.new(self)
+ def save!(options={})
+ perform_validations(options) ? super : raise(RecordInvalid.new(self))
end
# Runs all the specified validations and returns true if no errors were added otherwise false.
- def valid?(options = nil)
- perform_validation = case options
- when NilClass
- true
- when Hash
- options[:validate] != false
- else
- ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
- options
- end
+ def valid?(context = nil)
+ context ||= (new_record? ? :create : :update)
+ super(context)
- if perform_validation
- errors.clear
+ deprecated_callback_method(:validate)
+ deprecated_callback_method(:"validate_on_#{context}")
- self.validation_context = new_record? ? :create : :update
- _run_validate_callbacks
+ errors.empty?
+ end
- deprecated_callback_method(:validate)
+ protected
- if new_record?
- deprecated_callback_method(:validate_on_create)
- else
- deprecated_callback_method(:validate_on_update)
- end
+ def perform_validations(options={})
+ perform_validation = case options
+ when Hash
+ options[:validate] != false
+ else
+ ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
+ options
+ end
- errors.empty?
+ if perform_validation
+ valid?(options.is_a?(Hash) ? options[:context] : nil)
else
true
end
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb
index 9aff538ce9..f4d123be15 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/active_schema_test_mysql.rb
@@ -15,6 +15,23 @@ class ActiveSchemaTest < ActiveRecord::TestCase
end
end
+ def test_add_index
+ 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})
+ end
+
def test_drop_table
assert_equal "DROP TABLE `people`", drop_table(:people)
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index e8db6d5dab..2beb3f8365 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -17,7 +17,7 @@ module Remembered
module ClassMethods
def remembered; @@remembered ||= []; end
- def rand; @@remembered.rand; end
+ def random_element; @@remembered.random_element; end
end
end
@@ -79,14 +79,14 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
end
1.upto(NUM_SIMPLE_OBJS) do
- PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id)
- PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id)
+ PaintColor.create!(:non_poly_one_id => NonPolyOne.random_element.id)
+ PaintTexture.create!(:non_poly_two_id => NonPolyTwo.random_element.id)
end
1.upto(NUM_SHAPE_EXPRESSIONS) do
- shape_type = [Circle, Square, Triangle].rand
- paint_type = [PaintColor, PaintTexture].rand
- ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id,
- :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id)
+ shape_type = [Circle, Square, Triangle].random_element
+ paint_type = [PaintColor, PaintTexture].random_element
+ ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.random_element.id,
+ :paint_type => paint_type.to_s, :paint_id => paint_type.random_element.id)
end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index b55b08bf9d..6e47967696 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -900,6 +900,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !company.clients.loaded?
end
+ def test_get_ids_ignores_include_option
+ assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
+ end
+
def test_get_ids_for_unloaded_finder_sql_associations_loads_them
company = companies(:first_firm)
assert !company.clients_using_sql.loaded?
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 8bdf8bcd55..dca72be4fd 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -289,7 +289,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_array_methods_called_by_method_missing
- assert true, authors(:david).categories.any? { |category| category.name == 'General' }
+ assert authors(:david).categories.any? { |category| category.name == 'General' }
assert_nothing_raised { authors(:david).categories.sort }
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 2995cc6f72..5cc5dff633 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -765,7 +765,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
@ship.destroy
@pirate.reload.catchphrase = "Arr"
@pirate.save
- assert 'Arr', @pirate.reload.catchphrase
+ assert_equal 'Arr', @pirate.reload.catchphrase
end
def test_should_automatically_save_the_associated_model
@@ -885,7 +885,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
@pirate.destroy
@ship.reload.name = "The Vile Insanity"
@ship.save
- assert 'The Vile Insanity', @ship.reload.name
+ assert_equal 'The Vile Insanity', @ship.reload.name
end
def test_should_automatically_save_the_associated_model
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index bbc4e543d5..b7ae619787 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1793,6 +1793,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "bar", k.table_name
end
+ def test_quoted_table_name_after_set_table_name
+ klass = Class.new(ActiveRecord::Base)
+
+ klass.set_table_name "foo"
+ assert_equal "foo", klass.table_name
+ assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
+
+ klass.set_table_name "bar"
+ assert_equal "bar", klass.table_name
+ assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
+ end
+
def test_set_table_name_with_block
k = Class.new( ActiveRecord::Base )
k.set_table_name { "ks" }
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 83deabb5b7..dcc49e12ca 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -17,6 +17,20 @@ class EachTest < ActiveRecord::TestCase
end
end
+ def test_each_should_raise_if_select_is_set_without_id
+ assert_raise(RuntimeError) do
+ Post.find_each(:select => :title, :batch_size => 1) { |post| post }
+ end
+ end
+
+ def test_each_should_execute_if_id_is_in_select
+ assert_queries(4) do
+ Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
+ assert_kind_of Post, post
+ end
+ end
+ end
+
def test_each_should_raise_if_the_order_is_set
assert_raise(RuntimeError) do
Post.find_each(:order => "title") { |post| post }
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 28a1ae5feb..8473150338 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -20,8 +20,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_average_field
value = Account.average(:credit_limit)
- assert_kind_of BigDecimal, value
- assert_equal BigDecimal.new('53.0'), value
+ assert_equal 53.0, value
end
def test_should_return_nil_as_average
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 7a17ef1ee0..3ea2948f62 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -338,6 +338,15 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.changed?
end
+ def test_cloned_objects_should_not_copy_dirty_flag_from_creator
+ pirate = Pirate.create!(:catchphrase => "shiver me timbers")
+ pirate_clone = pirate.clone
+ pirate_clone.reset_catchphrase!
+ pirate.catchphrase = "I love Rum"
+ assert pirate.catchphrase_changed?
+ assert !pirate_clone.catchphrase_changed?
+ end
+
def test_reverted_changes_are_not_dirty
phrase = "shiver me timbers"
pirate = Pirate.create!(:catchphrase => phrase)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index e78db8969d..c73ad50a71 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -722,10 +722,10 @@ class FinderTest < ActiveRecord::TestCase
def test_find_all_by_one_attribute_with_options
topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
- assert topics(:first), topics.last
+ assert_equal topics(:first), topics.last
topics = Topic.find_all_by_content("Have a nice day", :order => "id")
- assert topics(:first), topics.first
+ assert_equal topics(:first), topics.first
end
def test_find_all_by_array_attribute
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 3ce23209cc..8008b86f81 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -256,6 +256,11 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
def test_fixtures_from_root_yml_without_instantiation
assert !defined?(@unknown), "@unknown is not defined"
end
+
+ def test_visibility_of_accessor_method
+ assert_equal false, respond_to?(:topics, false), "should be private method"
+ assert_equal true, respond_to?(:topics, true), "confirm to respond surely"
+ end
def test_accessor_methods
assert_equal "The First Topic", topics(:first).title
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index a3b496a0e6..3a6354ec6d 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -587,6 +587,18 @@ class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
end
end
+class ClearDefaultScopeTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_should_clear_default_scope
+ klass = Class.new(DeveloperCalledDavid)
+ klass.__send__ :clear_default_scope
+ expected = Developer.all.collect { |dev| dev.name }
+ actual = klass.all.collect { |dev| dev.name }
+ assert_equal expected, actual
+ end
+end
+
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts
@@ -615,15 +627,30 @@ class DefaultScopingTest < ActiveRecord::TestCase
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, {}
+ klass.send :default_scope, :limit => 1
# Scopes added on children should append to parent scope
- assert klass.scoped.order_values.blank?
+ assert_equal 1, klass.scoped.limit_value
+ assert_equal ['salary DESC'], klass.scoped.order_values
# Parent should still have the original scope
+ assert_equal 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 }
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index a3d1ceaa1f..768a44f6df 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -92,6 +92,14 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
end
# quoting
@@ -852,6 +860,18 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal "Tester", Person.new.first_name
end
+ unless current_adapter?(:PostgreSQLAdapter)
+ def test_change_column_type_default_should_change
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
+ assert !old_columns.find { |c| c.name == 'data' }
+
+ assert_nothing_raised do
+ Person.connection.add_column "people", "data", :string, :default => ''
+ Person.connection.change_column "people", "data", :binary
+ end
+ end
+ end
+
def test_change_column_quotes_column_names
Person.connection.create_table :testings do |t|
t.column :select, :string
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index e4cafad11e..2007f5492f 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -142,6 +142,11 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
end
+ def test_named_scope_with_STI
+ assert_equal 3,Post.containing_the_letter_a.count
+ assert_equal 1,SpecialPost.containing_the_letter_a.count
+ end
+
def test_has_many_through_associations_have_access_to_named_scopes
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
assert !Comment.containing_the_letter_e.empty?
@@ -296,7 +301,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_rand_should_select_a_random_object_from_proxy
- assert_kind_of Topic, Topic.approved.rand
+ assert_kind_of Topic, Topic.approved.random_element
end
def test_should_use_where_in_query_for_named_scope
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index e1fb911cc9..3a34df2426 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -62,6 +62,23 @@ class ValidationsTest < ActiveRecord::TestCase
assert_equal ["is Wrong Update"], r.errors[:title], "A reply with a bad content should contain an error"
end
+ def test_error_on_given_context
+ r = WrongReply.new
+ assert !r.valid?(:special_case)
+ assert "Invalid", r.errors[:title].join
+
+ r.title = "secret"
+ r.content = "Good"
+ assert r.valid?(:special_case)
+
+ r.title = nil
+ assert !r.save(:context => :special_case)
+ assert "Invalid", r.errors[:title].join
+
+ r.title = "secret"
+ assert r.save(:context => :special_case)
+ end
+
def test_invalid_record_exception
assert_raise(ActiveRecord::RecordInvalid) { WrongReply.create! }
assert_raise(ActiveRecord::RecordInvalid) { WrongReply.new.save! }
@@ -135,12 +152,6 @@ class ValidationsTest < ActiveRecord::TestCase
end
end
- def test_create_without_validation_bang
- count = WrongReply.count
- assert_nothing_raised { WrongReply.new.save_without_validation! }
- assert count+1, WrongReply.count
- end
-
def test_validates_acceptance_of_with_non_existant_table
Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index d092c4bf09..dd06822cfd 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -69,6 +69,7 @@ class Post < ActiveRecord::Base
has_many :authors, :through => :categorizations
has_many :readers
+ has_many :readers_with_person, :include => :person, :class_name => "Reader"
has_many :people, :through => :readers
has_many :people_with_callbacks, :source=>:person, :through => :readers,
:before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index 264a49b465..6cc9ee038a 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -17,6 +17,7 @@ class WrongReply < Reply
validate :check_empty_title
validate :check_content_mismatch, :on => :create
validate :check_wrong_update, :on => :update
+ validate :check_title_is_secret, :on => :special_case
def check_empty_title
errors[:title] << "Empty" unless attribute_present?("title")
@@ -39,6 +40,10 @@ class WrongReply < Reply
def check_wrong_update
errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update"
end
+
+ def check_title_is_secret
+ errors[:title] << "Invalid" unless title == "secret"
+ end
end
class SillyReply < Reply