aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-11-02 03:52:15 +0530
committerPratik Naik <pratiknaik@gmail.com>2008-11-02 03:52:15 +0530
commit1147453fce0890ea229c3af5f43c909ebe53061e (patch)
tree221d816ef0c908044fd6029950ccad064866ab8f /activerecord
parenta3aa0c17ef8594a0084511f4852be7b5dc66e5e2 (diff)
parent5a02f0bccf55191c2cfbcc69bd8165df6d7a2012 (diff)
downloadrails-1147453fce0890ea229c3af5f43c909ebe53061e.tar.gz
rails-1147453fce0890ea229c3af5f43c909ebe53061e.tar.bz2
rails-1147453fce0890ea229c3af5f43c909ebe53061e.zip
Merge commit 'mainstream/master'
Conflicts: railties/doc/guides/html/layouts_and_rendering.html railties/doc/guides/source/active_record_basics.txt railties/doc/guides/source/layouts_and_rendering.txt
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG7
-rwxr-xr-xactiverecord/lib/active_record/associations.rb21
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb147
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb18
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb11
-rwxr-xr-xactiverecord/test/cases/base_test.rb6
-rw-r--r--activerecord/test/cases/finder_test.rb11
10 files changed, 157 insertions, 123 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index fec110d569..4ca062b535 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,3 +1,10 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster]
+
+* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 [Andreas Korth]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 52f6a04da1..84caca3518 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1596,16 +1596,19 @@ module ActiveRecord
reflection
end
+ mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
+ @@valid_keys_for_has_and_belongs_to_many_association = [
+ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
+ :select, :conditions, :include, :order, :group, :limit, :offset,
+ :uniq,
+ :finder_sql, :delete_sql, :insert_sql,
+ :before_add, :after_add, :before_remove, :after_remove,
+ :extend, :readonly,
+ :validate
+ ]
+
def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(
- :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
- :select, :conditions, :include, :order, :group, :limit, :offset,
- :uniq,
- :finder_sql, :delete_sql, :insert_sql,
- :before_add, :after_add, :before_remove, :after_remove,
- :extend, :readonly,
- :validate
- )
+ options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 1c753524de..177d156834 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -233,7 +233,7 @@ module ActiveRecord
method_name = method_id.to_s
if self.class.private_method_defined?(method_name)
- raise NoMethodError("Attempt to call private method", method_name, args)
+ raise NoMethodError.new("Attempt to call private method", method_name, args)
end
# If we haven't generated any methods yet, generate them, then
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 039e14435f..a36a137f0d 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1764,7 +1764,7 @@ module ActiveRecord #:nodoc:
#
# Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
- def method_missing(method_id, *arguments)
+ def method_missing(method_id, *arguments, &block)
if match = DynamicFinderMatch.match(method_id)
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
@@ -1819,7 +1819,7 @@ module ActiveRecord #:nodoc:
end
end
}, __FILE__, __LINE__
- send(method_id, *arguments)
+ send(method_id, *arguments, &block)
end
else
super
@@ -2410,10 +2410,11 @@ module ActiveRecord #:nodoc:
# be made (since they can't be persisted).
def destroy
unless new_record?
- connection.delete <<-end_sql, "#{self.class.name} Destroy"
- DELETE FROM #{self.class.quoted_table_name}
- WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}
- end_sql
+ connection.delete(
+ "DELETE FROM #{self.class.quoted_table_name} " +
+ "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}",
+ "#{self.class.name} Destroy"
+ )
end
freeze
@@ -2938,7 +2939,7 @@ module ActiveRecord #:nodoc:
end
def object_from_yaml(string)
- return string unless string.is_a?(String)
+ return string unless string.is_a?(String) && string =~ /^---/
YAML::load(string) rescue string
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 2fc50b9bfa..950bd72101 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -15,7 +15,7 @@ module ActiveRecord
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__
def #{method_name}_with_query_dirty(*args)
- clear_query_cache if query_cache_enabled
+ clear_query_cache if @query_cache_enabled
#{method_name}_without_query_dirty(*args)
end
@@ -25,38 +25,24 @@ module ActiveRecord
end
end
- def query_cache_enabled
- Thread.current['query_cache_enabled']
- end
-
- def query_cache_enabled=(flag)
- Thread.current['query_cache_enabled'] = flag
- end
-
- def query_cache
- Thread.current['query_cache']
- end
-
- def query_cache=(cache)
- Thread.current['query_cache'] = cache
- end
+ attr_reader :query_cache, :query_cache_enabled
# Enable the query cache within the block.
def cache
- old, self.query_cache_enabled = query_cache_enabled, true
- self.query_cache ||= {}
+ old, @query_cache_enabled = @query_cache_enabled, true
+ @query_cache ||= {}
yield
ensure
clear_query_cache
- self.query_cache_enabled = old
+ @query_cache_enabled = old
end
# Disable the query cache within the block.
def uncached
- old, self.query_cache_enabled = query_cache_enabled, false
+ old, @query_cache_enabled = @query_cache_enabled, false
yield
ensure
- self.query_cache_enabled = old
+ @query_cache_enabled = old
end
# Clears the query cache.
@@ -66,11 +52,11 @@ module ActiveRecord
# the same SQL query and repeatedly return the same result each time, silently
# undermining the randomness you were expecting.
def clear_query_cache
- query_cache.clear if query_cache
+ @query_cache.clear if @query_cache
end
def select_all_with_query_cache(*args)
- if query_cache_enabled
+ if @query_cache_enabled
cache_sql(args.first) { select_all_without_query_cache(*args) }
else
select_all_without_query_cache(*args)
@@ -78,8 +64,8 @@ module ActiveRecord
end
def columns_with_query_cache(*args)
- if query_cache_enabled
- query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
+ if @query_cache_enabled
+ @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
else
columns_without_query_cache(*args)
end
@@ -88,11 +74,11 @@ module ActiveRecord
private
def cache_sql(sql)
result =
- if query_cache.has_key?(sql)
+ if @query_cache.has_key?(sql)
log_info(sql, "CACHE", 0.0)
- query_cache[sql]
+ @query_cache[sql]
else
- query_cache[sql] = yield
+ @query_cache[sql] = yield
end
if Array === result
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bebab5d05d..60ec01b95e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -68,72 +68,6 @@ module ActiveRecord
super
end
- # Escapes binary strings for bytea input to the database.
- def self.string_to_binary(value)
- if PGconn.respond_to?(:escape_bytea)
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- PGconn.escape_bytea(value) if value
- end
- end
- else
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- if value
- result = ''
- value.each_byte { |c| result << sprintf('\\\\%03o', c) }
- result
- end
- end
- end
- end
- self.class.string_to_binary(value)
- end
-
- # Unescapes bytea output from a database to the binary string it represents.
- def self.binary_to_string(value)
- # In each case, check if the value actually is escaped PostgreSQL bytea output
- # or an unescaped Active Record attribute that was just written.
- if PGconn.respond_to?(:unescape_bytea)
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- PGconn.unescape_bytea(value)
- else
- value
- end
- end
- end
- else
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- result = ''
- i, max = 0, value.size
- while i < max
- char = value[i]
- if char == ?\\
- if value[i+1] == ?\\
- char = ?\\
- i += 1
- else
- char = value[i+1..i+3].oct
- i += 3
- end
- end
- result << char
- i += 1
- end
- result
- else
- value
- end
- end
- end
- end
- self.class.binary_to_string(value)
- end
-
# Maps PostgreSQL-specific data types to logical Rails types.
def simplified_type(field_type)
case field_type
@@ -347,10 +281,78 @@ module ActiveRecord
# QUOTING ==================================================
+ # Escapes binary strings for bytea input to the database.
+ def escape_bytea(value)
+ if PGconn.respond_to?(:escape_bytea)
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ PGconn.escape_bytea(value) if value
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ if value
+ result = ''
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+ result
+ end
+ end
+ end
+ end
+ escape_bytea(value)
+ end
+
+ # Unescapes bytea output from a database to the binary string it represents.
+ # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
+ # on escaped binary output from database drive.
+ def unescape_bytea(value)
+ # In each case, check if the value actually is escaped PostgreSQL bytea output
+ # or an unescaped Active Record attribute that was just written.
+ if PGconn.respond_to?(:unescape_bytea)
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ PGconn.unescape_bytea(value)
+ else
+ value
+ end
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ result = ''
+ i, max = 0, value.size
+ while i < max
+ char = value[i]
+ if char == ?\\
+ if value[i+1] == ?\\
+ char = ?\\
+ i += 1
+ else
+ char = value[i+1..i+3].oct
+ i += 3
+ end
+ end
+ result << char
+ i += 1
+ end
+ result
+ else
+ value
+ end
+ end
+ end
+ end
+ unescape_bytea(value)
+ end
+
# Quotes PostgreSQL-specific data types for SQL input.
def quote(value, column = nil) #:nodoc:
if value.kind_of?(String) && column && column.type == :binary
- "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'"
+ "#{quoted_string_prefix}'#{escape_bytea(value)}'"
elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
"xml '#{quote_string(value)}'"
elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
@@ -463,11 +465,20 @@ module ActiveRecord
# create a 2D array representing the result set
def result_as_array(res) #:nodoc:
+ # check if we have any binary column and if they need escaping
+ unescape_col = []
+ for j in 0...res.nfields do
+ # unescape string passed BYTEA field (OID == 17)
+ unescape_col << ( res.ftype(j)==17 )
+ end
+
ary = []
for i in 0...res.ntuples do
ary << []
for j in 0...res.nfields do
- ary[i] << res.getvalue(i,j)
+ data = res.getvalue(i,j)
+ data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
+ ary[i] << data
end
end
return ary
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 4f96e225c1..2181bdf2dd 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -159,13 +159,19 @@ HEADER
end
def indexes(table, stream)
- indexes = @connection.indexes(table)
- indexes.each do |index|
- stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}"
- stream.print ", :unique => true" if index.unique
+ 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
+
+ ' ' + statment_parts.join(', ')
+ end
+
+ stream.puts add_index_statements.sort.join("\n")
stream.puts
end
- stream.puts unless indexes.empty?
end
end
-end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 160716f944..77ee8d8fc4 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -233,8 +233,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "The pros and cons of programming naked.")
assert !topic.respond_to?(:title)
- assert_raise(NoMethodError) { topic.title }
- topic.send(:title)
+ exception = assert_raise(NoMethodError) { topic.title }
+ assert_equal "Attempt to call private method", exception.message
+ assert_equal "I'm private", topic.send(:title)
end
def test_write_attributes_respect_access_control
@@ -242,7 +243,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new
assert !topic.respond_to?(:title=)
- assert_raise(NoMethodError) { topic.title = "Pants"}
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
+ assert_equal "Attempt to call private method", exception.message
topic.send(:title=, "Very large pants")
end
@@ -251,7 +253,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
- assert_raise(NoMethodError) { topic.title? }
+ exception = assert_raise(NoMethodError) { topic.title? }
+ assert_equal "Attempt to call private method", exception.message
assert topic.send(:title?)
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d512834237..da9f2742d8 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1429,6 +1429,12 @@ class BasicsTest < ActiveRecord::TestCase
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
+
+ def test_serialized_string_attribute
+ myobj = "Yes"
+ topic = Topic.create("content" => myobj).reload
+ assert_equal(myobj, topic.content)
+ end
def test_nil_serialized_attribute_with_class_constraint
myobj = MyObject.new('value1', 'value2')
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 853474916c..153880afbd 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -846,6 +846,17 @@ class FinderTest < ActiveRecord::TestCase
assert !c.new_record?
end
+ def test_find_or_create_should_work_with_block_on_first_call
+ class << Company
+ undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
+ end
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
+ assert_equal "Fortune 1000", c.name
+ assert_equal 1000.to_f, c.rating.to_f
+ assert c.valid?
+ assert !c.new_record?
+ end
+
def test_dynamic_find_or_initialize_from_one_attribute_caches_method
class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }