scoped_search 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ module ScopedSearch
2
+ module RailsHelper
3
+ # Creates a link that alternates between ascending and descending.
4
+ #
5
+ # Examples:
6
+ #
7
+ # sort @search, :by => :username
8
+ # sort @search, :by => :created_at, :as => "Created"
9
+ #
10
+ # This helper accepts the following options:
11
+ #
12
+ # * <tt>:by</tt> - the name of the named scope. This helper will prepend this value with "ascend_by_" and "descend_by_"
13
+ # * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to :by
14
+ def sort(field, options = {}, html_options = {})
15
+
16
+ unless options[:as]
17
+ id = field.to_s.downcase == "id"
18
+ options[:as] = id ? field.to_s.upcase : field.to_s.humanize
19
+ end
20
+
21
+ ascend = "#{field} ASC"
22
+ descend = "#{field} DESC"
23
+
24
+ ascending = params[:order] == ascend
25
+ new_sort = ascending ? descend : ascend
26
+ selected = [ascend, descend].include?(params[:order])
27
+
28
+ if selected
29
+ css_classes = html_options[:class] ? html_options[:class].split(" ") : []
30
+ if ascending
31
+ options[:as] = "&#9650;&nbsp;#{options[:as]}"
32
+ css_classes << "ascending"
33
+ else
34
+ options[:as] = "&#9660;&nbsp;#{options[:as]}"
35
+ css_classes << "descending"
36
+ end
37
+ html_options[:class] = css_classes.join(" ")
38
+ end
39
+
40
+ url_options = params.merge(:order => new_sort)
41
+
42
+ options[:as] = raw(options[:as]) if defined?(RailsXss)
43
+
44
+ a_link(options[:as], html_escape(url_for(url_options)),html_options)
45
+ end
46
+
47
+ # Adds AJAX auto complete functionality to the text input field with the
48
+ # DOM ID specified by +field_id+.
49
+ #
50
+ # Required +options+ is:
51
+ # <tt>:url</tt>:: URL to call for auto completion results
52
+ # in url_for format.
53
+ #
54
+ # Additional +options+ are:
55
+ # <tt>:update</tt>:: Specifies the DOM ID of the element whose
56
+ # innerHTML should be updated with the auto complete
57
+ # entries returned by the AJAX request.
58
+ # Defaults to <tt>field_id</tt> + '_auto_complete'
59
+ # <tt>:with</tt>:: A JavaScript expression specifying the
60
+ # parameters for the XMLHttpRequest. This defaults
61
+ # to 'fieldname=value'.
62
+ # <tt>:frequency</tt>:: Determines the time to wait after the last keystroke
63
+ # for the AJAX request to be initiated.
64
+ # <tt>:indicator</tt>:: Specifies the DOM ID of an element which will be
65
+ # displayed while auto complete is running.
66
+ # <tt>:tokens</tt>:: A string or an array of strings containing
67
+ # separator tokens for tokenized incremental
68
+ # auto completion. Example: <tt>:tokens => ','</tt> would
69
+ # allow multiple auto completion entries, separated
70
+ # by commas.
71
+ # <tt>:min_chars</tt>:: The minimum number of characters that should be
72
+ # in the input field before an Ajax call is made
73
+ # to the server.
74
+ # <tt>:on_hide</tt>:: A Javascript expression that is called when the
75
+ # auto completion div is hidden. The expression
76
+ # should take two variables: element and update.
77
+ # Element is a DOM element for the field, update
78
+ # is a DOM element for the div from which the
79
+ # innerHTML is replaced.
80
+ # <tt>:on_show</tt>:: Like on_hide, only now the expression is called
81
+ # then the div is shown.
82
+ # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
83
+ # user has selected one of the proposed values.
84
+ # The expression should take two variables: element and value.
85
+ # Element is a DOM element for the field, value
86
+ # is the value selected by the user.
87
+ # <tt>:select</tt>:: Pick the class of the element from which the value for
88
+ # insertion should be extracted. If this is not specified,
89
+ # the entire element is used.
90
+ # <tt>:method</tt>:: Specifies the HTTP verb to use when the auto completion
91
+ # request is made. Defaults to POST.
92
+ def auto_complete_field(field_id, options = {})
93
+ function = "var #{field_id}_auto_completer = new Ajax.Autocompleter("
94
+ function << "'#{field_id}', "
95
+ function << "'" + (options[:update] || "#{field_id}_auto_complete") + "', "
96
+ function << "'#{url_for(options[:url])}'"
97
+
98
+ js_options = {}
99
+ js_options[:tokens] = array_or_string_for_javascript(options[:tokens]) if options[:tokens]
100
+ js_options[:callback] = "function(element, value) { return #{options[:with]} }" if options[:with]
101
+ js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator]
102
+ js_options[:select] = "'#{options[:select]}'" if options[:select]
103
+ js_options[:paramName] = "'#{options[:param_name]}'" if options[:param_name]
104
+ js_options[:frequency] = "#{options[:frequency]}" if options[:frequency]
105
+ js_options[:method] = "'#{options[:method].to_s}'" if options[:method]
106
+
107
+ { :after_update_element => :afterUpdateElement,
108
+ :on_show => :onShow, :on_hide => :onHide, :min_chars => :minChars }.each do |k,v|
109
+ js_options[v] = options[k] if options[k]
110
+ end
111
+
112
+ function << (', ' + options_for_javascript(js_options) + ')')
113
+
114
+ javascript_tag(function)
115
+ end
116
+
117
+ def auto_complete_field_jquery(method, url, options = {})
118
+ function = <<-EOF
119
+ $.widget( "custom.catcomplete", $.ui.autocomplete, {
120
+ _renderMenu: function( ul, items ) {
121
+ var self = this,
122
+ currentCategory = "";
123
+ $.each( items, function( index, item ) {
124
+ if ( item.category != undefined && item.category != currentCategory ) {
125
+ ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
126
+ currentCategory = item.category;
127
+ }
128
+ if ( item.error != undefined ) {
129
+ ul.append( "<li class='ui-autocomplete-error'>" + item.error + "</li>" );
130
+ }
131
+ if( item.completed != undefined ) {
132
+ $( "<li></li>" ).data( "item.autocomplete", item )
133
+ .append( "<a>" + "<strong class='ui-autocomplete-completed'>" + item.completed + "</strong>" + item.part + "</a>" )
134
+ .appendTo( ul );
135
+ } else {
136
+ self._renderItem( ul, item );
137
+ }
138
+ });
139
+ }
140
+ });
141
+
142
+ $("##{method}")
143
+ .catcomplete({
144
+ source: function( request, response ) { $.getJSON( "#{url}", { #{method}: request.term }, response ); },
145
+ minLength: #{options[:min_length] || 0},
146
+ delay: #{options[:delay] || 200},
147
+ select: function(event, ui) { $( this ).catcomplete( "search" , ui.item.value); },
148
+ search: function(event, ui) { $(".auto_complete_clear").hide(); },
149
+ open: function(event, ui) { $(".auto_complete_clear").show(); }
150
+ });
151
+
152
+ $("##{method}").bind( "focus", function( event ) {
153
+ if( $( this )[0].value == "" ) {
154
+ $( this ).catcomplete( "search" );
155
+ }
156
+ });
157
+
158
+ EOF
159
+
160
+
161
+ javascript_tag(function)
162
+ end
163
+
164
+ def auto_complete_clear_value_button(field_id)
165
+ html_options = {:tabindex => '-1',:class=>"auto_complete_clear",:title =>'Clear Search', :onclick=>"document.getElementById('#{field_id}').value = '';"}
166
+ a_link("", "#", html_options)
167
+ end
168
+
169
+ def a_link(name, href, html_options)
170
+ tag_options = tag_options(html_options)
171
+ link = "<a href=\"#{href}\"#{tag_options}>#{name}</a>"
172
+ return link.respond_to?(:html_safe) ? link.html_safe : link
173
+ end
174
+
175
+ # Use this method in your view to generate a return for the AJAX auto complete requests.
176
+ #
177
+ # The auto_complete_result can of course also be called from a view belonging to the
178
+ # auto_complete action if you need to decorate it further.
179
+ def auto_complete_result(entries, phrase = nil)
180
+ return unless entries
181
+ items = entries.map { |entry| content_tag("li", phrase ? highlight(entry, phrase) : h(entry)) }
182
+ content_tag("ul", items)
183
+ end
184
+
185
+ # Wrapper for text_field with added AJAX auto completion functionality.
186
+ #
187
+ # In your controller, you'll need to define an action called
188
+ # auto_complete_method to respond the AJAX calls,
189
+ def auto_complete_field_tag(method, val,tag_options = {}, completion_options = {})
190
+ auto_completer_options = { :url => { :action => "auto_complete_#{method}" } }.update(completion_options)
191
+
192
+ text_field_tag(method, val,tag_options.merge(:class => "auto_complete_input")) +
193
+ auto_complete_clear_value_button(method) +
194
+ content_tag("div", "", :id => "#{method}_auto_complete", :class => "auto_complete") +
195
+ auto_complete_field(method, auto_completer_options)
196
+ end
197
+
198
+ # Wrapper for text_field with added JQuery auto completion functionality.
199
+ #
200
+ # In your controller, you'll need to define an action called
201
+ # auto_complete_method to respond the JQuery calls,
202
+ def auto_complete_field_tag_jquery(method, val,tag_options = {}, completion_options = {})
203
+ url = url_for(:action => "auto_complete_#{method}")
204
+ options = tag_options.merge(:class => "auto_complete_input")
205
+ text_field_tag(method, val, options) + auto_complete_clear_value_button(method) +
206
+ auto_complete_field_jquery(method, url, completion_options)
207
+ end
208
+
209
+ end
210
+ end
data/lib/scoped_search.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'active_record'
2
+
1
3
  # ScopedSearch is the base module for the scoped_search plugin. This file
2
4
  # defines some modules and exception classes, loads the necessary files, and
3
5
  # installs itself in ActiveRecord.
@@ -12,7 +14,7 @@ module ScopedSearch
12
14
 
13
15
  # The current scoped_search version. Do not change thisvalue by hand,
14
16
  # because it will be updated automatically by the gem release script.
15
- VERSION = "2.2.1"
17
+ VERSION = "2.3.0"
16
18
 
17
19
  # The ClassMethods module will be included into the ActiveRecord::Base class
18
20
  # to add the <tt>ActiveRecord::Base.scoped_search</tt> method and the
@@ -85,7 +87,13 @@ end
85
87
  require 'scoped_search/definition'
86
88
  require 'scoped_search/query_language'
87
89
  require 'scoped_search/query_builder'
90
+ require 'scoped_search/auto_complete_builder'
88
91
 
89
92
  # Import the search_on method in the ActiveReocrd::Base class
90
93
  ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods)
91
94
  ActiveRecord::Base.send(:extend, ScopedSearch::BackwardsCompatibility)
95
+
96
+ if defined?(ActionController)
97
+ require "scoped_search/rails_helper"
98
+ ActionController::Base.helper(ScopedSearch::RailsHelper)
99
+ end
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
 
4
4
  # Do not change the version and date fields by hand. This will be done
5
5
  # automatically by the gem release script.
6
- s.version = "2.2.1"
7
- s.date = "2010-11-09"
6
+ s.version = "2.3.0"
7
+ s.date = "2011-05-16"
8
8
 
9
9
  s.summary = "Easily search you ActiveRecord models with a simple query language using a named scope."
10
10
  s.description = <<-EOS
@@ -26,12 +26,14 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_runtime_dependency('activerecord', '>= 2.1.0')
28
28
  s.add_development_dependency('rspec', '~> 2.0')
29
+
30
+ s.add_development_dependency('sqlite3-ruby')
29
31
 
30
32
  s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
31
33
  s.extra_rdoc_files = ['README.rdoc']
32
34
 
33
35
  # Do not change the files and test_files fields by hand. This will be done
34
36
  # automatically by the gem release script.
35
- s.files = %w(.gitignore LICENSE README.rdoc Rakefile init.rb lib/scoped_search.rb lib/scoped_search/definition.rb lib/scoped_search/query_builder.rb lib/scoped_search/query_language.rb lib/scoped_search/query_language/ast.rb lib/scoped_search/query_language/parser.rb lib/scoped_search/query_language/tokenizer.rb scoped_search.gemspec spec/database.yml spec/integration/api_spec.rb spec/integration/ordinal_querying_spec.rb spec/integration/profile_querying_spec.rb spec/integration/relation_querying_spec.rb spec/integration/string_querying_spec.rb spec/lib/database.rb spec/lib/matchers.rb spec/lib/mocks.rb spec/spec_helper.rb spec/unit/ast_spec.rb spec/unit/definition_spec.rb spec/unit/parser_spec.rb spec/unit/query_builder_spec.rb spec/unit/tokenizer_spec.rb tasks/github-gem.rake)
36
- s.test_files = %w(spec/integration/api_spec.rb spec/integration/ordinal_querying_spec.rb spec/integration/profile_querying_spec.rb spec/integration/relation_querying_spec.rb spec/integration/string_querying_spec.rb spec/unit/ast_spec.rb spec/unit/definition_spec.rb spec/unit/parser_spec.rb spec/unit/query_builder_spec.rb spec/unit/tokenizer_spec.rb)
37
- end
37
+ s.files = %w(.gitignore .infinity_test Gemfile LICENSE README.rdoc Rakefile init.rb lib/scoped_search.rb lib/scoped_search/auto_complete_builder.rb lib/scoped_search/definition.rb lib/scoped_search/query_builder.rb lib/scoped_search/query_language.rb lib/scoped_search/query_language/ast.rb lib/scoped_search/query_language/parser.rb lib/scoped_search/query_language/tokenizer.rb lib/scoped_search/rails_helper.rb scoped_search.gemspec spec/database.yml spec/integration/api_spec.rb spec/integration/auto_complete_spec.rb spec/integration/key_value_querying_spec.rb spec/integration/ordinal_querying_spec.rb spec/integration/profile_querying_spec.rb spec/integration/relation_querying_spec.rb spec/integration/set_query_spec.rb spec/integration/string_querying_spec.rb spec/lib/database.rb spec/lib/matchers.rb spec/lib/mocks.rb spec/spec_helper.rb spec/unit/ast_spec.rb spec/unit/auto_complete_builder_spec.rb spec/unit/definition_spec.rb spec/unit/parser_spec.rb spec/unit/query_builder_spec.rb spec/unit/tokenizer_spec.rb tasks/github-gem.rake)
38
+ s.test_files = %w(spec/integration/api_spec.rb spec/integration/auto_complete_spec.rb spec/integration/key_value_querying_spec.rb spec/integration/ordinal_querying_spec.rb spec/integration/profile_querying_spec.rb spec/integration/relation_querying_spec.rb spec/integration/set_query_spec.rb spec/integration/string_querying_spec.rb spec/unit/ast_spec.rb spec/unit/auto_complete_builder_spec.rb spec/unit/definition_spec.rb spec/unit/parser_spec.rb spec/unit/query_builder_spec.rb spec/unit/tokenizer_spec.rb)
39
+ end
data/spec/database.yml CHANGED
@@ -7,13 +7,20 @@
7
7
  # to you.
8
8
 
9
9
 
10
- <% if Gem.available?('sqlite3-ruby') %>
11
- sqlite3:
12
- adapter: "sqlite3"
13
- database: ":memory:"
14
- <% end %>
10
+ # <% if Gem.available?('sqlite3-ruby') %>
11
+ # sqlite3:
12
+ # adapter: "sqlite3"
13
+ # database: ":memory:"
14
+ # <% end %>
15
15
 
16
- <% if Gem.available?('mysql') %>
16
+ <% if Gem.available?('mysql2') %>
17
+ mysql2:
18
+ adapter: "mysql2"
19
+ host: "localhost"
20
+ username: "root"
21
+ password:
22
+ database: "scoped_search_test"
23
+ <% elsif Gem.available?('mysql') %>
17
24
  mysql:
18
25
  adapter: "mysql"
19
26
  host: "localhost"
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
2
 
3
3
  describe ScopedSearch, "API" do
4
4
 
@@ -0,0 +1,140 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::RSpec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::RSpec::Database.establish_named_connection(db)
11
+
12
+ ## The related class
13
+ ActiveRecord::Migration.drop_table(:bars) rescue nil
14
+ ActiveRecord::Migration.create_table(:bars) { |t| t.string :related; t.integer :foo_id }
15
+ class ::Bar < ActiveRecord::Base; belongs_to :foo; end
16
+
17
+ ::Foo = ScopedSearch::RSpec::Database.create_model(:string => :string, :another => :string, :explicit => :string, :int => :integer, :date => :date, :unindexed => :integer) do |klass|
18
+ klass.has_many :bars
19
+
20
+ klass.scoped_search :on => [:string, :int, :date]
21
+ klass.scoped_search :on => :another, :default_operator => :eq, :alias => :alias
22
+ klass.scoped_search :on => :explicit, :only_explicit => true, :complete_value => true
23
+ klass.scoped_search :on => :related, :in => :bars, :rename => 'bars.related'.to_sym
24
+
25
+ end
26
+
27
+ @foo_1 = Foo.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz', :int => 9 , :date => 'February 8, 20011' , :unindexed => 10)
28
+ Foo.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz', :int => 9 , :date => 'February 10, 20011', :unindexed => 10)
29
+ Foo.create!(:string => 'baz', :another => nil, :explicit => nil , :int => nil, :date => nil , :unindexed => nil)
30
+
31
+ Bar.create!(:related => 'lala', :foo => @foo_1)
32
+ Bar.create!(:related => 'another lala', :foo => @foo_1)
33
+ end
34
+
35
+ after(:all) do
36
+ ScopedSearch::RSpec::Database.drop_model(Foo)
37
+ ScopedSearch::RSpec::Database.drop_model(Bar)
38
+ ScopedSearch::RSpec::Database.close_connection
39
+ end
40
+
41
+ context 'basic auto completer' do
42
+ it "should complete the field name" do
43
+ Foo.complete_for('str').should =~ ([' string '])
44
+ end
45
+
46
+ it "should not complete the logical operators at the beginning" do
47
+ Foo.complete_for('a').should_not contain(['and'])
48
+ end
49
+
50
+ it "should complete the string comparators" do
51
+ Foo.complete_for('string ').should =~ (["string != ", "string !~ ", "string = ", "string ~ "])
52
+ end
53
+
54
+ it "should complete the numerical comparators" do
55
+ Foo.complete_for('int ').should =~ (["int != ", "int < ", "int <= ", "int = ", "int > ", "int >= "])
56
+ end
57
+
58
+ it "should complete the temporal (date) comparators" do
59
+ Foo.complete_for('date ').should =~ (["date = ", "date < ", "date > "])
60
+ end
61
+
62
+ it "should raise error for unindexed field" do
63
+ lambda { Foo.complete_for('unindexed = 10 ')}.should raise_error(ScopedSearch::QueryNotSupported)
64
+ end
65
+
66
+ it "should raise error for unknown field" do
67
+ lambda {Foo.complete_for('unknown = 10 ')}.should raise_error(ScopedSearch::QueryNotSupported)
68
+ end
69
+
70
+ it "should complete logical comparators" do
71
+ Foo.complete_for('string ~ fo ').should contain("string ~ fo and", "string ~ fo or")
72
+ end
73
+
74
+ it "should complete prefix operators" do
75
+ Foo.complete_for(' ').should contain(" has", " not")
76
+ end
77
+
78
+ it "should not complete logical infix operators" do
79
+ Foo.complete_for(' ').should_not contain(" and", " or")
80
+ end
81
+
82
+ it "should not repeat logical operators" do
83
+ Foo.complete_for('string = foo and ').should_not contain("string = foo and and", "string = foo and or")
84
+ end
85
+ end
86
+
87
+ context 'using an aliased field' do
88
+ it "should complete an explicit match using its alias" do
89
+ Foo.complete_for('al').should contain(' alias ')
90
+ end
91
+ end
92
+
93
+ context 'value auto complete' do
94
+ it "should complete values list of values " do
95
+ Foo.complete_for('explicit = ').should have(1).item
96
+ end
97
+
98
+ it "should complete values should contain baz" do
99
+ Foo.complete_for('explicit = ').should contain('explicit = baz')
100
+ end
101
+ end
102
+
103
+ context 'auto complete relations' do
104
+ it "should complete related object name" do
105
+ Foo.complete_for('ba').should contain(' bars.related ')
106
+ end
107
+
108
+ it "should complete related object name with field name" do
109
+ Foo.complete_for('bars.').should contain(' bars.related ')
110
+ end
111
+ end
112
+
113
+ context 'using null prefix operators queries' do
114
+
115
+ it "should complete has operator" do
116
+ Foo.complete_for('has strin').should eql(['has string '])
117
+ end
118
+
119
+ it "should complete null? operator" do
120
+ Foo.complete_for('null? st').should eql(['null? string '])
121
+ end
122
+
123
+ it "should complete set? operator" do
124
+ Foo.complete_for('set? exp').should eql(['set? explicit '])
125
+ end
126
+
127
+ it "should complete null? operator for explicit field" do
128
+ Foo.complete_for('null? explici').should eql(['null? explicit '])
129
+ end
130
+
131
+ it "should not complete comparators after prefix statement" do
132
+ Foo.complete_for('has string ').should_not contain(['has string = '])
133
+ end
134
+
135
+ it "should not complete infix operator" do
136
+ Foo.complete_for('has string ').should_not contain('has string = ')
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,87 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::RSpec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::RSpec::Database.establish_named_connection(db)
11
+ end
12
+
13
+ after(:all) do
14
+ ScopedSearch::RSpec::Database.close_connection
15
+ end
16
+
17
+ context 'querying a key-value schema' do
18
+
19
+ before(:all) do
20
+ ActiveRecord::Migration.create_table(:keys) { |t| t.string :name }
21
+ class ::Key < ActiveRecord::Base; has_many :facts; end
22
+
23
+ ActiveRecord::Migration.create_table(:facts) { |t| t.string :value; t.integer :key_id; t.integer :bar_id }
24
+ class ::Fact < ActiveRecord::Base; belongs_to :key; belongs_to :bar; end
25
+
26
+ # The class that will run the queries
27
+ ::Bar = ScopedSearch::RSpec::Database.create_model(:name => :string) do |klass|
28
+ klass.has_many :facts
29
+ klass.has_many :keys, :through => :facts
30
+ klass.scoped_search :in => :facts, :on => :value, :rename => :facts, :in_key => :keys, :on_key => :name, :complete_value => true
31
+ end
32
+
33
+ @key1 = Key.create!(:name => 'color')
34
+ @key2 = Key.create!(:name => 'size')
35
+
36
+
37
+ @bar1 = Bar.create!(:name => 'bar')
38
+ @bar2 = Bar.create!(:name => 'barbary')
39
+
40
+ Fact.create!(:value => 'green', :key => @key1, :bar => @bar1)
41
+ Fact.create!(:value => 'gold' , :key => @key1, :bar => @bar2)
42
+ Fact.create!(:value => '5' , :key => @key2, :bar => @bar1)
43
+
44
+ end
45
+
46
+ after(:all) do
47
+ ScopedSearch::RSpec::Database.drop_model(Bar)
48
+ ScopedSearch::RSpec::Database.drop_model(Fact)
49
+ ScopedSearch::RSpec::Database.drop_model(Key)
50
+ Object.send :remove_const, :Fact
51
+ Object.send :remove_const, :Key
52
+ Object.send :remove_const, :Bar
53
+ end
54
+
55
+ it "should find all bars with a fact name color and fact value green" do
56
+ Bar.search_for('facts.color = green').should have(1).items
57
+ end
58
+
59
+ it "should find all bars with a fact name size and fact value 5" do
60
+ Bar.search_for('facts.size = 5').should have(1).items
61
+ end
62
+
63
+ it "should find all bars with a fact color green and fact size 5" do
64
+ Bar.search_for('facts.color = green and facts.size = 5').should have(1).items
65
+ end
66
+
67
+ it "should find all bars that has size value" do
68
+ Bar.search_for('has facts.size').should have(1).items
69
+ end
70
+
71
+ it "should find all bars that has color value" do
72
+ Bar.search_for('has facts.color').should have(2).items
73
+ end
74
+
75
+ it "should complete facts names" do
76
+ Bar.complete_for('facts.').should have(2).items
77
+ end
78
+
79
+ it "should complete values for fact name = color" do
80
+ Bar.complete_for('facts.color = ').should have(2).items
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
87
+