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.rb39
-rw-r--r--activerecord/lib/active_record/associations/association.rb8
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb90
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb39
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb11
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb6
-rw-r--r--activerecord/lib/active_record/model.rb2
-rw-r--r--activerecord/lib/active_record/querying.rb6
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
18 files changed, 63 insertions, 172 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a90c5a2a9a..6124fe315f 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1104,15 +1104,6 @@ module ActiveRecord
# a +belongs_to+, and the records which get deleted are the join records, rather than
# the associated records.
#
- # [:finder_sql]
- # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
- # associations that depend on multiple tables. May be supplied as a string or a proc where interpolation is
- # required. Note: When this option is used, +find_in_collection+
- # is _not_ added.
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
# [:extend]
# Specify a named module for extending the proxy. See "Association extensions".
# [:include]
@@ -1184,15 +1175,7 @@ module ActiveRecord
# has_many :tags, :as => :taggable
# has_many :reports, :readonly => true
# has_many :subscribers, :through => :subscriptions, :source => :user
- # has_many :subscribers, :class_name => "Person", :finder_sql => Proc.new {
- # %Q{
- # SELECT DISTINCT *
- # FROM people p, post_subscriptions ps
- # WHERE ps.post_id = #{id} AND ps.person_id = p.id
- # ORDER BY p.first_name
- # }
- # }
- def has_many(name, scope = {}, options = nil, &extension)
+ def has_many(name, scope = nil, options = {}, &extension)
Builder::HasMany.build(self, name, scope, options, &extension)
end
@@ -1308,7 +1291,7 @@ module ActiveRecord
# has_one :boss, :readonly => :true
# has_one :club, :through => :membership
# has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
- def has_one(name, scope = {}, options = nil)
+ def has_one(name, scope = nil, options = {})
Builder::HasOne.build(self, name, scope, options)
end
@@ -1431,7 +1414,7 @@ module ActiveRecord
# belongs_to :post, :counter_cache => true
# belongs_to :company, :touch => true
# belongs_to :company, :touch => :employees_last_updated_at
- def belongs_to(name, scope = {}, options = nil)
+ def belongs_to(name, scope = nil, options = {})
Builder::BelongsTo.build(self, name, scope, options)
end
@@ -1555,18 +1538,6 @@ module ActiveRecord
# such as <tt>last_name, first_name DESC</tt>
# [:uniq]
# If true, duplicate associated objects will be ignored by accessors and query methods.
- # [:finder_sql]
- # Overwrite the default generated SQL statement used to fetch the association with a manual statement
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
- # [:delete_sql]
- # Overwrite the default generated SQL statement used to remove links between the associated
- # classes with a manual statement.
- # [:insert_sql]
- # Overwrite the default generated SQL statement used to add links between the associated classes
- # with a manual statement.
# [:extend]
# Anonymous module for extending the proxy, see "Association extensions".
# [:include]
@@ -1603,9 +1574,7 @@ module ActiveRecord
# has_and_belongs_to_many :nations, :class_name => "Country"
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
# has_and_belongs_to_many :categories, :readonly => true
- # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
- # proc { |record| "DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}" }
- def has_and_belongs_to_many(name, scope = {}, options = nil, &extension)
+ def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension)
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 4b989793da..7db6d658f6 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -140,14 +140,6 @@ module ActiveRecord
reset
end
- def interpolate(sql, record = nil)
- if sql.respond_to?(:to_proc)
- owner.send(:instance_exec, record, &sql)
- else
- sql
- end
- end
-
# We can't dump @reflection since it contains the scope proc
def marshal_dump
reflection = @reflection
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 1303822868..cb97490ef1 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -5,7 +5,7 @@ module ActiveRecord
attr_reader :association, :alias_tracker
- delegate :klass, :owner, :reflection, :interpolate, :to => :association
+ delegate :klass, :owner, :reflection, :to => :association
delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection
def initialize(association)
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 4a78a9c076..f45ab1aff4 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -16,12 +16,12 @@ module ActiveRecord::Associations::Builder
@model = model
@name = name
- if options
- @scope = scope
- @options = options
- else
+ if scope.is_a?(Hash)
@scope = nil
@options = scope
+ else
+ @scope = scope
+ @options = options
end
if @scope && @scope.arity == 0
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index af81af4ad2..b28d6a746c 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -3,7 +3,7 @@ module ActiveRecord::Associations::Builder
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
def valid_options
- super + [:table_name, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove]
+ super + [:table_name, :before_add, :after_add, :before_remove, :after_remove]
end
attr_reader :block_extension, :extension_module
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 7f79ef25f2..a30e2dab26 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
end
def valid_options
- super + [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
+ super + [:join_table, :association_foreign_key]
end
def build
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 55628c81bb..30522e3a5d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -44,7 +44,7 @@ module ActiveRecord
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
def ids_reader
- if loaded? || options[:finder_sql]
+ if loaded?
load_target.map do |record|
record.send(reflection.association_primary_key)
end
@@ -79,11 +79,7 @@ module ActiveRecord
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
else
- if options[:finder_sql]
- find_by_scan(*args)
- else
- scoped.find(*args)
- end
+ scoped.find(*args)
end
end
@@ -170,35 +166,26 @@ module ActiveRecord
end
end
- # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
- # association, it will be used for the query. Otherwise, construct options and pass them with
+ # Count all records using SQL. Construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
- if options[:counter_sql] || options[:finder_sql]
- unless count_options.blank?
- raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
- end
-
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- if association_scope.uniq_value
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
- column_name ||= reflection.klass.primary_key
- count_options[:distinct] = true
- end
+ if association_scope.uniq_value
+ # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
+ column_name ||= reflection.klass.primary_key
+ count_options[:distinct] = true
+ end
- value = scoped.count(column_name, count_options)
+ value = scoped.count(column_name, count_options)
- limit = options[:limit]
- offset = options[:offset]
+ limit = options[:limit]
+ offset = options[:offset]
- if limit || offset
- [ [value - offset.to_i, 0].max, limit.to_i ].min
- else
- value
- end
+ if limit || offset
+ [ [value - offset.to_i, 0].max, limit.to_i ].min
+ else
+ value
end
end
@@ -298,9 +285,9 @@ module ActiveRecord
end
end
- def uniq(collection = load_target)
+ def uniq
seen = {}
- collection.find_all do |record|
+ load_target.find_all do |record|
seen[record.id] = true unless seen.key?(record.id)
end
end
@@ -323,7 +310,6 @@ module ActiveRecord
if record.new_record?
include_in_memory?(record)
else
- load_target if options[:finder_sql]
loaded? ? target.include?(record) : scoped.exists?(record)
end
else
@@ -358,32 +344,8 @@ module ActiveRecord
private
- def custom_counter_sql
- if options[:counter_sql]
- interpolate(options[:counter_sql])
- else
- # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
- interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
- count_with = $2.to_s
- count_with = '*' if count_with.blank? || count_with =~ /,/
- "SELECT #{$1}COUNT(#{count_with}) FROM"
- end
- end
- end
-
- def custom_finder_sql
- interpolate(options[:finder_sql])
- end
-
def find_target
- records =
- if options[:finder_sql]
- reflection.klass.find_by_sql(custom_finder_sql)
- else
- scoped.all
- end
-
- records = options[:uniq] ? uniq(records) : records
+ records = scoped.to_a
records.each { |record| set_inverse_instance(record) }
records
end
@@ -522,7 +484,6 @@ module ActiveRecord
# Otherwise, go to the database only if none of the following are true:
# * target already loaded
# * owner is new record
- # * custom :finder_sql exists
# * target contains new or changed record(s)
# * the first arg is an integer (which indicates the number of records to be returned)
def fetch_first_or_last_using_find?(args)
@@ -531,7 +492,6 @@ module ActiveRecord
else
!(loaded? ||
owner.new_record? ||
- options[:finder_sql] ||
target.any? { |record| record.new_record? || record.changed? } ||
args.first.kind_of?(Integer))
end
@@ -548,20 +508,6 @@ module ActiveRecord
end
end
- # If using a custom finder_sql, #find scans the entire collection.
- def find_by_scan(*args)
- expects_array = args.first.kind_of?(Array)
- ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
-
- if ids.size == 1
- id = ids.first
- record = load_target.detect { |r| id == r.id }
- expects_array ? [ record ] : record
- else
- load_target.select { |r| ids.include?(r.id) }
- end
- end
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
def first_or_last(type, *args)
args.shift if args.first.is_a?(Hash) && args.first.empty?
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 93618721bb..e5b40f3911 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -18,16 +18,12 @@ module ActiveRecord
end
end
- if options[:insert_sql]
- owner.connection.insert(interpolate(options[:insert_sql], record))
- else
- stmt = join_table.compile_insert(
- join_table[reflection.foreign_key] => owner.id,
- join_table[reflection.association_foreign_key] => record.id
- )
+ stmt = join_table.compile_insert(
+ join_table[reflection.foreign_key] => owner.id,
+ join_table[reflection.association_foreign_key] => record.id
+ )
- owner.connection.insert stmt
- end
+ owner.connection.insert stmt
record
end
@@ -39,22 +35,17 @@ module ActiveRecord
end
def delete_records(records, method)
- if sql = options[:delete_sql]
- records = load_target if records == :all
- records.each { |record| owner.connection.delete(interpolate(sql, record)) }
- else
- relation = join_table
- condition = relation[reflection.foreign_key].eq(owner.id)
-
- unless records == :all
- condition = condition.and(
- relation[reflection.association_foreign_key]
- .in(records.map { |x| x.id }.compact)
- )
- end
-
- owner.connection.delete(relation.where(condition).compile_delete)
+ relation = join_table
+ condition = relation[reflection.foreign_key].eq(owner.id)
+
+ unless records == :all
+ condition = condition.and(
+ relation[reflection.association_foreign_key]
+ .in(records.map { |x| x.id }.compact)
+ )
end
+
+ owner.connection.delete(relation.where(condition).compile_delete)
end
def invertible_for?(record)
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 669b7e03c2..41c6ca92cc 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -33,13 +33,7 @@ module ActiveRecord
# If the collection is empty the target is set to an empty array and
# the loaded flag is set to true as well.
def count_records
- count = if has_cached_counter?
- owner.send(:read_attribute, cached_counter_attribute_name)
- elsif options[:counter_sql] || options[:finder_sql]
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- scoped.count
- end
+ count = has_cached_counter? ? owner[cached_counter_attribute_name] : scoped.count
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index e7fd72994f..fa77a8733b 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -93,7 +93,7 @@ module ActiveRecord
end
def reflection_scope
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(&reflection.scope) : klass.unscoped
+ @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
end
def build_scope
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 ef17dfbbc5..f9ae1ed475 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -271,7 +271,7 @@ module ActiveRecord
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
# <tt>:updated_at</tt> to the table.
def timestamps(*args)
- options = { :null => false }.merge(args.extract_options!)
+ options = args.extract_options!
column(:created_at, :datetime, options)
column(:updated_at, :datetime, options)
end
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 3c3f01223c..a80c1cec39 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -204,11 +204,14 @@ module ActiveRecord
join_table_name = find_join_table_name(table_1, table_2, options)
column_options = options.delete(:column_options) || {}
- column_options.reverse_merge!({:null => false})
+ column_options.reverse_merge!(null: false)
- create_table(join_table_name, options.merge!(:id => false)) do |td|
- td.integer :"#{table_1.to_s.singularize}_id", column_options
- td.integer :"#{table_2.to_s.singularize}_id", column_options
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
+
+ create_table(join_table_name, options.merge!(id: false)) do |td|
+ td.integer t1_column, column_options
+ td.integer t2_column, column_options
+ yield td if block_given?
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 90f156456e..803d68187c 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -380,7 +380,7 @@ module ActiveRecord
#
# So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
#
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
def to_ary # :nodoc:
nil
end
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 01a580781b..e880ae97bb 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -4,13 +4,11 @@ module ActiveRecord
private
def find_join_table_name(table_1, table_2, options = {})
- options.delete(:table_name) { join_table_name(table_1, table_2) }
+ options.delete(:table_name) || join_table_name(table_1, table_2)
end
def join_table_name(table_1, table_2)
- tables_names = [table_1, table_2].map(&:to_s).sort
-
- tables_names.join("_").to_sym
+ [table_1, table_2].sort.join("_").to_sym
end
end
end
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 0015e3a567..fb3fb643ff 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -74,9 +74,9 @@ module ActiveRecord
include Inheritance
include Scoping
include Sanitization
- include Integration
include AttributeAssignment
include ActiveModel::Conversion
+ include Integration
include Validations
include CounterCache
include Locking::Optimistic
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 4d8283bcff..609d810654 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -62,8 +62,10 @@ module ActiveRecord
#
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
+ logging_query_plan do
+ sql = sanitize_conditions(sql)
+ connection.select_value(sql, "#{name} Count").to_i
+ end
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 3e8d9718f5..dede453e38 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -116,10 +116,6 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
- def sanitized_conditions #:nodoc:
- @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
- end
-
private
def derive_class_name
name.to_s.camelize
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 9e4b588ac2..5a24135f8e 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,7 +26,7 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array(options[:scope]).each do |scope_item|
- scope_value = record.send(scope_item)
+ scope_value = record.read_attribute(scope_item)
reflection = record.class.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)