scoped_search 3.2.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/scoped_search/auto_complete_builder.rb +8 -6
- data/lib/scoped_search/definition.rb +1 -1
- data/lib/scoped_search/query_builder.rb +13 -6
- data/lib/scoped_search/rails_helper.rb +3 -3
- data/lib/scoped_search/version.rb +1 -1
- data/spec/integration/auto_complete_spec.rb +4 -0
- data/spec/integration/key_value_querying_spec.rb +4 -0
- data/spec/integration/string_querying_spec.rb +45 -0
- data/spec/unit/rails_helper_spec.rb +22 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8e91bf7d864d576d7020bd56184d9ed7173e5c3
|
4
|
+
data.tar.gz: 502a182f6303fb040a4b3e77f54d984a85d54093
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce108bace0ec727d852a4e0d9db9d330bab9d157a4dd985d0bb77e88eff186553462451beaee8fc532ac8d0b95361174b60772511ee7e1016be5c6f3640fe16e
|
7
|
+
data.tar.gz: c5dcd4e411820161628a61cf0485bf457121ba4ab8338f12d3723f83d8a2938bb1b85469eab096bce6f96f9b76bfb65af58723973de9d63f8e996692cfa4c3b4
|
@@ -176,11 +176,12 @@ module ScopedSearch
|
|
176
176
|
|
177
177
|
field.key_klass
|
178
178
|
.where(value_conditions(field_name, val))
|
179
|
-
.select(
|
179
|
+
.select(field_name)
|
180
180
|
.limit(20)
|
181
181
|
.map(&field.key_field)
|
182
182
|
.compact
|
183
183
|
.map { |f| "#{name}.#{f} " }
|
184
|
+
.uniq
|
184
185
|
end
|
185
186
|
|
186
187
|
# this method auto-completes values of fields that have a :complete_value marker
|
@@ -201,12 +202,13 @@ module ScopedSearch
|
|
201
202
|
return complete_key_value(field, token, val) if field.key_field
|
202
203
|
|
203
204
|
completer_scope(field)
|
204
|
-
.where(value_conditions(field, val))
|
205
|
-
.select(
|
205
|
+
.where(value_conditions(field.quoted_field, val))
|
206
|
+
.select(field.quoted_field)
|
206
207
|
.limit(20)
|
207
208
|
.map(&field.field)
|
208
209
|
.compact
|
209
210
|
.map { |v| v.to_s =~ /\s/ ? "\"#{v}\"" : v }
|
211
|
+
.uniq
|
210
212
|
end
|
211
213
|
|
212
214
|
def completer_scope(field)
|
@@ -251,7 +253,7 @@ module ScopedSearch
|
|
251
253
|
end
|
252
254
|
|
253
255
|
query
|
254
|
-
.where(value_conditions(field, val))
|
256
|
+
.where(value_conditions(field.quoted_field, val))
|
255
257
|
.select("DISTINCT #{field.quoted_field}")
|
256
258
|
.limit(20)
|
257
259
|
.map(&field.field)
|
@@ -260,8 +262,8 @@ module ScopedSearch
|
|
260
262
|
end
|
261
263
|
|
262
264
|
# This method returns conditions for selecting completion from partial value
|
263
|
-
def value_conditions(
|
264
|
-
val.blank? ? nil : "CAST(#{
|
265
|
+
def value_conditions(field_name, val)
|
266
|
+
val.blank? ? nil : "CAST(#{field_name} as CHAR(50)) LIKE '#{val.gsub("'","''")}%'".tr_s('%*', '%')
|
265
267
|
end
|
266
268
|
|
267
269
|
# This method complete infix operators by field type
|
@@ -250,7 +250,7 @@ module ScopedSearch
|
|
250
250
|
def register_named_scope! # :nodoc
|
251
251
|
definition = self
|
252
252
|
@klass.scope(:search_for, proc { |query, options|
|
253
|
-
search_scope = ActiveRecord::VERSION::MAJOR == 3 ? @klass.scoped : @klass
|
253
|
+
search_scope = ActiveRecord::VERSION::MAJOR == 3 ? @klass.scoped : (ActiveRecord::VERSION::MINOR < 1 ? @klass.where(nil) : @klass.all)
|
254
254
|
|
255
255
|
find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options || {})
|
256
256
|
search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
|
@@ -83,17 +83,21 @@ module ScopedSearch
|
|
83
83
|
return find_attributes
|
84
84
|
end
|
85
85
|
|
86
|
-
def
|
86
|
+
def find_field_for_order_by(order, &block)
|
87
87
|
order ||= definition.default_order
|
88
|
-
return nil if order.blank?
|
88
|
+
return [nil, nil] if order.blank?
|
89
89
|
field_name, direction_name = order.to_s.split(/\s+/, 2)
|
90
90
|
field = definition.field_by_name(field_name)
|
91
91
|
raise ScopedSearch::QueryNotSupported, "the field '#{field_name}' in the order statement is not valid field for search" unless field
|
92
|
+
return field, direction_name
|
93
|
+
end
|
94
|
+
|
95
|
+
def order_by(order, &block)
|
96
|
+
field, direction_name = find_field_for_order_by(order, &block)
|
97
|
+
return nil if field.nil?
|
92
98
|
sql = field.to_sql(&block)
|
93
99
|
direction = (!direction_name.nil? && direction_name.downcase.eql?('desc')) ? " DESC" : " ASC"
|
94
|
-
|
95
|
-
|
96
|
-
return order
|
100
|
+
return sql + direction
|
97
101
|
end
|
98
102
|
|
99
103
|
# A hash that maps the operators of the query language with the corresponding SQL operator.
|
@@ -516,7 +520,10 @@ module ScopedSearch
|
|
516
520
|
|
517
521
|
def order_by(order, &block)
|
518
522
|
sql = super(order, &block)
|
519
|
-
|
523
|
+
if sql
|
524
|
+
field, _ = find_field_for_order_by(order, &block)
|
525
|
+
sql += sql.include?('DESC') ? ' NULLS LAST ' : ' NULLS FIRST ' if !field.nil? && field.column.null
|
526
|
+
end
|
520
527
|
sql
|
521
528
|
end
|
522
529
|
end
|
@@ -22,7 +22,7 @@ module ScopedSearch
|
|
22
22
|
|
23
23
|
ascend = "#{field} ASC"
|
24
24
|
descend = "#{field} DESC"
|
25
|
-
|
25
|
+
selected_sort = [ascend, descend].find { |o| o == params[:order] }
|
26
26
|
|
27
27
|
case params[:order]
|
28
28
|
when ascend
|
@@ -33,9 +33,9 @@ module ScopedSearch
|
|
33
33
|
new_sort = ["ASC", "DESC"].include?(options[:default]) ? "#{field} #{options[:default]}" : ascend
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
unless selected_sort.nil?
|
37
37
|
css_classes = html_options[:class] ? html_options[:class].split(" ") : []
|
38
|
-
if
|
38
|
+
if selected_sort == ascend
|
39
39
|
options[:as] = "▲ #{options[:as]}"
|
40
40
|
css_classes << "ascending"
|
41
41
|
else
|
@@ -89,6 +89,10 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
89
89
|
Foo.complete_for('date ').should =~ (["date = ", "date < ", "date > "])
|
90
90
|
end
|
91
91
|
|
92
|
+
it "should complete when query is already distinct" do
|
93
|
+
Foo.uniq.complete_for('int =').length.should > 0
|
94
|
+
end
|
95
|
+
|
92
96
|
it "should raise error for unindexed field" do
|
93
97
|
lambda { Foo.complete_for('unindexed = 10 ')}.should raise_error(ScopedSearch::QueryNotSupported)
|
94
98
|
end
|
@@ -91,6 +91,10 @@ require "spec_helper"
|
|
91
91
|
Item.complete_for('facts.').length.should == 2
|
92
92
|
end
|
93
93
|
|
94
|
+
it "should complete facts names with partial name" do
|
95
|
+
Item.complete_for('facts.c').length.should == 1
|
96
|
+
end
|
97
|
+
|
94
98
|
it "should complete values for fact name = color" do
|
95
99
|
Item.complete_for('facts.color = ').length.should == 2
|
96
100
|
end
|
@@ -31,6 +31,51 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
31
31
|
ScopedSearch::RSpec::Database.close_connection
|
32
32
|
end
|
33
33
|
|
34
|
+
context 'with no scoped_search defaults' do
|
35
|
+
|
36
|
+
before(:all) do
|
37
|
+
@class2 = ScopedSearch::RSpec::Database.create_model(
|
38
|
+
:string => :string,
|
39
|
+
:another => :string,
|
40
|
+
) do |klass|
|
41
|
+
klass.scoped_search :on => :string
|
42
|
+
klass.scoped_search :on => :another
|
43
|
+
end
|
44
|
+
|
45
|
+
@class2.create!(:string => 'foo', :another => 'foo')
|
46
|
+
@class2.create!(:string => 'bar', :another => 'foo')
|
47
|
+
@class2.create!(:string => 'baz', :another => 'bar')
|
48
|
+
end
|
49
|
+
|
50
|
+
after(:all) do
|
51
|
+
ScopedSearch::RSpec::Database.drop_model(@class2)
|
52
|
+
end
|
53
|
+
|
54
|
+
context '#search_for with an empty string' do
|
55
|
+
|
56
|
+
it 'should not remove previous scope' do
|
57
|
+
@class2.where(another: 'foo').search_for('').count.should == 2
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should return an ActiveRecord::Relation' do
|
61
|
+
@class2.search_for('').should be_a(ActiveRecord::Relation)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context '#search_for with nil' do
|
67
|
+
it 'should not remove previous scope' do
|
68
|
+
@class2.where(another: 'foo').search_for(nil).count.should == 2
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should return an ActiveRecord::Relation' do
|
72
|
+
@class2.search_for(nil).should be_a(ActiveRecord::Relation)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
34
79
|
context 'in an implicit string field' do
|
35
80
|
it "should find the record with an exact string match" do
|
36
81
|
@class.search_for('foo').length.should == 1
|
@@ -70,4 +70,26 @@ describe ScopedSearch::RailsHelper do
|
|
70
70
|
params[:order] = "field ASC"
|
71
71
|
sort("other")
|
72
72
|
end
|
73
|
+
|
74
|
+
it "should add no styling by default" do
|
75
|
+
should_receive(:url_for)
|
76
|
+
should_receive(:a_link).with('Field', anything, hash_excluding(:class))
|
77
|
+
sort("field")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should add ascending style for current ascending sort order " do
|
81
|
+
should_receive(:url_for)
|
82
|
+
should_receive(:a_link).with('▲ Field', anything, hash_including(:class => 'ascending'))
|
83
|
+
|
84
|
+
params[:order] = "field ASC"
|
85
|
+
sort("field")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should add descending style for current descending sort order " do
|
89
|
+
should_receive(:url_for)
|
90
|
+
should_receive(:a_link).with('▼ Field', anything, hash_including(:class => 'descending'))
|
91
|
+
|
92
|
+
params[:order] = "field DESC"
|
93
|
+
sort("field")
|
94
|
+
end
|
73
95
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scoped_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amos Benari
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-
|
13
|
+
date: 2015-06-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|