Browse files

new "chain" syntax

implementing Jeremy's idea: rails/rails#5950 (comment)
  • Loading branch information...
1 parent e1688a5 commit 64b173d74c3daf67488375fda94f330abba395a8 @amatsuda committed Jun 21, 2012
Showing with 178 additions and 0 deletions.
  1. +58 −0 lib/everywhere/chain.rb
  2. +120 −0 spec/chain_spec.rb
View
58 lib/everywhere/chain.rb
@@ -0,0 +1,58 @@
+require 'everywhere/util'
+
+module ActiveRecord
+ module ChainRelation
+ module NotBuilder
+ include Everywhere::Util
+
+ def build_where(opts, other = [])
+ super.map {|r| negate r}
+ end
+ end
+
+ module LikeBuilder
+ def build_where(opts, other = [])
+ super.map {|r| Arel::Nodes::Matches.new r.left, r.right}
+ end
+ end
+
+ module NotLikeBuilder
+ def build_where(opts, other = [])
+ super.map {|r| Arel::Nodes::DoesNotMatch.new r.left, r.right}
+ end
+ end
+
+ def not(opts, *rest)
+ extend(NotBuilder).where(opts, *rest).dup
+ end
+
+ def like(opts, *rest)
+ extend(LikeBuilder).where(opts, *rest).dup
+ end
+
+ def not_like(opts, *rest)
+ extend(NotLikeBuilder).where(opts, *rest).dup
+ end
+ end
+
+ module QueryMethods
+ if ActiveRecord::VERSION::STRING > '4'
+ def where_with_not_and_like_and_not_like(opts = nil, *rest)
+ if opts.nil?
+ spawn.extend(ChainRelation)
+ else
+ where_without_not_and_like_and_not_like(opts, *rest)
+ end
+ end
+ else
+ def where_with_not_and_like_and_not_like(opts = nil, *rest)
+ if opts.nil?
+ clone.extend(ChainRelation)
+ else
+ where_without_not_and_like_and_not_like(opts, *rest)
+ end
+ end
+ end
+ alias_method_chain :where, :not_and_like_and_not_like
+ end
+end
View
120 spec/chain_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+require 'everywhere/chain'
+
+describe 'normal query' do
+ before do
+ @where = Post.where(:title => 'hello').where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" = 'hello'] }
+end
+
+describe 'not' do
+ describe 'not eq' do
+ before do
+ @where = Post.where.not(:title => 'hello').where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" != 'hello'] }
+ end
+
+ describe 'not null' do
+ before do
+ @where = Post.where.not(:created_at => nil).where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."created_at" IS NOT NULL] }
+ end
+
+ describe 'not in' do
+ before do
+ @where = Post.where.not(:title => %w[hello goodbye]).where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" NOT IN ('hello', 'goodbye')] }
+ end
+
+ describe 'association' do
+ before do
+ @where = Post.joins(:comments).where.not(:comments => {:body => 'foo'}).where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["comments"."body" != 'foo'] }
+ end
+
+ describe 'with preceding where' do
+ before do
+ @where = Post.where(:title => 'hello').where.not(:title => 'world').where_values
+ end
+ specify { @where.should have(2).items }
+ it 'should work' do
+ @where.last.to_sql.should == %q["posts"."title" != 'world']
+ end
+ describe 'preceding where' do
+ it 'should not be affected' do
+ @where.first.to_sql.should == %q["posts"."title" = 'hello']
+ end
+ end
+ end
+
+ describe 'with succeeding where' do
+ before do
+ @where = Post.where.not(:title => 'hello').where(:title => 'world').where_values
+ end
+ specify { @where.should have(2).items }
+ it 'should work' do
+ @where.first.to_sql.should == %q["posts"."title" != 'hello']
+ end
+ describe 'succeeding where' do
+ it 'should not be affected' do
+ @where.last.to_sql.should == %q["posts"."title" = 'world']
+ end
+ end
+ end
+end
+
+describe 'like' do
+ describe 'like match' do
+ before do
+ @where = Post.where.like(:title => 'he%').where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" LIKE 'he%'] }
+ end
+end
+
+describe 'not like' do
+ describe 'not like match' do
+ before do
+ @where = Post.where.not_like(:title => 'he%').where_values
+ end
+ specify { @where.should have(1).item }
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" NOT LIKE 'he%'] }
+ end
+end
+
+describe 'chaining multiple extended queries' do
+ before do
+ @where = Post.where.like(:title => '%hell%').where.not(:title => 'world').where.not_like(:title => 'heaven and %').where_values
+ end
+ specify { @where.should have(3).items }
+ describe 'not' do
+ subject { @where[1] }
+ its(:to_sql) { should == %q["posts"."title" != 'world'] }
+ end
+ describe 'like' do
+ subject { @where.first }
+ its(:to_sql) { should == %q["posts"."title" LIKE '%hell%'] }
+ end
+ describe 'not like' do
+ subject { @where.last }
+ its(:to_sql) { should == %q["posts"."title" NOT LIKE 'heaven and %'] }
+ end
+end

0 comments on commit 64b173d

Please sign in to comment.