diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 963b002..687cbba 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -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) diff --git a/lib/scoped_search/auto_complete_builder.rb b/lib/scoped_search/auto_complete_builder.rb index 68bf60f..f27ea85 100644 --- a/lib/scoped_search/auto_complete_builder.rb +++ b/lib/scoped_search/auto_complete_builder.rb @@ -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)) @@ -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) diff --git a/spec/integration/auto_complete_spec.rb b/spec/integration/auto_complete_spec.rb index 697391f..51c2d2f 100644 --- a/spec/integration/auto_complete_spec.rb +++ b/spec/integration/auto_complete_spec.rb @@ -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 @@ -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 @@ -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 diff --git a/spec/integration/relation_querying_spec.rb b/spec/integration/relation_querying_spec.rb index 107d605..9557571 100644 --- a/spec/integration/relation_querying_spec.rb +++ b/spec/integration/relation_querying_spec.rb @@ -249,13 +249,13 @@ 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 @@ -263,38 +263,38 @@ class Koo < ActiveRecord::Base # 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 @@ -303,27 +303,27 @@ 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') @@ -331,17 +331,17 @@ class Zoo < ActiveRecord::Base 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 @@ -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]) @@ -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 @@ -547,22 +547,22 @@ 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 @@ -570,29 +570,29 @@ class Koo < ActiveRecord::Base @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