Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Please add an entry to the "Unreleased changes" section in your pull requests.

=== Unreleased changes

=== Version 4.3.1
- Autocomplete now offers only relevant options available through scopes set on associations (#233)

=== Version 4.3.0

- Prevent scoped_search from modifying an empty string on newer rubies (#229)
Expand Down
5 changes: 4 additions & 1 deletion lib/scoped_search/auto_complete_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def complete_value

def complete_value_from_db(field, special_values, val)
count = 20 - special_values.count

completer_scope(field)
.where(@options[:value_filter])
.where(value_conditions(field.quoted_field, val))
Expand All @@ -224,7 +225,9 @@ def completer_scope(field)

if field.klass != field.definition.klass
reflection = field.definition.reflection_by_name(field.definition.klass, field.relation)
scope = scope.instance_exec(&reflection.scope) if reflection.try(:has_scope?)
sub_scope = reflection.active_record.joins(reflection.name)
sub_scope = sub_scope.select("#{field.klass.table_name}.#{field.klass.primary_key}")
scope = scope.where(field.klass.primary_key => sub_scope)
end

scope.respond_to?(:reorder) ? scope.reorder(Arel.sql(field.quoted_field)) : scope.scoped(:order => field.quoted_field)
Expand Down
47 changes: 44 additions & 3 deletions spec/integration/auto_complete_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
t.integer :foo_id
end

ActiveRecord::Migration.create_table(:asds, :force => true) do |t|
t.integer :baz_id
t.string :string
end

ActiveRecord::Migration.create_table(:qwes, :force => true) do |t|
t.integer :asd_id
t.string :string
end

class ::Bar < ActiveRecord::Base
belongs_to :foo
end
Expand All @@ -71,34 +81,61 @@ class ::Foo < ActiveRecord::Base

class ::Baz < ActiveRecord::Base
belongs_to :foo, -> { where(string: 'foo') }
has_one :asd, -> { where(string: 'foo') }
has_one :qwe, through: :asd

scoped_search :on => :string, :relation => :foo, :complete_value => true
scoped_search :on => :string, :relation => :foo, :complete_value => true, :rename => 'foos.string'.to_sym
scoped_search :on => :string, :relation => :asd, :complete_value => true, :rename => 'asds.string'.to_sym
scoped_search :on => :string, :relation => :qwe, :complete_value => true, :rename => 'qwes.string'.to_sym
end

class ::Infoo < ::Foo
end

class ::Asd < ActiveRecord::Base
belongs_to :baz
has_one :qwe
end

class ::Qwe < ActiveRecord::Base
belongs_to :asd
end

@qux_1 = Qux.create!()
@qux_2 = Qux.create!()
@foo_1 = Foo.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz', :int => 9 , :date => 'February 8, 2011' , :unindexed => 10, :qux => @qux_1)
@foo_2 = Foo.create!(:string => 'foo', :another => 'temp 2', :explicit => 'baz', :int => 10 , :date => 'February 8, 2011' , :unindexed => 10, :qux => @qux_2)
Foo.create!(:string => 'bar', :another => 'temp "2"', :explicit => 'baz', :int => 22 , :date => 'February 10, 2011', :unindexed => 10)
@foo_3 = Foo.create!(:string => 'bar', :another => 'temp "2"', :explicit => 'baz', :int => 22 , :date => 'February 10, 2011', :unindexed => 10)
Foo.create!(:string => 'baz', :another => nil, :explicit => nil , :int => nil, :date => nil , :unindexed => nil)
20.times { Foo.create!(:explicit => "aaa") }

@baz_1 = Baz.create!(:foo => @foo_1)
@baz_2 = Baz.create!(:foo => @foo_2)
Baz.create!(:foo => @foo_3)

Bar.create!(:related => 'lala', :foo => @foo_1)
Bar.create!(:related => 'another lala', :foo => @foo_1)

@asd_1 = Asd.create!(:string => 'foo', :baz => @baz_1)
@asd_2 = Asd.create!(:string => 'bar', :baz => @baz_2)
Qwe.create!(:asd => @asd_1, :string => 'qwe1')
Qwe.create!(:asd => @asd_2, :string => 'qwe2')
end

after(:all) do
ActiveRecord::Migration.drop_table(:foos)
ActiveRecord::Migration.drop_table(:bars)
ActiveRecord::Migration.drop_table(:bazs)
ActiveRecord::Migration.drop_table(:asds)
ActiveRecord::Migration.drop_table(:qwes)

Object.send :remove_const, :Foo
Object.send :remove_const, :Bar
Object.send :remove_const, :Baz
Object.send :remove_const, :Qux
Object.send :remove_const, :Infoo
Object.send :remove_const, :Asd
Object.send :remove_const, :Qwe

ScopedSearch::RSpec::Database.close_connection
end
Expand Down Expand Up @@ -282,7 +319,11 @@ class ::Infoo < ::Foo

context 'autocompleting with scopes' do
it 'should honor the scope' do
::Baz.complete_for('string =').should == ['string = foo']
Baz.complete_for('foos.string =').should == ['foos.string = "foo"']
end

it 'should honor scope on the through relation' do
Baz.complete_for('qwes.string =').should == ['qwes.string = "qwe1"']
end
end
end
Expand Down
106 changes: 53 additions & 53 deletions spec/integration/relation_querying_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,52 +249,52 @@ class Joo < ActiveRecord::Base
before do

# Create some tables
ActiveRecord::Migration.create_table(:mars) { |t| t.integer :koo_id; t.integer :baz_id }
ActiveRecord::Migration.create_table(:bazs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:mars) { |t| t.integer :koo_id; t.integer :zab_id }
ActiveRecord::Migration.create_table(:zabs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:koos) { |t| t.string :foo }

# The related classes
class Mar < ActiveRecord::Base; belongs_to :baz; belongs_to :koo; end
class Baz < ActiveRecord::Base; has_many :mars; end
class Mar < ActiveRecord::Base; belongs_to :zab; belongs_to :koo; end
class Zab < ActiveRecord::Base; has_many :mars; end

# The class on which to call search_for
class Koo < ActiveRecord::Base
has_many :mars
# having the source option here is not needed for the statement correctness.
# It is here to fail the code introduced in 2.6.2 that wrongly detected source instead of source_type
# as an indication for a polymorphic relation.
has_many :bazs, :through => :mars, :source => :baz
has_many :zabs, :through => :mars, :source => :zab

scoped_search :relation => :bazs, :on => :related
scoped_search :relation => :zabs, :on => :related
end

@koo_1 = Koo.create!(:foo => 'foo')
@koo_2 = Koo.create!(:foo => 'foo too')
@koo_3 = Koo.create!(:foo => 'foo three')

@baz_1 = Baz.create(:related => 'baz')
@baz_2 = Baz.create(:related => 'baz too!')
@zab_1 = Zab.create(:related => 'zab')
@zab_2 = Zab.create(:related => 'zab too!')

@bar_1 = Mar.create!(:koo => @koo_1, :baz => @baz_1)
@bar_1 = Mar.create!(:koo => @koo_1, :zab => @zab_1)
@bar_2 = Mar.create!(:koo => @koo_1)
@bar_3 = Mar.create!(:koo => @koo_2, :baz => @baz_1)
@bar_3 = Mar.create!(:koo => @koo_2, :baz => @baz_2)
@bar_3 = Mar.create!(:koo => @koo_2, :baz => @baz_2)
@bar_3 = Mar.create!(:koo => @koo_2, :zab => @zab_1)
@bar_3 = Mar.create!(:koo => @koo_2, :zab => @zab_2)
@bar_3 = Mar.create!(:koo => @koo_2, :zab => @zab_2)
@bar_4 = Mar.create!(:koo => @koo_3)
end

after do
ActiveRecord::Migration.drop_table(:bazs)
ActiveRecord::Migration.drop_table(:zabs)
ActiveRecord::Migration.drop_table(:mars)
ActiveRecord::Migration.drop_table(:koos)
end

it "should find the two records that are related to a baz record" do
Koo.search_for('baz').length.should == 2
it "should find the two records that are related to a zab record" do
Koo.search_for('zab').length.should == 2
end

it "should find the two records that are related to a baz record" do
Koo.search_for('related=baz AND related="baz too!"').length.should == 1
it "should find the two records that are related to a zab record" do
Koo.search_for('related=zab AND related="zab too!"').length.should == 1
end
end

Expand All @@ -303,45 +303,45 @@ class Koo < ActiveRecord::Base
before do

# Create some tables
ActiveRecord::Migration.create_table(:zars) { |t| t.integer :baz_id }
ActiveRecord::Migration.create_table(:bazs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:zars) { |t| t.integer :zab_id }
ActiveRecord::Migration.create_table(:zabs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:zoos) { |t| t.integer :zar_id; t.string :foo }

# The related classes
class Zar < ActiveRecord::Base; belongs_to :baz; has_many :zoos; end
class Baz < ActiveRecord::Base; has_many :zars; end
class Zar < ActiveRecord::Base; belongs_to :zab; has_many :zoos; end
class Zab < ActiveRecord::Base; has_many :zars; end

# The class on which to call search_for
class Zoo < ActiveRecord::Base
belongs_to :zar
has_many :bazs, :through => :zar
has_many :zabs, :through => :zar

scoped_search :relation => :bazs, :on => :related
scoped_search :relation => :zabs, :on => :related
end

baz_1 = Baz.create(:related => 'baz')
baz_2 = Baz.create(:related => 'baz too!')
zab_1 = Zab.create(:related => 'zab')
zab_2 = Zab.create(:related => 'zab too!')

zar_1 = Zar.create!( :baz => baz_1)
zar_2 = Zar.create!( :baz => baz_2)
zar_1 = Zar.create!( :zab => zab_1)
zar_2 = Zar.create!( :zab => zab_2)

Zoo.create!(:zar => zar_1, :foo => 'foo')
Zoo.create!(:zar => zar_1, :foo => 'foo too')
Zoo.create!(:zar => zar_2, :foo => 'foo three')
end

after do
ActiveRecord::Migration.drop_table(:bazs)
ActiveRecord::Migration.drop_table(:zabs)
ActiveRecord::Migration.drop_table(:zars)
ActiveRecord::Migration.drop_table(:zoos)
end

it "should find the three records that are related to a baz record" do
Zoo.search_for('baz').length.should == 3
it "should find the three records that are related to a zab record" do
Zoo.search_for('zab').length.should == 3
end

it "should find no records that are related to a baz record" do
Zoo.search_for('related=baz AND related="baz too!"').length.should == 0
it "should find no records that are related to a zab record" do
Zoo.search_for('related=zab AND related="zab too!"').length.should == 0
end
end

Expand Down Expand Up @@ -392,8 +392,8 @@ class Owner < ActiveRecord::Base
@tag_2 = Tag.create!(:foo => 'foo too')
@tag_3 = Tag.create!(:foo => 'foo three')

@dog_1 = Dog.create(:related => 'baz')
@dog_2 = Dog.create(:related => 'baz too!')
@dog_1 = Dog.create(:related => 'zab')
@dog_2 = Dog.create(:related => 'zab too!')
@cat_1 = Cat.create(:related => 'mitzi')

@owner_1 = Owner.create(:name => 'Fred', :dogs => [@dog_1])
Expand Down Expand Up @@ -429,11 +429,11 @@ class Owner < ActiveRecord::Base
end

it "should find the two tags that are related to a dog record" do
Tag.search_for('dog=baz').length.should == 2
Tag.search_for('dog=zab').length.should == 2
end

it "should find the 3 tags that are related to dogs record" do
Tag.search_for('baz').length.should == 3
Tag.search_for('zab').length.should == 3
end
end

Expand Down Expand Up @@ -547,52 +547,52 @@ class User < ActiveRecord::Base
before do

# Create some tables with namespaces
ActiveRecord::Migration.create_table(:zan_mars) { |t| t.integer :koo_id; t.integer :baz_id }
ActiveRecord::Migration.create_table(:zan_bazs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:zan_mars) { |t| t.integer :koo_id; t.integer :zab_id }
ActiveRecord::Migration.create_table(:zan_zabs) { |t| t.string :related }
ActiveRecord::Migration.create_table(:zan_koos) { |t| t.string :foo }

# The related classes
module Zan; class Mar < ActiveRecord::Base; belongs_to :baz; belongs_to :koo; self.table_name = "zan_mars"; end; end
module Zan; class Baz < ActiveRecord::Base; has_many :mars; self.table_name = "zan_bazs"; end; end
module Zan; class Mar < ActiveRecord::Base; belongs_to :zab; belongs_to :koo; self.table_name = "zan_mars"; end; end
module Zan; class Zab < ActiveRecord::Base; has_many :mars; self.table_name = "zan_zabs"; end; end

# The class on which to call search_for
module Zan
class Koo < ActiveRecord::Base
has_many :mars, :class_name => "Zan::Mar"
has_many :bazs, :through => :mars
has_many :zabs, :through => :mars
self.table_name = "zan_koos"

scoped_search :relation => :bazs, :on => :related
scoped_search :relation => :zabs, :on => :related
end
end

@koo_1 = Zan::Koo.create!(:foo => 'foo')
@koo_2 = Zan::Koo.create!(:foo => 'foo too')
@koo_3 = Zan::Koo.create!(:foo => 'foo three')

@baz_1 = Zan::Baz.create(:related => 'baz')
@baz_2 = Zan::Baz.create(:related => 'baz too!')
@zab_1 = Zan::Zab.create(:related => 'zab')
@zab_2 = Zan::Zab.create(:related => 'zab too!')

@bar_1 = Zan::Mar.create!(:koo => @koo_1, :baz => @baz_1)
@bar_1 = Zan::Mar.create!(:koo => @koo_1, :zab => @zab_1)
@bar_2 = Zan::Mar.create!(:koo => @koo_1)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :baz => @baz_1)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :baz => @baz_2)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :baz => @baz_2)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :zab => @zab_1)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :zab => @zab_2)
@bar_3 = Zan::Mar.create!(:koo => @koo_2, :zab => @zab_2)
@bar_4 = Zan::Mar.create!(:koo => @koo_3)
end

after do
ActiveRecord::Migration.drop_table(:zan_bazs)
ActiveRecord::Migration.drop_table(:zan_zabs)
ActiveRecord::Migration.drop_table(:zan_mars)
ActiveRecord::Migration.drop_table(:zan_koos)
end

it "should find the two records that are related to a baz record" do
Zan::Koo.search_for('baz').length.should == 2
it "should find the two records that are related to a zab record" do
Zan::Koo.search_for('zab').length.should == 2
end

it "should find the one record that is related to two baz records" do
Zan::Koo.search_for('related=baz AND related="baz too!"').length.should == 1
it "should find the one record that is related to two zab records" do
Zan::Koo.search_for('related=zab AND related="zab too!"').length.should == 1
end
end

Expand Down
Loading