Permalink
Browse files

Calculations now use Arel to construct the query.

Implemented other methods in AR::Base with Arel support.
  • Loading branch information...
1 parent 0905396 commit 19d2ff83db5232a816dee201800baf3924705b31 @miloops miloops committed Apr 29, 2009
@@ -510,14 +510,14 @@ def association_instance_set(name, association)
#
# Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
# Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
- #
+ #
# Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
#
# will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
# <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
# In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
# and not just to the association. You must disambiguate column references for this fallback to happen, for example
- # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
+ # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
#
# If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association
# which has conditions defined on it:
@@ -551,10 +551,10 @@ def association_instance_set(name, association)
#
# Address.find(:all, :include => :addressable)
#
- # will execute one query to load the addresses and load the addressables with one query per addressable type.
+ # will execute one query to load the addresses and load the addressables with one query per addressable type.
# For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
# addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
# model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
#
# == Table Aliasing
@@ -869,7 +869,7 @@ def has_many(association_id, options = {}, &extension)
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
# [:through]
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
- # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
+ # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
# <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
# [:source]
# Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
@@ -1123,8 +1123,8 @@ def belongs_to(association_id, options = {})
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
# [:conditions]
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
- # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
- # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
+ # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
# [:order]
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
@@ -1335,12 +1335,12 @@ def add_counter_cache_callbacks(reflection)
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
)
end
-
+
def add_touch_callbacks(reflection, touch_attribute)
method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
-
+
if touch_attribute == true
association.touch unless association.nil?
else
@@ -1552,7 +1552,7 @@ def create_has_and_belongs_to_many_reflection(association_id, options, &extensio
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
-
+
if reflection.association_foreign_key == reflection.primary_key_name
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
end
@@ -1607,6 +1607,14 @@ def add_limited_ids_condition!(sql, options, join_dependency)
end
end
+ def construct_limited_ids_condition(where, options, join_dependency)
+ unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
+ "#{where.blank? ? 'WHERE ' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
+ else
+ throw :invalid_query
+ end
+ end
+
def select_limited_ids_list(options, join_dependency)
pk = columns_hash[primary_key]
@@ -904,9 +904,7 @@ def destroy_all(conditions = nil)
# Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent
# associations or call your <tt>before_*</tt> or +after_destroy+ callbacks, use the +destroy_all+ method instead.
def delete_all(conditions = nil)
- sql = "DELETE FROM #{quoted_table_name} "
- add_conditions!(sql, conditions, scope(:find))
- connection.delete(sql, "#{name} Delete all")
+ Arel(table_name).where(construct_conditions(conditions, scope(:find))).delete
end
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
@@ -1687,19 +1685,27 @@ def default_select(qualified)
end
end
- def construct_finder_sql(options)
+ def arel_table(table)
+ Arel(table)
+ end
+
+ def construct_finder_arel(options)
scope = scope(:find)
# TODO add lock to Arel
- Arel(table_name).
- join(construct_join(options[:joins], scope)).
- where(construct_conditions(options[:conditions], scope)).
+ arel_table(options[:from] || table_name).
+ join(options[:merged_joins] || construct_join(options[:joins], scope)).
+ where(options[:merged_conditions] || construct_conditions(options[:conditions], scope)).
project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))).
group(construct_group(options[:group], options[:having], scope)).
order(construct_order(options[:order], scope)).
take(construct_limit(options, scope)).
skip(construct_offset(options, scope)
- ).to_sql
+ )
+ end
+
+ def construct_finder_sql(options)
+ construct_finder_arel(options).to_sql
end
def construct_join(joins, scope = :auto)
@@ -1715,6 +1721,8 @@ def construct_join(joins, scope = :auto)
end
when String
" #{merged_joins} "
+ else
+ ""
end
end
@@ -2644,11 +2652,8 @@ def delete
# be made (since they can't be persisted).
def destroy
unless new_record?
- connection.delete(
- "DELETE FROM #{self.class.quoted_table_name} " +
- "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}",
- "#{self.class.name} Destroy"
- )
+ table = Arel(self.class.table_name)
+ table.where(table[self.class.primary_key].eq(quoted_id)).delete
end
freeze
Oops, something went wrong.

0 comments on commit 19d2ff8

Please sign in to comment.