scoped_search 2.6.5 → 2.7.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +10 -11
- data/lib/scoped_search.rb +8 -4
- data/lib/scoped_search/auto_complete_builder.rb +9 -9
- data/lib/scoped_search/definition.rb +11 -8
- data/lib/scoped_search/query_builder.rb +28 -22
- data/lib/scoped_search/rails_helper.rb +12 -4
- data/lib/scoped_search/version.rb +1 -1
- data/spec/database.jruby.yml +9 -9
- data/spec/database.ruby.yml +14 -12
- data/spec/integration/auto_complete_spec.rb +9 -0
- data/spec/integration/relation_querying_spec.rb +20 -0
- data/spec/integration/string_querying_spec.rb +23 -5
- data/spec/unit/rails_helper_spec.rb +73 -0
- metadata +19 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7053f29622ce510229c05ba561ce8a1e275942cf
|
4
|
+
data.tar.gz: e28a7d2541cc4e586b2bcbde23ea4742fa7a5671
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e3fac5a915408137d1ae6eb7194ddb43f75501407e13dd7203da779dc8e23fc011b0f55f2b7a1b6c461fb799e3afd376881bd001d6cc7276443d96ce8f2e7cd
|
7
|
+
data.tar.gz: 7b8643f0e611d089409b34f87f0102ef856ec9079d2f07e0ce1458ded6402f1d7c08d251ab4d609c29489e4eb5d5a673390e99948ce429eea663c3ef1638c85b
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -5,28 +5,26 @@ before_script:
|
|
5
5
|
- mysql -e 'create database scoped_search_test;'
|
6
6
|
rvm:
|
7
7
|
- 1.8.7
|
8
|
-
- 1.9.2
|
9
8
|
- 1.9.3
|
10
9
|
- 2.0.0
|
10
|
+
- 2.1.1
|
11
11
|
- ruby-head
|
12
|
-
- ree
|
13
12
|
- jruby-18mode
|
14
13
|
- jruby-19mode
|
15
14
|
- jruby-head
|
16
15
|
gemfile:
|
17
16
|
- Gemfile.activerecord2
|
18
17
|
- Gemfile.activerecord3
|
18
|
+
- Gemfile.activerecord4
|
19
19
|
matrix:
|
20
20
|
include:
|
21
|
-
- rvm: 1.9.3
|
22
|
-
gemfile: Gemfile.activerecord4
|
23
|
-
- rvm: 2.0.0
|
24
|
-
gemfile: Gemfile.activerecord4
|
25
21
|
allow_failures:
|
26
22
|
- rvm: ruby-head
|
27
23
|
- rvm: jruby-head
|
28
|
-
- rvm:
|
29
|
-
|
24
|
+
- rvm: 2.0.0
|
25
|
+
gemfile: Gemfile.activerecord2
|
26
|
+
- rvm: 2.1.1
|
27
|
+
gemfile: Gemfile.activerecord2
|
30
28
|
exclude:
|
31
29
|
- rvm: jruby-18mode
|
32
30
|
gemfile: Gemfile.activerecord2
|
@@ -34,6 +32,7 @@ matrix:
|
|
34
32
|
gemfile: Gemfile.activerecord2
|
35
33
|
- rvm: jruby-head
|
36
34
|
gemfile: Gemfile.activerecord2
|
37
|
-
- rvm:
|
38
|
-
gemfile: Gemfile.
|
39
|
-
- rvm:
|
35
|
+
- rvm: 1.8.7
|
36
|
+
gemfile: Gemfile.activerecord4
|
37
|
+
- rvm: jruby-18mode
|
38
|
+
gemfile: Gemfile.activerecord4
|
data/lib/scoped_search.rb
CHANGED
@@ -17,19 +17,23 @@ module ScopedSearch
|
|
17
17
|
# <tt>ActiveRecord::Base.search_for</tt> named scope.
|
18
18
|
module ClassMethods
|
19
19
|
|
20
|
+
def self.extended(base)
|
21
|
+
base.class_attribute :scoped_search_definition
|
22
|
+
end
|
23
|
+
|
20
24
|
# Export the scoped_search method fo defining the search options.
|
21
25
|
# This method will create a definition instance for the class if it does not yet exist,
|
22
26
|
# and use the object as block argument and retun value.
|
23
27
|
def scoped_search(*definitions)
|
24
|
-
|
28
|
+
self.scoped_search_definition ||= ScopedSearch::Definition.new(self)
|
25
29
|
definitions.each do |definition|
|
26
30
|
if definition[:on].kind_of?(Array)
|
27
|
-
definition[:on].each { |field|
|
31
|
+
definition[:on].each { |field| self.scoped_search_definition.define(definition.merge(:on => field)) }
|
28
32
|
else
|
29
|
-
|
33
|
+
self.scoped_search_definition.define(definition)
|
30
34
|
end
|
31
35
|
end
|
32
|
-
return
|
36
|
+
return self.scoped_search_definition
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
@@ -137,13 +137,13 @@ module ScopedSearch
|
|
137
137
|
q.chomp!(quoted[1]) if quoted
|
138
138
|
end
|
139
139
|
|
140
|
-
# for
|
140
|
+
# for dotted field names compact the suggestions list to be one suggestion
|
141
141
|
# unless the user has typed the relation name entirely or the suggestion list
|
142
142
|
# is short.
|
143
|
-
if (suggestions.size > 10 && (tokens.empty? || !
|
144
|
-
suggestions = suggestions.map
|
145
|
-
|
146
|
-
|
143
|
+
if (suggestions.size > 10 && (tokens.empty? || !tokens.last.to_s.include?('.')) && !is_value)
|
144
|
+
suggestions = suggestions.map do |s|
|
145
|
+
s.to_s.split('.')[0].end_with?(tokens.last.to_s) ? s.to_s : s.to_s.split('.')[0]
|
146
|
+
end
|
147
147
|
end
|
148
148
|
|
149
149
|
suggestions.uniq.map {|m| "#{q} #{m}"}
|
@@ -158,7 +158,7 @@ module ScopedSearch
|
|
158
158
|
if (f[1].key_field)
|
159
159
|
keywords += complete_key(f[0], f[1], tokens.last)
|
160
160
|
else
|
161
|
-
keywords << f[0].to_s+' '
|
161
|
+
keywords << f[0].to_s + ' '
|
162
162
|
end
|
163
163
|
end
|
164
164
|
keywords.sort
|
@@ -182,11 +182,11 @@ module ScopedSearch
|
|
182
182
|
# this method auto-completes values of fields that have a :complete_value marker
|
183
183
|
def complete_value
|
184
184
|
if last_token_is(COMPARISON_OPERATORS)
|
185
|
-
token = tokens[tokens.size-2]
|
185
|
+
token = tokens[tokens.size - 2]
|
186
186
|
val = ''
|
187
187
|
else
|
188
|
-
token = tokens[tokens.size-3]
|
189
|
-
val = tokens[tokens.size-1]
|
188
|
+
token = tokens[tokens.size - 3]
|
189
|
+
val = tokens[tokens.size - 1]
|
190
190
|
end
|
191
191
|
|
192
192
|
field = definition.field_by_name(token)
|
@@ -234,13 +234,14 @@ module ScopedSearch
|
|
234
234
|
|
235
235
|
# Registers the search_for named scope within the class that is used for searching.
|
236
236
|
def register_named_scope! # :nodoc
|
237
|
+
definition = self
|
237
238
|
if @klass.ancestors.include?(ActiveRecord::Base)
|
238
239
|
case ActiveRecord::VERSION::MAJOR
|
239
240
|
when 2
|
240
|
-
@klass.named_scope(:search_for, lambda { |*args| ScopedSearch::QueryBuilder.build_query(
|
241
|
+
@klass.named_scope(:search_for, lambda { |*args| ScopedSearch::QueryBuilder.build_query(definition, args[0], args[1]) })
|
241
242
|
when 3
|
242
243
|
@klass.scope(:search_for, lambda { |*args|
|
243
|
-
find_options = ScopedSearch::QueryBuilder.build_query(
|
244
|
+
find_options = ScopedSearch::QueryBuilder.build_query(definition, args[0], args[1])
|
244
245
|
search_scope = @klass.scoped
|
245
246
|
search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
|
246
247
|
search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
|
@@ -250,7 +251,7 @@ module ScopedSearch
|
|
250
251
|
})
|
251
252
|
when 4
|
252
253
|
@klass.scope(:search_for, lambda { |*args|
|
253
|
-
find_options = ScopedSearch::QueryBuilder.build_query(
|
254
|
+
find_options = ScopedSearch::QueryBuilder.build_query(definition, args[0], args[1])
|
254
255
|
search_scope = @klass.all
|
255
256
|
search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
|
256
257
|
search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
|
@@ -269,11 +270,13 @@ module ScopedSearch
|
|
269
270
|
|
270
271
|
# Registers the complete_for method within the class that is used for searching.
|
271
272
|
def register_complete_for! # :nodoc
|
272
|
-
@klass.
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
273
|
+
@klass.extend(ScopedSearch::AutoCompleteClassMethods)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
module AutoCompleteClassMethods
|
278
|
+
def complete_for(query, options = {})
|
279
|
+
ScopedSearch::AutoCompleteBuilder.auto_complete(scoped_search_definition, query, options)
|
277
280
|
end
|
278
281
|
end
|
279
282
|
end
|
@@ -89,19 +89,20 @@ module ScopedSearch
|
|
89
89
|
def order_by(order, &block)
|
90
90
|
order ||= definition.default_order
|
91
91
|
return nil if order.blank?
|
92
|
-
|
93
|
-
|
92
|
+
field_name, direction_name = order.to_s.split(/\s+/, 2)
|
93
|
+
field = definition.field_by_name(field_name)
|
94
|
+
raise ScopedSearch::QueryNotSupported, "the field '#{field_name}' in the order statement is not valid field for search" unless field
|
94
95
|
sql = field.to_sql(&block)
|
95
|
-
direction = (
|
96
|
+
direction = (!direction_name.nil? && direction_name.downcase.eql?('desc')) ? " DESC" : " ASC"
|
96
97
|
order = sql + direction
|
97
98
|
|
98
99
|
return order
|
99
100
|
end
|
100
101
|
|
101
102
|
# A hash that maps the operators of the query language with the corresponding SQL operator.
|
102
|
-
SQL_OPERATORS = { :eq =>'=', :ne => '<>', :like => 'LIKE', :unlike => 'NOT LIKE',
|
103
|
-
:gt => '>',
|
104
|
-
:in => 'IN'
|
103
|
+
SQL_OPERATORS = { :eq => '=', :ne => '<>', :like => 'LIKE', :unlike => 'NOT LIKE',
|
104
|
+
:gt => '>', :lt =>'<', :lte => '<=', :gte => '>=',
|
105
|
+
:in => 'IN', :notin => 'NOT IN' }
|
105
106
|
|
106
107
|
# Return the SQL operator to use given an operator symbol and field definition.
|
107
108
|
#
|
@@ -223,13 +224,14 @@ module ScopedSearch
|
|
223
224
|
elsif field.definition.klass.reflections[field.relation].try(:macro) == :has_many
|
224
225
|
value = value.to_i if field.offset
|
225
226
|
yield(:parameter, value)
|
226
|
-
|
227
|
+
connection = field.definition.klass.connection
|
228
|
+
primary_key = "#{connection.quote_table_name(field.definition.klass.table_name)}.#{connection.quote_column_name(field.definition.klass.primary_key)}"
|
227
229
|
if field.definition.klass.reflections[field.relation].options.has_key?(:through)
|
228
230
|
join = has_many_through_join(field)
|
229
231
|
return "#{primary_key} IN (SELECT #{primary_key} FROM #{join} WHERE #{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ? )"
|
230
232
|
else
|
231
|
-
foreign_key = field.reflection_keys(field.definition.klass.reflections[field.relation])[1]
|
232
|
-
return "#{primary_key} IN (SELECT #{foreign_key} FROM #{field.klass.
|
233
|
+
foreign_key = connection.quote_column_name(field.reflection_keys(field.definition.klass.reflections[field.relation])[1])
|
234
|
+
return "#{primary_key} IN (SELECT #{foreign_key} FROM #{connection.quote_table_name(field.klass.table_name)} WHERE #{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ? )"
|
233
235
|
end
|
234
236
|
else
|
235
237
|
value = value.to_i if field.offset
|
@@ -241,23 +243,27 @@ module ScopedSearch
|
|
241
243
|
def has_many_through_join(field)
|
242
244
|
many_class = field.definition.klass
|
243
245
|
through = many_class.reflections[field.relation].options[:through]
|
244
|
-
|
246
|
+
connection = many_class.connection
|
247
|
+
|
248
|
+
# table names
|
245
249
|
endpoint_table_name = field.klass.table_name
|
246
250
|
many_table_name = many_class.table_name
|
247
251
|
middle_table_name = many_class.reflections[through].klass.table_name
|
248
|
-
|
252
|
+
|
253
|
+
# primary and foreign keys + optional condition for the many to middle join
|
249
254
|
pk1, fk1 = field.reflection_keys(many_class.reflections[through])
|
250
255
|
condition1 = field.reflection_conditions(field.klass.reflections[many_table_name.to_sym])
|
251
|
-
|
256
|
+
|
257
|
+
# primary and foreign keys + optional condition for the endpoint to middle join
|
252
258
|
pk2, fk2 = field.reflection_keys(field.klass.reflections[middle_table_name.to_sym])
|
253
259
|
condition2 = field.reflection_conditions(many_class.reflections[field.relation])
|
254
260
|
|
255
261
|
<<-SQL
|
256
|
-
#{many_table_name}
|
257
|
-
INNER JOIN #{middle_table_name}
|
258
|
-
ON #{many_table_name}.#{pk1} = #{middle_table_name}.#{fk1} #{condition1}
|
259
|
-
INNER JOIN #{endpoint_table_name}
|
260
|
-
ON #{middle_table_name}.#{fk2} = #{endpoint_table_name}.#{pk2} #{condition2}
|
262
|
+
#{connection.quote_table_name(many_table_name)}
|
263
|
+
INNER JOIN #{connection.quote_table_name(middle_table_name)}
|
264
|
+
ON #{connection.quote_table_name(many_table_name)}.#{connection.quote_column_name(pk1)} = #{connection.quote_table_name(middle_table_name)}.#{connection.quote_column_name(fk1)} #{condition1}
|
265
|
+
INNER JOIN #{connection.quote_table_name(endpoint_table_name)}
|
266
|
+
ON #{connection.quote_table_name(middle_table_name)}.#{connection.quote_column_name(fk2)} = #{connection.quote_table_name(endpoint_table_name)}.#{connection.quote_column_name(pk2)} #{condition2}
|
261
267
|
SQL
|
262
268
|
end
|
263
269
|
|
@@ -277,13 +283,13 @@ module ScopedSearch
|
|
277
283
|
if key_relation
|
278
284
|
yield(:joins, construct_join_sql(key_relation, num) )
|
279
285
|
yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?")
|
280
|
-
klass_table_name = relation ? "#{klass.table_name}_#{num}" :
|
281
|
-
return "#{klass_table_name}.#{connection.quote_column_name(field.to_s)}"
|
286
|
+
klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name
|
287
|
+
return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}"
|
282
288
|
elsif key_field
|
283
289
|
yield(:joins, construct_simple_join_sql(num))
|
284
290
|
yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?")
|
285
|
-
klass_table_name = relation ? "#{klass.table_name}_#{num}" :
|
286
|
-
return "#{klass_table_name}.#{connection.quote_column_name(field.to_s)}"
|
291
|
+
klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name
|
292
|
+
return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}"
|
287
293
|
elsif relation
|
288
294
|
yield(:include, relation)
|
289
295
|
end
|
@@ -340,7 +346,7 @@ module ScopedSearch
|
|
340
346
|
main_table = definition.klass.table_name
|
341
347
|
main_table_pk, value_table_fk_main = reflection_keys(definition.klass.reflections[relation])
|
342
348
|
|
343
|
-
join_sql = "\n INNER JOIN #{connection.quote_table_name(key_value_table)} #{key_value_table}_#{num} ON (#{connection.quote_table_name(main_table)}.#{main_table_pk} = #{key_value_table}_#{num}.#{value_table_fk_main})"
|
349
|
+
join_sql = "\n INNER JOIN #{connection.quote_table_name(key_value_table)} #{key_value_table}_#{num} ON (#{connection.quote_table_name(main_table)}.#{connection.quote_column_name(main_table_pk)} = #{key_value_table}_#{num}.#{connection.quote_column_name(value_table_fk_main)})"
|
344
350
|
return join_sql
|
345
351
|
end
|
346
352
|
|
@@ -7,11 +7,13 @@ module ScopedSearch
|
|
7
7
|
#
|
8
8
|
# sort @search, :by => :username
|
9
9
|
# sort @search, :by => :created_at, :as => "Created"
|
10
|
+
# sort @search, :by => :created_at, :default => "DESC"
|
10
11
|
#
|
11
12
|
# This helper accepts the following options:
|
12
13
|
#
|
13
14
|
# * <tt>:by</tt> - the name of the named scope. This helper will prepend this value with "ascend_by_" and "descend_by_"
|
14
15
|
# * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to :by
|
16
|
+
# * <tt>:default</tt> - default sorting order, DESC or ASC
|
15
17
|
def sort(field, options = {}, html_options = {})
|
16
18
|
|
17
19
|
unless options[:as]
|
@@ -21,14 +23,20 @@ module ScopedSearch
|
|
21
23
|
|
22
24
|
ascend = "#{field} ASC"
|
23
25
|
descend = "#{field} DESC"
|
24
|
-
|
25
|
-
ascending = params[:order] == ascend
|
26
|
-
new_sort = ascending ? descend : ascend
|
27
26
|
selected = [ascend, descend].include?(params[:order])
|
28
27
|
|
28
|
+
case params[:order]
|
29
|
+
when ascend
|
30
|
+
new_sort = descend
|
31
|
+
when descend
|
32
|
+
new_sort = ascend
|
33
|
+
else
|
34
|
+
new_sort = ["ASC", "DESC"].include?(options[:default]) ? "#{field} #{options[:default]}" : ascend
|
35
|
+
end
|
36
|
+
|
29
37
|
if selected
|
30
38
|
css_classes = html_options[:class] ? html_options[:class].split(" ") : []
|
31
|
-
if
|
39
|
+
if new_sort == ascend
|
32
40
|
options[:as] = "▲ #{options[:as]}"
|
33
41
|
css_classes << "ascending"
|
34
42
|
else
|
data/spec/database.jruby.yml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
sqlite:
|
2
|
-
adapter:
|
2
|
+
adapter: jdbcsqlite3
|
3
3
|
database: ":memory:"
|
4
4
|
|
5
5
|
mysql:
|
6
|
-
adapter:
|
7
|
-
host:
|
8
|
-
username:
|
9
|
-
database:
|
6
|
+
adapter: jdbcmysql
|
7
|
+
host: localhost
|
8
|
+
username: root
|
9
|
+
database: scoped_search_test
|
10
10
|
|
11
11
|
postgresql:
|
12
|
-
adapter:
|
13
|
-
host:
|
14
|
-
username:
|
15
|
-
database:
|
12
|
+
adapter: jdbcpostgresql
|
13
|
+
host: localhost
|
14
|
+
username: postgres
|
15
|
+
database: scoped_search_test
|
data/spec/database.ruby.yml
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
sqlite:
|
2
|
-
adapter:
|
2
|
+
adapter: sqlite3
|
3
3
|
database: ":memory:"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
mysql:
|
6
|
+
adapter: mysql2
|
7
|
+
host: localhost
|
8
|
+
port: 3306
|
9
|
+
username: root
|
10
|
+
database: scoped_search_test
|
11
|
+
encoding: utf8
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
postgresql:
|
14
|
+
adapter: postgresql
|
15
|
+
host: localhost
|
16
|
+
username: postgres
|
17
|
+
port: 5432
|
18
|
+
database: scoped_search_test
|
@@ -38,6 +38,9 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
38
38
|
scoped_search :on => :related, :in => :bars, :rename => 'bars.related'.to_sym
|
39
39
|
end
|
40
40
|
|
41
|
+
class ::Infoo < ::Foo
|
42
|
+
end
|
43
|
+
|
41
44
|
@foo_1 = Foo.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz', :int => 9 , :date => 'February 8, 2011' , :unindexed => 10)
|
42
45
|
Foo.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz', :int => 9 , :date => 'February 10, 2011', :unindexed => 10)
|
43
46
|
Foo.create!(:string => 'baz', :another => nil, :explicit => nil , :int => nil, :date => nil , :unindexed => nil)
|
@@ -52,6 +55,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
52
55
|
|
53
56
|
Object.send :remove_const, :Foo
|
54
57
|
Object.send :remove_const, :Bar
|
58
|
+
Object.send :remove_const, :Infoo
|
55
59
|
|
56
60
|
ScopedSearch::RSpec::Database.close_connection
|
57
61
|
end
|
@@ -104,7 +108,12 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
104
108
|
it "should not contain deprecated field in autocompleter" do
|
105
109
|
Foo.complete_for(' ').should_not contain(" deprecated")
|
106
110
|
end
|
111
|
+
end
|
107
112
|
|
113
|
+
context 'inherited auto completer' do
|
114
|
+
it "should complete the field name" do
|
115
|
+
Infoo.complete_for('str').should =~ ([' string '])
|
116
|
+
end
|
108
117
|
end
|
109
118
|
|
110
119
|
context 'using an aliased field' do
|
@@ -14,6 +14,26 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
14
14
|
ScopedSearch::RSpec::Database.close_connection
|
15
15
|
end
|
16
16
|
|
17
|
+
context 'querying a subclass' do
|
18
|
+
before do
|
19
|
+
ActiveRecord::Migration.create_table(:supers) { |t| t.string :name }
|
20
|
+
class Super < ActiveRecord::Base
|
21
|
+
scoped_search :on => :name
|
22
|
+
end
|
23
|
+
class Sub < Super; end
|
24
|
+
|
25
|
+
@super_record = Super.create!(:name => 'test')
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
ScopedSearch::RSpec::Database.drop_model(Super)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should find records when searching the subclass" do
|
33
|
+
Sub.search_for('test').should have(1).item
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
17
37
|
context 'querying a :belongs_to relation' do
|
18
38
|
|
19
39
|
before do
|
@@ -9,15 +9,21 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
9
9
|
before(:all) do
|
10
10
|
ScopedSearch::RSpec::Database.establish_named_connection(db)
|
11
11
|
|
12
|
-
@class = ScopedSearch::RSpec::Database.create_model(
|
12
|
+
@class = ScopedSearch::RSpec::Database.create_model(
|
13
|
+
:string => :string,
|
14
|
+
:another => :string,
|
15
|
+
:explicit => :string,
|
16
|
+
:description => :string
|
17
|
+
) do |klass|
|
13
18
|
klass.scoped_search :on => :string
|
14
19
|
klass.scoped_search :on => :another, :default_operator => :eq, :alias => :alias, :default_order => :desc
|
15
20
|
klass.scoped_search :on => :explicit, :only_explicit => true
|
21
|
+
klass.scoped_search :on => :description
|
16
22
|
end
|
17
23
|
|
18
|
-
@class.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz')
|
19
|
-
@class.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz')
|
20
|
-
@class.create!(:string => 'baz', :another => nil, :explicit => nil)
|
24
|
+
@class.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz', :description => '1 - one')
|
25
|
+
@class.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz', :description => '2 - two')
|
26
|
+
@class.create!(:string => 'baz', :another => nil, :explicit => nil, :description => '3 - three')
|
21
27
|
end
|
22
28
|
|
23
29
|
after(:all) do
|
@@ -202,7 +208,15 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
202
208
|
@class.search_for('',:order => 'string DESC').first.string.should eql('foo')
|
203
209
|
end
|
204
210
|
|
205
|
-
|
211
|
+
it "sort by description ASC" do
|
212
|
+
@class.search_for('',:order => 'description ASC').first.description.should eql('1 - one')
|
213
|
+
end
|
214
|
+
|
215
|
+
it "sort by description DESC" do
|
216
|
+
@class.search_for('',:order => 'description DESC').first.description.should eql('3 - three')
|
217
|
+
end
|
218
|
+
|
219
|
+
it "default order by another DESC" do
|
206
220
|
@class.search_for('').first.string.should eql('bar')
|
207
221
|
end
|
208
222
|
|
@@ -212,6 +226,10 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
212
226
|
|
213
227
|
Set.new(distinct_search.map(&:explicit)).should == Set['baz', nil]
|
214
228
|
end
|
229
|
+
|
230
|
+
it 'should order using symbol' do
|
231
|
+
@class.search_for('',:order => :string).first.string.should eql('bar')
|
232
|
+
end
|
215
233
|
end
|
216
234
|
end
|
217
235
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "scoped_search/rails_helper"
|
3
|
+
|
4
|
+
module ActionViewHelperStubs
|
5
|
+
def html_escape(str)
|
6
|
+
str
|
7
|
+
end
|
8
|
+
|
9
|
+
def tag_options(options)
|
10
|
+
""
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ScopedSearch::RailsHelper do
|
15
|
+
include ScopedSearch::RailsHelper
|
16
|
+
include ActionViewHelperStubs
|
17
|
+
|
18
|
+
let(:params) { HashWithIndifferentAccess.new(:controller => "resources", :action => "search") }
|
19
|
+
|
20
|
+
it "should generate a link with the order param set" do
|
21
|
+
should_receive(:url_for).with(
|
22
|
+
"controller" => "resources",
|
23
|
+
"action" => "search",
|
24
|
+
"order" => "field ASC"
|
25
|
+
).and_return("/example")
|
26
|
+
|
27
|
+
sort("field")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should generate a link with order param set to alternative default sorting order" do
|
31
|
+
should_receive(:url_for).with(
|
32
|
+
"controller" => "resources",
|
33
|
+
"action" => "search",
|
34
|
+
"order" => "field DESC"
|
35
|
+
).and_return("/example")
|
36
|
+
|
37
|
+
sort("field", :default => "DESC")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should generate a link with the order param inverted" do
|
41
|
+
should_receive(:url_for).with(
|
42
|
+
"controller" => "resources",
|
43
|
+
"action" => "search",
|
44
|
+
"order" => "field DESC"
|
45
|
+
).and_return("/example")
|
46
|
+
|
47
|
+
params[:order] = "field ASC"
|
48
|
+
sort("field")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should generate a link with other parameters retained" do
|
52
|
+
should_receive(:url_for).with(
|
53
|
+
"controller" => "resources",
|
54
|
+
"action" => "search",
|
55
|
+
"walrus" => "unicorns",
|
56
|
+
"order" => "field ASC"
|
57
|
+
).and_return("/example")
|
58
|
+
|
59
|
+
params[:walrus] = "unicorns"
|
60
|
+
sort("field")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should replace the current sorting order" do
|
64
|
+
should_receive(:url_for).with(
|
65
|
+
"controller" => "resources",
|
66
|
+
"action" => "search",
|
67
|
+
"order" => "other ASC"
|
68
|
+
).and_return("/example")
|
69
|
+
|
70
|
+
params[:order] = "field ASC"
|
71
|
+
sort("other")
|
72
|
+
end
|
73
|
+
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: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amos Benari
|
@@ -10,48 +10,48 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-03-
|
13
|
+
date: 2014-03-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 2.1.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: 2.1.0
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: rspec
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- - ~>
|
33
|
+
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '2.0'
|
36
36
|
type: :development
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- - ~>
|
40
|
+
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '2.0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: rake
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '0'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
57
|
description: " Scoped search makes it easy to search your ActiveRecord-based models.\n
|
@@ -74,8 +74,8 @@ extensions: []
|
|
74
74
|
extra_rdoc_files:
|
75
75
|
- README.rdoc
|
76
76
|
files:
|
77
|
-
- .gitignore
|
78
|
-
- .travis.yml
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
79
|
- Gemfile
|
80
80
|
- Gemfile.activerecord2
|
81
81
|
- Gemfile.activerecord3
|
@@ -120,33 +120,34 @@ files:
|
|
120
120
|
- spec/unit/definition_spec.rb
|
121
121
|
- spec/unit/parser_spec.rb
|
122
122
|
- spec/unit/query_builder_spec.rb
|
123
|
+
- spec/unit/rails_helper_spec.rb
|
123
124
|
- spec/unit/tokenizer_spec.rb
|
124
125
|
homepage: https://github.com/wvanbergen/scoped_search/wiki
|
125
126
|
licenses: []
|
126
127
|
metadata: {}
|
127
128
|
post_install_message:
|
128
129
|
rdoc_options:
|
129
|
-
- --title
|
130
|
+
- "--title"
|
130
131
|
- scoped_search
|
131
|
-
- --main
|
132
|
+
- "--main"
|
132
133
|
- README.rdoc
|
133
|
-
- --line-numbers
|
134
|
-
- --inline-source
|
134
|
+
- "--line-numbers"
|
135
|
+
- "--inline-source"
|
135
136
|
require_paths:
|
136
137
|
- lib
|
137
138
|
required_ruby_version: !ruby/object:Gem::Requirement
|
138
139
|
requirements:
|
139
|
-
- -
|
140
|
+
- - ">="
|
140
141
|
- !ruby/object:Gem::Version
|
141
142
|
version: '0'
|
142
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
144
|
requirements:
|
144
|
-
- -
|
145
|
+
- - ">="
|
145
146
|
- !ruby/object:Gem::Version
|
146
147
|
version: '0'
|
147
148
|
requirements: []
|
148
149
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.
|
150
|
+
rubygems_version: 2.2.2
|
150
151
|
signing_key:
|
151
152
|
specification_version: 4
|
152
153
|
summary: Easily search you ActiveRecord models with a simple query language using
|
@@ -171,4 +172,5 @@ test_files:
|
|
171
172
|
- spec/unit/definition_spec.rb
|
172
173
|
- spec/unit/parser_spec.rb
|
173
174
|
- spec/unit/query_builder_spec.rb
|
175
|
+
- spec/unit/rails_helper_spec.rb
|
174
176
|
- spec/unit/tokenizer_spec.rb
|