diff options
-rw-r--r-- | lib/arel/algebra/predicates.rb | 23 | ||||
-rw-r--r-- | lib/arel/engines/memory/predicates.rb | 53 | ||||
-rw-r--r-- | lib/arel/engines/sql/predicates.rb | 6 | ||||
-rw-r--r-- | spec/relations/join_spec.rb | 6 | ||||
-rw-r--r-- | spec/relations/relation_spec.rb | 2 | ||||
-rw-r--r-- | spec/shared/relation_spec.rb | 37 |
6 files changed, 106 insertions, 21 deletions
diff --git a/lib/arel/algebra/predicates.rb b/lib/arel/algebra/predicates.rb index 43606139b0..be80f2a987 100644 --- a/lib/arel/algebra/predicates.rb +++ b/lib/arel/algebra/predicates.rb @@ -14,19 +14,28 @@ module Arel end end - class Grouped < Predicate - attributes :operator, :operand1, :operands2 + class Polyadic < Predicate + attributes :operator, :operand1, :additional_operands - def initialize(operator, operand1, *operands2) + def initialize(operator, operand1, *additional_operands) @operator = operator @operand1 = operand1 - @operands2 = operands2.uniq + @additional_operands = additional_operands.uniq end def ==(other) self.class === other and + @operator == operator and @operand1 == other.operand1 and - same_elements?(@operands2, other.operands2) + same_elements?(@additional_operands, other.additional_operands) + end + + def bind(relation) + self.class.new( + operator, + operand1.find_correlate_in(relation), + *additional_operands.map {|o| o.find_correlate_in(relation)} + ) end private @@ -43,6 +52,10 @@ module Arel class Unary < Predicate attributes :operand deriving :initialize, :== + + def bind(relation) + self.class.new(operand.find_correlate_in(relation)) + end end class Binary < Predicate diff --git a/lib/arel/engines/memory/predicates.rb b/lib/arel/engines/memory/predicates.rb index d0963e2f74..c0ee862626 100644 --- a/lib/arel/engines/memory/predicates.rb +++ b/lib/arel/engines/memory/predicates.rb @@ -5,6 +5,49 @@ module Arel operand1.eval(row).send(operator, operand2.eval(row)) end end + + class Unary < Predicate + def eval(row) + operand.eval(row).send(operator) + end + end + + class Not < Unary + def operator; '!' end + end + + class CompoundPredicate < Binary + def eval(row) + eval "operand1.eval(row) #{operator} operand2.eval(row)" + end + end + + class Or < CompoundPredicate + def operator; :or end + end + + class And < CompoundPredicate + def operator; :and end + end + + class GroupedPredicate < Polyadic + def eval(row) + group = additional_operands.inject([]) do |results, operand| + results << operator.new(operand1, operand) + end + group.send(compounder) do |operation| + operation.eval(row) + end + end + end + + class Any < GroupedPredicate + def compounder; :any? end + end + + class All < GroupedPredicate + def compounder; :all? end + end class Equality < Binary def operator; :== end @@ -37,16 +80,20 @@ module Arel end class NotMatch < Binary - def operator; :!~ end + def eval(row) + operand1.eval(row) !~ operand2.eval(row) + end end class In < Binary - def operator; :include? end + def eval(row) + operand2.eval(row).include?(operand1.eval(row)) + end end class NotIn < Binary def eval(row) - !(operand1.eval(row).include?(operand2.eval(row))) + !(operand2.eval(row).include?(operand1.eval(row))) end end end diff --git a/lib/arel/engines/sql/predicates.rb b/lib/arel/engines/sql/predicates.rb index 29bc74c605..b459168620 100644 --- a/lib/arel/engines/sql/predicates.rb +++ b/lib/arel/engines/sql/predicates.rb @@ -30,11 +30,11 @@ module Arel def predicate_sql; "AND" end end - class GroupedPredicate < Grouped + class GroupedPredicate < Polyadic def to_sql(formatter = nil) "(" + - operands2.inject([]) { |predicates, operand| - predicates << operator.new(operand1, operand).to_sql + additional_operands.inject([]) { |predicates, operand| + predicates << operator.new(operand1, operand).to_sql(formatter) }.join(" #{predicate_sql} ") + ")" end diff --git a/spec/relations/join_spec.rb b/spec/relations/join_spec.rb index 47e468a9f9..3894d175e8 100644 --- a/spec/relations/join_spec.rb +++ b/spec/relations/join_spec.rb @@ -13,6 +13,7 @@ describe "Arel" do r.attribute :id, Arel::Attributes::Integer r.attribute :owner_id, Arel::Attributes::Integer + r.attribute :name, Arel::Attributes::String r.attribute :age, Arel::Attributes::Integer end end @@ -28,9 +29,10 @@ describe "Arel" do 8.times do |i| thing_id = owner_id * 8 + i age = 2 * thing_id + name = "Name#{thing_id}" - @thing.insert([thing_id, owner_id, age]) - @expected << Arel::Row.new(@relation, [thing_id, owner_id, age, owner_id]) + @thing.insert([thing_id, owner_id, name, age]) + @expected << Arel::Row.new(@relation, [thing_id, owner_id, name, age, owner_id]) end end end diff --git a/spec/relations/relation_spec.rb b/spec/relations/relation_spec.rb index 808ddf1444..0381f8759d 100644 --- a/spec/relations/relation_spec.rb +++ b/spec/relations/relation_spec.rb @@ -14,7 +14,7 @@ describe "Arel" do describe "Relation" do before :all do - @expected = (1..20).map { |i| @relation.insert([i, nil, 2 * i]) } + @expected = (1..20).map { |i| @relation.insert([i, "Name#{i}", 2 * i]) } end it_should_behave_like 'A Relation' diff --git a/spec/shared/relation_spec.rb b/spec/shared/relation_spec.rb index 1407dddb2a..dabbde2dd5 100644 --- a/spec/shared/relation_spec.rb +++ b/spec/shared/relation_spec.rb @@ -35,7 +35,7 @@ share_examples_for 'A Relation' do @relation.where(@relation[:age].eq(@pivot[@relation[:age]])).should have_rows(expected) end - it "finds rows with a not predicate" do + it "finds rows with a noteq predicate" do expected = @expected.select { |r| r[@relation[:age]] != @pivot[@relation[:age]] } @relation.where(@relation[:age].noteq(@pivot[@relation[:age]])).should have_rows(expected) end @@ -60,17 +60,40 @@ share_examples_for 'A Relation' do @relation.where(@relation[:age].gteq(@pivot[@relation[:age]])).should have_rows(expected) end - it "finds rows with a matches predicate" + it "finds rows with a matches predicate" do + expected = @expected.select { |r| r[@relation[:name]] =~ /#{@pivot[@relation[:name]]}/ } + @relation.where(@relation[:name].matches(/#{@pivot[@relation[:name]]}/)).should have_rows(expected) + end - it "finds rows with a not matches predicate" + it "finds rows with a not matches predicate" do + expected = @expected.select { |r| r[@relation[:name]] !~ /#{@pivot[@relation[:name]]}/ } + @relation.where(@relation[:name].notmatches(/#{@pivot[@relation[:name]]}/)).should have_rows(expected) + end it "finds rows with an in predicate" do - pending - set = @expected[1..(@expected.length/2+1)] - @relation.all(:id.in => set.map { |r| r.id }).should have_resources(set) + expected = @expected.select {|r| r[@relation[:age]] >=3 && r[@relation[:age]] <= 20} + @relation.where(@relation[:age].in(3..20)).should have_rows(expected) end - it "finds rows with a not in predicate" + it "finds rows with a not in predicate" do + expected = @expected.select {|r| !(r[@relation[:age]] >=3 && r[@relation[:age]] <= 20)} + @relation.where(@relation[:age].notin(3..20)).should have_rows(expected) + end + + it "finds rows with a not predicate" do + expected = @expected.select {|r| !(r[@relation[:age]] >= 3 && r[@relation[:age]] <= 20)} + @relation.where(@relation[:age].in(3..20).not).should have_rows(expected) + end + + it "finds rows with a grouped predicate of class Any" do + expected = @expected.select {|r| [2,4,8,16].include?(r[@relation[:age]])} + @relation.where(@relation[:age].in_any([2,4], [8, 16])).should have_rows(expected) + end + + it "finds rows with a grouped predicate of class All" do + expected = @expected.select {|r| r[@relation[:name]] =~ /Name/ && r[@relation[:name]] =~ /1/} + @relation.where(@relation[:name].matches_all(/Name/, /1/)).should have_rows(expected) + end end describe "#order" do |