aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/Rakefile8
-rwxr-xr-xactiverecord/lib/active_record/base.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb2
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rwxr-xr-xactiverecord/test/cases/base_test.rb7
-rw-r--r--activerecord/test/cases/connection_test_mysql.rb26
-rw-r--r--activerecord/test/cases/copy_table_test_sqlite.rb11
-rw-r--r--activerecord/test/cases/method_scoping_test.rb10
-rw-r--r--activerecord/test/cases/named_scope_test.rb20
-rw-r--r--activerecord/test/connections/jdbc_jdbcderby/connection.rb18
-rw-r--r--activerecord/test/connections/jdbc_jdbch2/connection.rb18
-rw-r--r--activerecord/test/connections/jdbc_jdbchsqldb/connection.rb18
-rw-r--r--activerecord/test/connections/jdbc_jdbcmysql/connection.rb26
-rw-r--r--activerecord/test/connections/jdbc_jdbcpostgresql/connection.rb26
-rw-r--r--activerecord/test/connections/jdbc_jdbcsqlite3/connection.rb25
-rw-r--r--activerecord/test/models/post.rb6
-rw-r--r--activerecord/test/models/topic.rb2
-rw-r--r--activerecord/test/schema/schema.rb5
19 files changed, 255 insertions, 11 deletions
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index f47674d5b7..1c7e2603ee 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -32,9 +32,13 @@ task :default => :test
desc 'Run mysql, sqlite, and postgresql tests'
task :test => %w(test_mysql test_sqlite3 test_postgresql)
-for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase )
+for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb )
Rake::TestTask.new("test_#{adapter}") { |t|
- t.libs << "test" << "test/connections/native_#{adapter}"
+ if adapter =~ /jdbc/
+ t.libs << "test" << "test/connections/jdbc_#{adapter}"
+ else
+ t.libs << "test" << "test/connections/native_#{adapter}"
+ end
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
t.verbose = true
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index ebc0b7783f..0efccb66ee 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -927,7 +927,7 @@ module ActiveRecord #:nodoc:
#
# ==== Parameters
#
- # * +id+ - The id of the object you wish to update a counter on.
+ # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
# * +counters+ - An Array of Hashes containing the names of the fields
# to update as keys and the amount to update the field by as values.
#
@@ -941,12 +941,27 @@ module ActiveRecord #:nodoc:
# # SET comment_count = comment_count - 1,
# # action_count = action_count + 1
# # WHERE id = 5
+ #
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
+ # Post.update_counters [10, 15], :comment_count => 1
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = comment_count + 1,
+ # # WHERE id IN (10, 15)
def update_counters(id, counters)
updates = counters.inject([]) { |list, (counter_name, increment)|
sign = increment < 0 ? "-" : "+"
list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
}.join(", ")
- update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
+
+ if id.is_a?(Array)
+ ids_list = id.map {|i| quote_value(i)}.join(', ')
+ condition = "IN (#{ids_list})"
+ else
+ condition = "= #{quote_value(id)}"
+ end
+
+ update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}")
end
# Increment a number field by one, usually representing a count.
@@ -1700,7 +1715,7 @@ module ActiveRecord #:nodoc:
end
join
end
- joins.flatten.uniq
+ joins.flatten.map{|j| j.strip}.uniq
else
joins.collect{|j| safe_to_array(j)}.flatten.uniq
end
@@ -2097,7 +2112,11 @@ module ActiveRecord #:nodoc:
(hash[method].keys + params.keys).uniq.each do |key|
merge = hash[method][key] && params[key] # merge if both scopes have the same key
if key == :conditions && merge
- hash[method][key] = merge_conditions(params[key], hash[method][key])
+ if params[key].is_a?(Hash) && hash[method][key].is_a?(Hash)
+ hash[method][key] = merge_conditions(hash[method][key].deep_merge(params[key]))
+ else
+ hash[method][key] = merge_conditions(params[key], hash[method][key])
+ end
elsif key == :include && merge
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
elsif key == :joins && merge
@@ -2107,7 +2126,7 @@ module ActiveRecord #:nodoc:
end
end
else
- hash[method] = params.merge(hash[method])
+ hash[method] = hash[method].merge(params)
end
else
hash[method] = params
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index b2345fd571..9300df28ee 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -152,6 +152,7 @@ module ActiveRecord
# * <tt>:password</tt> - Defaults to nothing.
# * <tt>:database</tt> - The name of the database. No default, must be provided.
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
+ # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
@@ -563,8 +564,6 @@ module ActiveRecord
private
def connect
- @connection.reconnect = true if @connection.respond_to?(:reconnect=)
-
encoding = @config[:encoding]
if encoding
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
@@ -575,6 +574,10 @@ module ActiveRecord
end
@connection.real_connect(*@connection_options)
+
+ # reconnect must be set after real_connect is called, because real_connect sets it to false internally
+ @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
+
configure_connection
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 9387cf8827..5390f49f04 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -306,7 +306,7 @@ module ActiveRecord
end
def copy_table(from, to, options = {}) #:nodoc:
- options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?)
+ options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
create_table(to, options) do |definition|
@definition = definition
columns(from).each do |column|
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 6a9690ba85..6d750accb0 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -904,7 +904,7 @@ module ActiveRecord
configuration.update(attr_names.extract_options!)
validates_each(attr_names, configuration) do |record, attr_name, value|
- unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
+ unless (value.is_a?(Array) ? value : [value]).collect { |r| r.nil? || r.valid? }.all?
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 0f03dae829..973bb567bd 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -639,6 +639,13 @@ class BasicsTest < ActiveRecord::TestCase
category.reload
assert_not_nil category.categorizations_count
assert_equal 4, category.categorizations_count
+
+ category_2 = categories(:technology)
+ count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0)
+ Category.update_counters([category.id, category_2.id], "categorizations_count" => 2)
+ category.reload; category_2.reload
+ assert_equal count_1 + 2, category.categorizations_count
+ assert_equal count_2 + 2, category_2.categorizations_count
end
def test_update_all
diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb
index 40ddcf5420..f79ee2f1f7 100644
--- a/activerecord/test/cases/connection_test_mysql.rb
+++ b/activerecord/test/cases/connection_test_mysql.rb
@@ -2,9 +2,24 @@ 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')
@@ -27,4 +42,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase
@connection.verify!
assert @connection.active?
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
index f0cfb67866..72bd7e2dab 100644
--- a/activerecord/test/cases/copy_table_test_sqlite.rb
+++ b/activerecord/test/cases/copy_table_test_sqlite.rb
@@ -46,6 +46,17 @@ class CopyTableTest < ActiveRecord::TestCase
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))
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 80a06116ad..71e2ce8790 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -186,6 +186,16 @@ class MethodScopingTest < ActiveRecord::TestCase
assert_equal authors(:david).attributes, scoped_authors.first.attributes
end
+ def test_scoped_find_strips_spaces_from_string_joins_and_eliminates_duplicate_string_joins
+ scoped_authors = Author.with_scope(:find => { :joins => ' INNER JOIN posts ON posts.author_id = authors.id '}) do
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => ['INNER JOIN posts ON posts.author_id = authors.id'], :conditions => 'posts.id = 1')
+ end
+ assert scoped_authors.include?(authors(:david))
+ assert !scoped_authors.include?(authors(:mary))
+ assert_equal 1, scoped_authors.size
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
+ end
+
def test_scoped_count_include
# with the include, will retrieve only developers for the given project
Developer.with_scope(:find => { :include => :projects }) do
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index bab842cf66..e1e27fa130 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -277,6 +277,26 @@ class NamedScopeTest < ActiveRecord::TestCase
post = Post.find(1)
assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
end
+
+ def test_chanining_should_use_latest_conditions_when_creating
+ post1 = Topic.rejected.approved.new
+ assert post1.approved?
+
+ post2 = Topic.approved.rejected.new
+ assert ! post2.approved?
+ end
+
+ def test_chanining_should_use_latest_conditions_when_searching
+ # Normal hash conditions
+ assert_equal Topic.all(:conditions => {:approved => true}), Topic.rejected.approved.all
+ assert_equal Topic.all(:conditions => {:approved => false}), Topic.approved.rejected.all
+
+ # Nested hash conditions with same keys
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all
+
+ # Nested hash conditions with different keys
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq
+ end
end
class DynamicScopeMatchTest < ActiveRecord::TestCase
diff --git a/activerecord/test/connections/jdbc_jdbcderby/connection.rb b/activerecord/test/connections/jdbc_jdbcderby/connection.rb
new file mode 100644
index 0000000000..222ef5db38
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbcderby/connection.rb
@@ -0,0 +1,18 @@
+print "Using Derby via JRuby, activerecord-jdbc-adapter and activerecord-jdbcderby-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'jdbcderby',
+ :database => 'activerecord_unittest'
+ },
+ 'arunit2' => {
+ :adapter => 'jdbcderby',
+ :database => 'activerecord_unittest2'
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
diff --git a/activerecord/test/connections/jdbc_jdbch2/connection.rb b/activerecord/test/connections/jdbc_jdbch2/connection.rb
new file mode 100644
index 0000000000..9d2875e8e7
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbch2/connection.rb
@@ -0,0 +1,18 @@
+print "Using H2 via JRuby, activerecord-jdbc-adapter and activerecord-jdbch2-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'jdbch2',
+ :database => 'activerecord_unittest'
+ },
+ 'arunit2' => {
+ :adapter => 'jdbch2',
+ :database => 'activerecord_unittest2'
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
diff --git a/activerecord/test/connections/jdbc_jdbchsqldb/connection.rb b/activerecord/test/connections/jdbc_jdbchsqldb/connection.rb
new file mode 100644
index 0000000000..fa943c2c76
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbchsqldb/connection.rb
@@ -0,0 +1,18 @@
+print "Using HSQLDB via JRuby, activerecord-jdbc-adapter and activerecord-jdbchsqldb-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'jdbchsqldb',
+ :database => 'activerecord_unittest'
+ },
+ 'arunit2' => {
+ :adapter => 'jdbchsqldb',
+ :database => 'activerecord_unittest2'
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
diff --git a/activerecord/test/connections/jdbc_jdbcmysql/connection.rb b/activerecord/test/connections/jdbc_jdbcmysql/connection.rb
new file mode 100644
index 0000000000..e2517a50eb
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbcmysql/connection.rb
@@ -0,0 +1,26 @@
+print "Using MySQL via JRuby, activerecord-jdbc-adapter and activerecord-jdbcmysql-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+# GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost';
+# GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost';
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'jdbcmysql',
+ :username => 'rails',
+ :encoding => 'utf8',
+ :database => 'activerecord_unittest',
+ },
+ 'arunit2' => {
+ :adapter => 'jdbcmysql',
+ :username => 'rails',
+ :database => 'activerecord_unittest2'
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
+
diff --git a/activerecord/test/connections/jdbc_jdbcpostgresql/connection.rb b/activerecord/test/connections/jdbc_jdbcpostgresql/connection.rb
new file mode 100644
index 0000000000..0685da4433
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbcpostgresql/connection.rb
@@ -0,0 +1,26 @@
+print "Using Postgrsql via JRuby, activerecord-jdbc-adapter and activerecord-postgresql-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+# createuser rails --createdb --no-superuser --no-createrole
+# createdb -O rails activerecord_unittest
+# createdb -O rails activerecord_unittest2
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'jdbcpostgresql',
+ :username => ENV['USER'] || 'rails',
+ :database => 'activerecord_unittest'
+ },
+ 'arunit2' => {
+ :adapter => 'jdbcpostgresql',
+ :username => ENV['USER'] || 'rails',
+ :database => 'activerecord_unittest2'
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
+
diff --git a/activerecord/test/connections/jdbc_jdbcsqlite3/connection.rb b/activerecord/test/connections/jdbc_jdbcsqlite3/connection.rb
new file mode 100644
index 0000000000..26d4676ff3
--- /dev/null
+++ b/activerecord/test/connections/jdbc_jdbcsqlite3/connection.rb
@@ -0,0 +1,25 @@
+print "Using SQLite3 via JRuby, activerecord-jdbc-adapter and activerecord-jdbcsqlite3-adapter\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+class SqliteError < StandardError
+end
+
+BASE_DIR = FIXTURES_ROOT
+sqlite_test_db = "#{BASE_DIR}/fixture_database.sqlite3"
+sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
+
+def make_connection(clazz, db_file)
+ ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'jdbcsqlite3', :database => db_file, :timeout => 5000 } }
+ unless File.exist?(db_file)
+ puts "SQLite3 database not found at #{db_file}. Rebuilding it."
+ sqlite_command = %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
+ puts "Executing '#{sqlite_command}'"
+ raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
+ end
+ clazz.establish_connection(clazz.name)
+end
+
+make_connection(ActiveRecord::Base, sqlite_test_db)
+make_connection(Course, sqlite_test_db2)
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index e0d8be676a..388fff8fba 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -17,6 +17,12 @@ class Post < ActiveRecord::Base
has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
+ named_scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
+ named_scope :with_very_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'VerySpecialComment'} }
+ named_scope :with_post, lambda {|post_id|
+ { :joins => :comments, :conditions => {:comments => {:post_id => post_id} } }
+ }
+
has_many :comments, :order => "body" do
def find_most_recent
find(:first, :order => "id DESC")
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 39ca1bf42a..08bb24ed03 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -4,6 +4,8 @@ class Topic < ActiveRecord::Base
{ :conditions => ['written_on < ?', time] }
}
named_scope :approved, :conditions => {:approved => true}
+ named_scope :rejected, :conditions => {:approved => false}
+
named_scope :by_lifo, :conditions => {:author_name => 'lifo'}
named_scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}}
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 094932d375..d44faf04cc 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -154,6 +154,11 @@ ActiveRecord::Schema.define do
t.string :name
end
+ create_table :goofy_string_id, :force => true, :id => false do |t|
+ t.string :id, :null => false
+ t.string :info
+ end
+
create_table :items, :force => true do |t|
t.column :name, :integer
end