Permalink
Browse files

Initial work on 4.0.0 compatibility. Specs pass.

This work will not be backwards compatible with 3.x at the moment.

References #239
  • Loading branch information...
1 parent bbbce19 commit c71db6cbec6884527c2b6205dc25f79e243063bc @ernie ernie committed May 6, 2013
View
@@ -0,0 +1 @@
+squeel
View
@@ -0,0 +1 @@
+ruby-1.9.3-p374
View
@@ -1,13 +1,5 @@
rvm:
- - 1.8.7
- 1.9.3
-# TODO: Re-enable when updates to Rubinius on
-# Travis stop randomly breaking specs that
-# pass on MRI. :(
-# - rbx-18mode
-# - rbx-19mode
env:
- - RAILS=3-2-stable
- - RAILS=3-1-stable AREL=2-2-stable
- - RAILS=3-0-stable AREL=2-0-stable
+ - RAILS=4-0-stable AREL=master
View
@@ -3,8 +3,8 @@ gemspec
gem 'rake'
-rails = ENV['RAILS'] || '3-2-stable'
-arel = ENV['AREL'] || '3-0-stable'
+rails = ENV['RAILS'] || '4-0-stable'
+arel = ENV['AREL'] || 'master'
arel_opts = case arel
when /\// # A path
@@ -1,5 +1,5 @@
case ActiveRecord::VERSION::MAJOR
-when 3
+when 3, 4
ActiveRecord::Relation.send :include, Squeel::Nodes::Aliasing
require 'squeel/adapters/active_record/join_dependency_extensions'
require 'squeel/adapters/active_record/base_extensions'
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module Associations
+ class AssociationScope
+ def eval_scope(klass, scope)
+ if scope.is_a?(Relation)
+ scope
+ else
+ klass.unscoped.instance_exec(owner, &scope).visited
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1 @@
+require 'squeel/adapters/active_record/compat'
@@ -0,0 +1 @@
+require 'squeel/adapters/active_record/context'
@@ -0,0 +1 @@
+require 'squeel/adapters/active_record/preloader_extensions'
@@ -0,0 +1,75 @@
+require 'squeel/adapters/active_record/relation_extensions'
+
+module Squeel
+ module Adapters
+ module ActiveRecord
+ module RelationExtensions
+
+ def where(opts = :chain, *rest)
+ if block_given?
+ super(DSL.eval &Proc.new)
+ else
+ super
+ end
+ end
+
+ def build_arel
+ arel = Arel::SelectManager.new(table.engine, table)
+
+ build_joins(arel, joins_values) unless joins_values.empty?
+
+ collapse_wheres(arel, where_visit((where_values - ['']).uniq))
+
+ arel.having(*having_visit(having_values.uniq.reject{|h| h.blank?})) unless having_values.empty?
+
+ arel.take(connection.sanitize_limit(limit_value)) if limit_value
+ arel.skip(offset_value.to_i) if offset_value
+
+ arel.group(*group_visit(group_values.uniq.reject{|g| g.blank?})) unless group_values.empty?
+
+ build_order(arel)
+
+ build_select(arel, select_visit(select_values.uniq))
+
+ arel.distinct(distinct_value)
+ arel.from(from_visit(from_value)) if from_value
+ arel.lock(lock_value) if lock_value
+
+ arel
+ end
+
+ def build_order(arel)
+ orders = order_visit(order_values)
+ orders = reverse_sql_order(attrs_to_orderings(orders)) if reverse_order_value
+
+ orders = orders.uniq.reject(&:blank?).flat_map do |order|
+ case order
+ when Symbol
+ table[order].asc
+ when Hash
+ order.map { |field, dir| table[field].send(dir) }
+ else
+ order
+ end
+ end
+
+ arel.order(*orders) unless orders.empty?
+ end
+
+ def build_from
+ opts, name = from_value
+ case opts
+ when Relation
+ name ||= 'subquery'
+ opts.arel.as(name.to_s)
+ else
+ opts
+ end
+ end
+
+ end
+ end
+ end
+end
+
+ActiveRecord::Relation.send :include, Squeel::Adapters::ActiveRecord::RelationExtensions
@@ -44,6 +44,16 @@ def traverse(keypath, parent = @base, include_endpoint = false)
parent
end
+ def classify(object)
+ if Class === object
+ object
+ elsif object.respond_to? :base_klass
+ object.base_klass
+ else
+ raise ArgumentError, "#{object} can't be converted to a class"
+ end
+ end
+
private
def get_table(object)
@@ -58,16 +68,6 @@ def get_table(object)
end
end
- def classify(object)
- if Class === object
- object
- elsif object.respond_to? :active_record
- object.active_record
- else
- raise ArgumentError, "#{object} can't be converted to a class"
- end
- end
-
def get_arel_visitor
@engine.connection.visitor
end
@@ -14,7 +14,12 @@ module RelationExtensions
# because the default #reset already does this, despite never setting
# it anywhere that I can find. Serendipity, I say!
def join_dependency
- @join_dependency ||= (build_join_dependency(table.from(table), @joins_values) && @join_dependency)
+ @join_dependency ||= (
+ build_join_dependency(
+ Arel::SelectManager.new(table.engine, table),
+ joins_values
+ ) && @join_dependency
+ )
end
%w(where having group order select from).each do |visitor|
@@ -49,12 +54,12 @@ def visited
end
def visit!
- @where_values = where_visit((@where_values - ['']).uniq)
- @having_values = having_visit(@having_values.uniq.reject{|h| h.blank?})
+ self.where_values = where_visit((where_values - ['']).uniq)
+ self.having_values = having_visit(having_values.uniq.reject{|h| h.blank?})
# FIXME: AR barfs on ARel attributes in group_values. Workaround?
- # @group_values = group_visit(@group_values.uniq.reject{|g| g.blank?})
- @order_values = order_visit(@order_values.uniq.reject{|o| o.blank?})
- @select_values = select_visit(@select_values.uniq)
+ # self.group_values = group_visit(group_values.uniq.reject{|g| g.blank?})
+ self.order_values = order_visit(order_values.uniq.reject{|o| o.blank?})
+ self.select_values = select_visit(select_values.uniq)
self
end
@@ -100,22 +105,22 @@ def build_join_dependency(manager, joins)
buckets = joins.group_by do |join|
case join
when String
- 'string_join'
+ :string_join
when Hash, Symbol, Array, Nodes::Stub, Nodes::Join, Nodes::KeyPath
- 'association_join'
+ :association_join
when JoinAssociation
- 'stashed_join'
+ :stashed_join
when Arel::Nodes::Join
- 'join_node'
+ :join_node
else
raise 'unknown class: %s' % join.class.name
end
end
- association_joins = buckets['association_join'] || []
- stashed_association_joins = buckets['stashed_join'] || []
- join_nodes = (buckets['join_node'] || []).uniq
- string_joins = (buckets['string_join'] || []).map { |x|
+ association_joins = buckets[:association_join] || []
+ stashed_association_joins = buckets[:stashed_join] || []
+ join_nodes = (buckets[:join_node] || []).uniq
+ string_joins = (buckets[:string_join] || []).map { |x|
x.strip
}.uniq
@@ -140,6 +145,8 @@ def build_join_dependency(manager, joins)
manager
end
+ # For 4.x adapters
+ alias :build_joins :build_join_dependency
def includes(*args)
if block_given? && args.empty?
@@ -335,7 +342,7 @@ def resolve_duplicate_squeel_equalities(wheres)
# DB for debug purposes without actually running the query.
def debug_sql
if eager_loading?
- including = (@eager_load_values + @includes_values).uniq
+ including = (eager_load_values + includes_values).uniq
join_dependency = JoinDependency.new(@klass, including, [])
construct_relation_for_association_find(join_dependency).to_sql
else
View
@@ -5,6 +5,8 @@ module Nodes
end
end
+require 'squeel/nodes/node'
+
require 'squeel/nodes/predicate_methods'
require 'squeel/nodes/operators'
require 'squeel/nodes/predicate_operators'
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# A node that represents an operation with two operands.
- class Binary
+ class Binary < Node
include PredicateOperators
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# A node that represents an SQL function call
- class Function
+ class Function < Node
include PredicateMethods
include PredicateOperators
@@ -22,16 +22,16 @@ class Function
alias :<= :lteq
# @return [Symbol] The name of the SQL function to be called
- attr_reader :name
+ attr_reader :function_name
# @return [Array] The arguments to be passed to the SQL function
attr_reader :args
# Create a node representing an SQL Function with the given name and arguments
# @param [Symbol] name The function name
# @param [Array] args The array of arguments to pass to the function.
- def initialize(name, args)
- @name, @args = name, args
+ def initialize(function_name, args)
+ @function_name, @args = function_name, args
end
# expand_hash_conditions_for_aggregates assumes our hash keys can be
@@ -48,7 +48,7 @@ def hash
def eql?(other)
self.class == other.class &&
- self.name.eql?(other.name) &&
+ self.function_name.eql?(other.function_name) &&
self.args.eql?(other.args)
end
@@ -3,7 +3,7 @@
module Squeel
module Nodes
# A node representing a joined association
- class Join
+ class Join < Node
undef_method :id if method_defined?(:id)
attr_reader :_join
@@ -4,7 +4,7 @@ module Squeel
module Nodes
# A node that stores a path of keys (of Symbol, Stub, or Join values) and
# an endpoint. Used similarly to a nested hash.
- class KeyPath
+ class KeyPath < Node
include PredicateOperators
include Operators
@@ -179,12 +179,17 @@ def to_s
# Appends to the KeyPath or delegates to the endpoint, as appropriate
# @return [KeyPath] The updated KeyPath
- def method_missing(method_id, *args)
+ def method_missing(method_id, *args, &block)
super if method_id == :to_ary
- if endpoint.respond_to? method_id
- self.endpoint = endpoint.send(method_id, *args)
- self
+ if endpoint.respond_to?(method_id)
+ if Predicate === endpoint && method_id == :==
+ false
+ else
+ # TODO: We really should not mutate here.
+ self.endpoint = endpoint.send(method_id, *args)
+ self
+ end
elsif Stub === endpoint || Join === endpoint
if args.empty?
@path << Stub.new(method_id)
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# Literal nodes are a container for raw SQL.
- class Literal
+ class Literal < Node
include PredicateMethods
include PredicateOperators
include Operators
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# A node containing multiple children. Just the And node for now.
- class Nary
+ class Nary < Node
include PredicateOperators
# @return [Array] This node's children
@@ -0,0 +1,6 @@
+module Squeel
+ module Nodes
+ class Node
+ end
+ end
+end
@@ -15,7 +15,7 @@ def initialize(left, operator, right)
end
# An operation should probably call its "function" name an "operator", shouldn't it?
- alias :operator :name
+ alias :operator :function_name
# @return The left operand
def left
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# A node that represents SQL orderings, such as "people.id DESC"
- class Order
+ class Order < Node
# @return The expression being ordered on. Might be an attribute, function, or operation
attr_reader :expr
@@ -5,7 +5,7 @@ module Nodes
# operation), the ARel predicate method name, and a value. these are then interpreted
# when visited by the PredicateVisitor to generate a condition against the appropriate
# columns.
- class Predicate
+ class Predicate < Node
include PredicateOperators
include Aliasing
Oops, something went wrong. Retry.

0 comments on commit c71db6c

Please sign in to comment.