Skip to content
This repository has been archived by the owner on Mar 30, 2022. It is now read-only.

Commit

Permalink
Initial work on 4.0.0 compatibility. Specs pass.
Browse files Browse the repository at this point in the history
This work will not be backwards compatible with 3.x at the moment.

References #239
  • Loading branch information
ernie committed May 6, 2013
1 parent bbbce19 commit c71db6c
Show file tree
Hide file tree
Showing 34 changed files with 216 additions and 96 deletions.
1 change: 1 addition & 0 deletions .ruby-gemset
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
squeel
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-1.9.3-p374
10 changes: 1 addition & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/adapters/active_record.rb
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/squeel/adapters/active_record/4.0/compat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'squeel/adapters/active_record/compat'
1 change: 1 addition & 0 deletions lib/squeel/adapters/active_record/4.0/context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'squeel/adapters/active_record/context'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'squeel/adapters/active_record/preloader_extensions'
75 changes: 75 additions & 0 deletions lib/squeel/adapters/active_record/4.0/relation_extensions.rb
Original file line number Diff line number Diff line change
@@ -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
20 changes: 10 additions & 10 deletions lib/squeel/adapters/active_record/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
37 changes: 22 additions & 15 deletions lib/squeel/adapters/active_record/relation_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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?
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib/squeel/nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/binary.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Squeel
module Nodes
# A node that represents an operation with two operands.
class Binary
class Binary < Node

include PredicateOperators

Expand Down
10 changes: 5 additions & 5 deletions lib/squeel/nodes/function.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/join.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 10 additions & 5 deletions lib/squeel/nodes/key_path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/literal.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/nary.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 6 additions & 0 deletions lib/squeel/nodes/node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Squeel
module Nodes
class Node
end
end
end
2 changes: 1 addition & 1 deletion lib/squeel/nodes/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/order.rb
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/squeel/nodes/predicate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit c71db6c

Please sign in to comment.