require File.join(File.dirname(__FILE__), 'spec_helper')

describe 'RecordFilter restrictions' do
  before do
    TestModel.extended_models.each { |model| model.last_find = {} }
  end

  it 'should filter for equality' do
    Post.filter do
      with :permalink, 'blog-post'
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{"posts".permalink = ?}, 'blog-post'] }
  end

  it 'should filter for equality with multiple conditions' do
    Post.filter do
      with :permalink, 'blog-post'
      with :blog_id, 3
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{("posts".permalink = ?) AND ("posts".blog_id = ?)}, 'blog-post', 3] }
  end

  it 'should filter by comparison operators' do
    [[:greater_than, :gt, '>'], [:less_than, :lt, '<'], 
     [:less_than_or_equal_to, :lte, '<='], [:greater_than_or_equal_to, :gte, '>=']].each do |set|
       Post.filter do
         with(:created_at).send(set[0], Time.parse('2009-01-03 23:02:00'))
       end.inspect
       Post.last_find.should == { :readonly => false, :conditions => ["\"posts\".created_at #{set[2]} ?", Time.parse('2009-01-03 23:02:00')] }

       Post.filter do
         with(:id).send(set[0], nil)
       end.inspect
       Post.last_find.should == { :readonly => false, :conditions => ["\"posts\".id #{set[2]} NULL"] }

       Post.filter do
         with(:created_at).send(set[1], Time.parse('2009-01-03 23:02:00'))
       end.inspect
       Post.last_find.should == { :readonly => false, :conditions => ["\"posts\".created_at #{set[2]} ?", Time.parse('2009-01-03 23:02:00')] }

       Post.filter do
         with(:created_at).not.send(set[0], Time.parse('2009-02-02 02:22:22'))
       end.inspect
       Post.last_find.should == { :readonly => false, :conditions => ["NOT (\"posts\".created_at #{set[2]} ?)", Time.parse('2009-02-02 02:22:22')] }
     end
  end

  it 'should create an IS NULL restriction when passing nil as the value to equal_to' do
    Post.filter do
      with(:blog_id).equal_to(nil)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IS NULL)] }
  end

  it 'should filter for in' do
    Post.filter do
      with(:blog_id).in [1, 3, 5]
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{"posts".blog_id IN (?)}, [1, 3, 5]] }
  end

  it 'should negate IN filters correctly' do
    Post.filter do
      with(:blog_id).not.in [1, 3, 5]
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{"posts".blog_id NOT IN (?)}, [1, 3, 5]] }
  end

  it 'should work correctly for NOT IN' do
    Post.filter do
      with(:blog_id).not_in [1, 3, 5]
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{"posts".blog_id NOT IN (?)}, [1, 3, 5]] }
  end

  it 'should do the right thing for IN filters with empty arrays' do
    Post.filter do
      with(:blog_id).in([])
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IN (?)), []] }
  end

  it 'should ignore the condition for NOT IN filters with empty arrays' do
    Post.filter do
      with(:blog_id).not_in([])
    end.inspect
    Post.last_find.should == { :conditions => [], :readonly => false }
  end

  it 'should do the right thing for IN filters with single values' do
    Post.filter do
      with(:blog_id).in(1)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IN (?)), 1] }
  end

  it 'should do the right thing for IN filters with nil' do
    Post.filter do
      with(:blog_id).in(nil)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IN (?)), nil] }
  end

  it 'should do the right thing for IN filters with a range' do
    Post.filter do
      with(:blog_id).in(1..5)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IN (?)), 1..5] }
  end

  it 'should negate is_not_null conditions correctly' do
    Post.filter do
      with(:blog_id).is_not_null.not
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id IS NULL)] }
  end 
  
  it 'should double-negate correctly' do
    Post.filter do
      with(:blog_id, 3).not.not
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".blog_id = ?), 3] }
  end

  it 'should filter for between' do
    time1 = Time.parse('2008-01-03 23:23:00')
    time2 = Time.parse('2008-01-03 23:36:00')
    Post.filter do
      with(:created_at).between time1..time2
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{"posts".created_at BETWEEN ? AND ?}, time1, time2] }
  end

  it 'should filter for between with two arguments passed' do
    Post.filter do
      with(:id).between(1, 5)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".id BETWEEN ? AND ?), 1, 5] }
  end

  it 'should filter for between with an array passed' do
    Post.filter do
      with(:id).between([2, 6])
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".id BETWEEN ? AND ?), 2, 6] }
  end

  it 'should filter by none_of' do
    Post.filter do
      none_of do
        with(:blog_id, 1)
        with(:permalink, 'eek')
      end
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{NOT (("posts".blog_id = ?) OR ("posts".permalink = ?))}, 1, 'eek'] }
  end

  it 'should filter by not_all_of' do
    Post.filter do
      not_all_of do
        with(:blog_id, 1)
        with(:permalink, 'eek')
      end
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{NOT (("posts".blog_id = ?) AND ("posts".permalink = ?))}, 1, 'eek'] }
  end

  it 'should filter by disjunction' do
    Post.filter do
      any_of do
        with(:blog_id).equal_to 1
        with(:permalink).equal_to 'my-post'
      end
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q{("posts".blog_id = ?) OR ("posts".permalink = ?)}, 1, 'my-post'] }
  end

  it 'should filter by disjunction composed of conjunction' do
    Post.filter do
      any_of do
        all_of do
          with(:blog_id).equal_to 1
          with(:permalink).equal_to 'my-post'
        end
        with(:permalink).equal_to 'another-post'
      end
    end.inspect

    Post.last_find.should == { :readonly => false, :conditions => [%q{(("posts".blog_id = ?) AND ("posts".permalink = ?)) OR ("posts".permalink = ?)},
                                                                  1, 'my-post', 'another-post'] }
  end

  it 'should filter for nil' do
    [:is_null, :null, :nil].each do |method|
      Post.filter do
        with(:permalink).send(method)
      end.inspect
      Post.last_find.should == { :readonly => false, :conditions => [%q("posts".permalink IS NULL)] }
    end
  end

  it 'should filter for is_not_null' do
    Post.filter do
      with(:permalink).is_not_null
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".permalink IS NOT NULL)] }
  end

  it 'should support like' do
    Post.filter do
      with(:permalink).like('%piglets%')
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".permalink LIKE ?), '%piglets%'] }
  end

  it 'should support NOT LIKE' do
    Post.filter do
      with(:permalink).not.like('%ostriches%')
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".permalink NOT LIKE ?), '%ostriches%'] }
  end

  it 'should provide access to the filter class in the filter' do
    Post.filter do
      with(:permalink).not.equal_to(filter_class.name)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q("posts".permalink <> ?), 'Post'] }
  end

  it 'should work correctly for one-line ORs with is_null' do
    Post.filter do
      with(:permalink, 'abc').or.is_null
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q(("posts".permalink = ?) OR ("posts".permalink IS NULL)), 'abc'] }
  end

  it 'should work correctly for one-line ANDs with is_null' do
    Post.filter do
      with(:id).gt(56).and.lt(200)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q(("posts".id > ?) AND ("posts".id < ?)), 56, 200] }
  end

  it 'should work properly with multiple one-line conjunctions' do
    Post.filter do
      with(:id).gt(56).and.lt(200).or.gt(500).or.lt(15)
    end.inspect
    Post.last_find.should == { :readonly => false, :conditions => [%q(("posts".id > ?) AND (("posts".id < ?) OR (("posts".id > ?) OR ("posts".id < ?)))), 56, 200, 500, 15] } 
  end

  it 'should properly filter when filter is applied to an association' do
    Post.create
    Post.first.comments.filter do
      with(:offensive, true)
    end.inspect
    Comment.last_find.should == { :readonly => false, :conditions => "(\"comments\".offensive = 't') AND (\"comments\".post_id = #{Post.first.id})" }
  end

  it 'should properly filter when applied to an association with order' do
    Blog.create
    Blog.first.ordered_posts.filter do
      with(:permalink, 'dfds')
    end.inspect
    Post.last_find[:order].should == 'created_at DESC'
  end

  it 'should allow a hash as the value for the restriction and use that as the name of a table' do
    Blog.filter do
      having(:comments)
      with(:created_at).gt(:comments => :created_at)
    end.inspect
    Blog.last_find[:conditions].should == [%q(("blogs".created_at > blogs__posts__comments.created_at))]
  end

  it 'should allow a hash as the value for the restriction and use that as the name of a table alias' do
    Blog.filter do
      having(:comments, :alias => 'cmts')
      with(:created_at).gt('cmts' => :created_at)
    end.inspect
    Blog.last_find[:conditions].should == [%q(("blogs".created_at > cmts.created_at))]
  end

  it 'should return the filter class from a filter' do
    filter = Blog.filter { having(:comments) }
    filter.filter_class.should == Blog
  end
end
