aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb6
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb21
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb6
-rw-r--r--activerecord/lib/active_record/no_touching.rb52
-rw-r--r--activerecord/lib/active_record/store.rb7
14 files changed, 117 insertions, 44 deletions
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 56331bbb0b..31b8d27892 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -163,7 +163,7 @@ module ActiveRecord
delete_through_records(records)
- if source_reflection.options[:counter_cache]
+ if source_reflection.options[:counter_cache] && method != :destroy
counter = source_reflection.counter_cache_column
klass.decrement_counter counter, records.map(&:id)
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 0008600418..944caacab6 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -26,11 +26,13 @@ module ActiveRecord
load_target
return self.target if !(target || record)
- if (target != record) || record.changed?
+
+ assigning_another_record = target != record
+ if assigning_another_record || record.changed?
save &&= owner.persisted?
transaction_if(save) do
- remove_target!(options[:dependent]) if target && !target.destroyed?
+ remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
if record
set_owner_attributes(record)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 69a9eabefb..e05e22ebb0 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -295,6 +295,7 @@ module ActiveRecord #:nodoc:
extend Delegation::DelegateCache
include Persistence
+ include NoTouching
include ReadonlyAttributes
include ModelSchema
include Inheritance
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 026b1e3275..8aa1ce5c04 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -17,6 +17,7 @@ module ActiveRecord
autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
autoload :IndexDefinition
autoload :ColumnDefinition
+ autoload :ChangeColumnDefinition
autoload :TableDefinition
autoload :Table
autoload :AlterTable
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 138ab811dc..ee6ca4fb6f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -206,6 +206,12 @@ module ActiveRecord
true
end
+ def type_cast(value, column)
+ return super unless value == true || value == false
+
+ value ? 1 : 0
+ end
+
# MySQL 4 technically support transaction isolation, but it is affected by a bug
# where the transaction level gets persisted for the whole session:
#
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 88c9494fc6..7b18b95c44 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -160,12 +160,6 @@ module ActiveRecord
# QUOTING ==================================================
- def type_cast(value, column)
- return super unless value == true || value == false
-
- value ? 1 : 0
- end
-
def quote_string(string) #:nodoc:
@connection.quote(string)
end
@@ -476,7 +470,11 @@ module ActiveRecord
def exec_stmt(sql, name, binds)
cache = {}
- log(sql, name, binds) do
+ type_casted_binds = binds.map { |col, val|
+ [col, type_cast(val, col)]
+ }
+
+ log(sql, name, type_casted_binds) do
if binds.empty?
stmt = @connection.prepare(sql)
else
@@ -487,7 +485,7 @@ module ActiveRecord
end
begin
- stmt.execute(*binds.map { |col, val| type_cast(val, col) })
+ stmt.execute(*type_casted_binds.map { |_, val| val })
rescue Mysql::Error => e
# Older versions of MySQL leave the prepared statement in a bad
# place when an error occurs. To support older mysql versions, we
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index e9daa5d7ff..c1f978a081 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -86,8 +86,11 @@ module ActiveRecord
case value
when Range
- return super(value, column) unless /range$/ =~ column.sql_type
- PostgreSQLColumn.range_to_string(value)
+ if /range$/ =~ column.sql_type
+ PostgreSQLColumn.range_to_string(value)
+ else
+ super(value, column)
+ end
when NilClass
if column.array && array_member
'NULL'
@@ -101,12 +104,21 @@ module ActiveRecord
when 'point' then PostgreSQLColumn.point_to_string(value)
when 'json' then PostgreSQLColumn.json_to_string(value)
else
- return super(value, column) unless column.array
- PostgreSQLColumn.array_to_string(value, column, self)
+ if column.array
+ PostgreSQLColumn.array_to_string(value, column, self)
+ else
+ super(value, column)
+ end
end
when String
- return super(value, column) unless 'bytea' == column.sql_type
- { :value => value, :format => 1 }
+ if 'bytea' == column.sql_type
+ # Return a bind param hash with format as binary.
+ # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
+ # for more information
+ { value: value, format: 1 }
+ else
+ super(value, column)
+ end
when Hash
case column.sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
@@ -114,8 +126,11 @@ module ActiveRecord
else super(value, column)
end
when IPAddr
- return super(value, column) unless ['inet','cidr'].include? column.sql_type
- PostgreSQLColumn.cidr_to_string(value)
+ if %w(inet cidr).include? column.sql_type
+ PostgreSQLColumn.cidr_to_string(value)
+ else
+ super(value, column)
+ end
else
super(value, column)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 3668aecd4b..adeb57d913 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -631,7 +631,6 @@ module ActiveRecord
true
end
- # Returns true.
def supports_explain?
true
end
@@ -783,11 +782,12 @@ module ActiveRecord
def exec_cache(sql, name, binds)
stmt_key = prepare_statement(sql)
+ type_casted_binds = binds.map { |col, val|
+ [col, type_cast(val, col)]
+ }
- log(sql, name, binds, stmt_key) do
- @connection.send_query_prepared(stmt_key, binds.map { |col, val|
- type_cast(val, col)
- })
+ log(sql, name, type_casted_binds, stmt_key) do
+ @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
@connection.block
@connection.get_last_result
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index e5ad08b6b0..2cf1015f2c 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -141,14 +141,12 @@ module ActiveRecord
'SQLite'
end
- # Returns true
def supports_ddl_transactions?
true
end
- # Returns true if SQLite version is '3.6.8' or greater, false otherwise.
def supports_savepoints?
- sqlite_version >= '3.6.8'
+ true
end
# Returns true, since this connection adapter supports prepared statement
@@ -162,7 +160,6 @@ module ActiveRecord
true
end
- # Returns true.
def supports_primary_key? #:nodoc:
true
end
@@ -171,7 +168,6 @@ module ActiveRecord
true
end
- # Returns true
def supports_add_column?
true
end
@@ -193,11 +189,6 @@ module ActiveRecord
@statements.clear
end
- # Returns true
- def supports_count_distinct? #:nodoc:
- true
- end
-
def supports_index_sort_order?
true
end
@@ -218,7 +209,6 @@ module ActiveRecord
@connection.encoding.to_s
end
- # Returns true.
def supports_explain?
true
end
@@ -291,8 +281,11 @@ module ActiveRecord
end
def exec_query(sql, name = nil, binds = [])
- log(sql, name, binds) do
+ type_casted_binds = binds.map { |col, val|
+ [col, type_cast(val, col)]
+ }
+ log(sql, name, type_casted_binds) do
# Don't cache statements if they are not prepared
if without_prepared_statement?(binds)
stmt = @connection.prepare(sql)
@@ -307,9 +300,7 @@ module ActiveRecord
stmt = cache[:stmt]
cols = cache[:cols] ||= stmt.columns
stmt.reset!
- stmt.bind_params binds.map { |col, val|
- type_cast(val, col)
- }
+ stmt.bind_params type_casted_binds.map { |_, val| val }
end
ActiveRecord::Result.new(cols, stmt.to_a)
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index d93c26c130..96b5686ae0 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -109,7 +109,7 @@ module ActiveRecord
elsif abstract_class?
"#{super}(abstract)"
elsif !connected?
- "#{super}(no database connection)"
+ "#{super} (call '#{super}.connection' to establish a connection)"
elsif table_exists?
attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
"#{super}(#{attr_list})"
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 927fbab8f0..654ef21b07 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -23,6 +23,8 @@ module ActiveRecord
def render_bind(column, value)
if column
if column.binary?
+ # This specifically deals with the PG adapter that casts bytea columns into a Hash.
+ value = value[:value] if value.is_a?(Hash)
value = "<#{value.bytesize} bytes of binary data>"
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 5224a6b67c..d010f23517 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -32,7 +32,11 @@ module ActiveRecord
class PendingMigrationError < ActiveRecordError#:nodoc:
def initialize
- super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
+ if defined?(Rails)
+ super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
+ else
+ super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.")
+ end
end
end
diff --git a/activerecord/lib/active_record/no_touching.rb b/activerecord/lib/active_record/no_touching.rb
new file mode 100644
index 0000000000..dbf4564ae5
--- /dev/null
+++ b/activerecord/lib/active_record/no_touching.rb
@@ -0,0 +1,52 @@
+module ActiveRecord
+ # = Active Record No Touching
+ module NoTouching
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Lets you selectively disable calls to `touch` for the
+ # duration of a block.
+ #
+ # ==== Examples
+ # ActiveRecord::Base.no_touching do
+ # Project.first.touch # does nothing
+ # Message.first.touch # does nothing
+ # end
+ #
+ # Project.no_touching do
+ # Project.first.touch # does nothing
+ # Message.first.touch # works, but does not touch the associated project
+ # end
+ #
+ def no_touching(&block)
+ NoTouching.apply_to(self, &block)
+ end
+ end
+
+ class << self
+ def apply_to(klass) #:nodoc:
+ klasses.push(klass)
+ yield
+ ensure
+ klasses.pop
+ end
+
+ def applied_to?(klass) #:nodoc:
+ klasses.any? { |k| k >= klass }
+ end
+
+ private
+ def klasses
+ Thread.current[:no_touching_classes] ||= []
+ end
+ end
+
+ def no_touching?
+ NoTouching.applied_to?(self.class)
+ end
+
+ def touch(*)
+ super unless no_touching?
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 301bc503c7..186a737561 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -15,9 +15,10 @@ module ActiveRecord
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
#
- # NOTE - If you are using special PostgreSQL columns like +hstore+ or +json+ there is no need for
- # the serialization provieded by +store+. You can simply use +store_accessor+ instead to generate
- # the accessor methods.
+ # NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
+ # the serialization provided by +store+. Simply use +store_accessor+ instead to generate
+ # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
+ # using a symbol.
#
# Examples:
#