Permalink
Browse files

Allow delayed evaluation to bubble up in condition specifiers

This makes it so you can do:

  DB[:a].where(:b=>Sequel.delay{c})

and have the query use the correct operator depending on the type
of c.  Previously, it would always have used the default =
operator.
  • Loading branch information...
1 parent 9beafa5 commit be8cc9eef955f3176a16cf690c67b851b4ecdc12 @jeremyevans committed Oct 29, 2012
Showing with 45 additions and 25 deletions.
  1. +25 −19 lib/sequel/sql.rb
  2. +20 −6 spec/core/expression_filters_spec.rb
View
@@ -962,27 +962,33 @@ class BooleanExpression < ComplexExpression
# ~from_value_pairs(hash)
# from_value_pairs(hash, :OR, true)
def self.from_value_pairs(pairs, op=:AND, negate=false)
- pairs = pairs.collect do |l,r|
- ce = case r
- when Range
- new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
- when ::Array, ::Sequel::Dataset
- new(:IN, l, r)
- when NegativeBooleanConstant
- new(:"IS NOT", l, r.constant)
- when BooleanConstant
- new(:IS, l, r.constant)
- when NilClass, TrueClass, FalseClass
- new(:IS, l, r)
- when Regexp
- StringExpression.like(l, r)
- else
- new(:'=', l, r)
- end
- negate ? invert(ce) : ce
- end
+ pairs = pairs.map{|l,r| from_value_pair(l, r)}
+ pairs.map!{|ce| invert(ce)} if negate
pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
end
+
+ # Return a BooleanExpression based on the right side of the pair.
+ def self.from_value_pair(l, r)
+ case r
+ when Range
+ new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
+ when ::Array, ::Sequel::Dataset
+ new(:IN, l, r)
+ when NegativeBooleanConstant
+ new(:"IS NOT", l, r.constant)
+ when BooleanConstant
+ new(:IS, l, r.constant)
+ when NilClass, TrueClass, FalseClass
+ new(:IS, l, r)
+ when Regexp
+ StringExpression.like(l, r)
+ when DelayedEvaluation
+ Sequel.delay{from_value_pair(l, r.callable.call)}
+ else
+ new(:'=', l, r)
+ end
+ end
+ private_class_method :from_value_pair
# Invert the expression, if possible. If the expression cannot
# be inverted, raise an error. An inverted expression should match everything that the
@@ -968,22 +968,36 @@ def o.sql_literal(ds) 'foo' end
end
describe "Sequel.delay" do
- specify "should delay calling the block until literalization" do
- o = Class.new do
+ before do
+ @o = Class.new do
def a
@a ||= 0
@a += 1
end
def _a
@a
end
+
+ attr_accessor :b
end.new
- ds = Sequel.mock[:b].where(:a=>Sequel.delay{o.a})
- o._a.should be_nil
+ end
+
+ specify "should delay calling the block until literalization" do
+ ds = Sequel.mock[:b].where(:a=>Sequel.delay{@o.a})
+ @o._a.should be_nil
ds.sql.should == "SELECT * FROM b WHERE (a = 1)"
- o._a.should == 1
+ @o._a.should == 1
ds.sql.should == "SELECT * FROM b WHERE (a = 2)"
- o._a.should == 2
+ @o._a.should == 2
+ end
+
+ specify "should have the condition specifier handling respect delayed evaluations" do
+ ds = Sequel.mock[:b].where(:a=>Sequel.delay{@o.b})
+ ds.sql.should == "SELECT * FROM b WHERE (a IS NULL)"
+ @o.b = 1
+ ds.sql.should == "SELECT * FROM b WHERE (a = 1)"
+ @o.b = [1, 2]
+ ds.sql.should == "SELECT * FROM b WHERE (a IN (1, 2))"
end
specify "should raise if called without a block" do

0 comments on commit be8cc9e

Please sign in to comment.