attr_searchable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.travis.yml +34 -0
- data/Appraisals +14 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +339 -0
- data/Rakefile +9 -0
- data/attr_searchable.gemspec +29 -0
- data/gemfiles/3.2.gemfile +26 -0
- data/gemfiles/4.0.gemfile +26 -0
- data/gemfiles/4.1.gemfile +26 -0
- data/lib/attr_searchable/arel/visitors.rb +159 -0
- data/lib/attr_searchable/arel.rb +4 -0
- data/lib/attr_searchable/hash_parser.rb +40 -0
- data/lib/attr_searchable/version.rb +3 -0
- data/lib/attr_searchable.rb +76 -0
- data/lib/attr_searchable_grammar/attributes.rb +196 -0
- data/lib/attr_searchable_grammar/nodes.rb +167 -0
- data/lib/attr_searchable_grammar.rb +136 -0
- data/lib/attr_searchable_grammar.treetop +55 -0
- data/test/and_test.rb +27 -0
- data/test/attr_searchable_test.rb +44 -0
- data/test/boolean_test.rb +47 -0
- data/test/database.yml +17 -0
- data/test/datetime_test.rb +70 -0
- data/test/float_test.rb +61 -0
- data/test/fulltext_test.rb +27 -0
- data/test/integer_test.rb +61 -0
- data/test/not_test.rb +27 -0
- data/test/or_test.rb +29 -0
- data/test/string_test.rb +84 -0
- data/test/sub_query_test.rb +17 -0
- data/test/test_helper.rb +97 -0
- metadata +204 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
|
2
|
+
require "attr_searchable_grammar/attributes"
|
3
|
+
require "attr_searchable_grammar/nodes"
|
4
|
+
|
5
|
+
module AttrSearchableGrammar
|
6
|
+
class BaseNode < Treetop::Runtime::SyntaxNode
|
7
|
+
attr_writer :model
|
8
|
+
|
9
|
+
def model
|
10
|
+
@model || parent.model
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_arel
|
14
|
+
elements.collect(&:to_arel).inject(:and)
|
15
|
+
end
|
16
|
+
|
17
|
+
def elements
|
18
|
+
super.select { |element| element.class != Treetop::Runtime::SyntaxNode }
|
19
|
+
end
|
20
|
+
|
21
|
+
def arel_collection_for(key)
|
22
|
+
raise AttrSearchable::UnknownColumn, "Unknown key: #{key}" if model.searchable_attributes[key].nil?
|
23
|
+
|
24
|
+
Attributes::Collection.new model, key
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class OperatorNode < Treetop::Runtime::SyntaxNode
|
29
|
+
def to_arel
|
30
|
+
text_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ComplexExpression < BaseNode; end
|
35
|
+
class ParenthesesExpression < BaseNode; end
|
36
|
+
|
37
|
+
class ComparativeExpression < BaseNode
|
38
|
+
def to_arel
|
39
|
+
elements[0].arel_collection.send elements[1].to_arel_method, elements[2].text_value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class IncludesOperator < OperatorNode
|
44
|
+
def to_arel_method
|
45
|
+
:matches
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class EqualOperator < OperatorNode
|
50
|
+
def to_arel_method
|
51
|
+
:eq
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class UnequalOperator < OperatorNode
|
56
|
+
def to_arel_method
|
57
|
+
:not_eq
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class GreaterEqualOperator < OperatorNode
|
62
|
+
def to_arel_method
|
63
|
+
:gteq
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class GreaterOperator < OperatorNode
|
68
|
+
def to_arel_method
|
69
|
+
:gt
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class LessEqualOperator < OperatorNode
|
74
|
+
def to_arel_method
|
75
|
+
:lteq
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class LessOperator < OperatorNode
|
80
|
+
def to_arel_method
|
81
|
+
:lt
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class AnywhereExpression < BaseNode
|
86
|
+
def to_arel
|
87
|
+
keys = model.searchable_attribute_options.select { |key, value| value[:default] == true }.keys
|
88
|
+
keys = model.searchable_attributes.keys if keys.empty?
|
89
|
+
|
90
|
+
queries = keys.collect { |key| arel_collection_for key }.select { |attribute| attribute.compatible? text_value }.collect { |attribute| attribute.matches text_value }
|
91
|
+
|
92
|
+
raise AttrSearchable::NoSearchableAttributes unless model.searchable_attributes
|
93
|
+
|
94
|
+
queries.flatten.inject(:or)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class AndExpression < BaseNode
|
99
|
+
def to_arel
|
100
|
+
[elements.first.to_arel, elements.last.to_arel].inject(:and)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class OrExpression < BaseNode
|
105
|
+
def to_arel
|
106
|
+
[elements.first.to_arel, elements.last.to_arel].inject(:or)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class NotExpression < BaseNode
|
111
|
+
def to_arel
|
112
|
+
elements.first.to_arel.not
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Column < BaseNode
|
117
|
+
def arel_collection
|
118
|
+
arel_collection_for text_value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class SingleQuotedValue < BaseNode
|
123
|
+
def text_value
|
124
|
+
super.gsub /^'|'$/, ""
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class DoubleQuotedValue < BaseNode
|
129
|
+
def text_value
|
130
|
+
super.gsub /^"|"$/, ""
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Value < BaseNode; end
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
grammar AttrSearchableGrammar
|
3
|
+
rule complex_expression
|
4
|
+
space? (boolean_expression / expression) space? <ComplexExpression>
|
5
|
+
end
|
6
|
+
|
7
|
+
rule boolean_expression
|
8
|
+
and_expression
|
9
|
+
end
|
10
|
+
|
11
|
+
rule and_expression
|
12
|
+
or_expression (space? ('AND' / 'and') space? / space !('OR' / 'or')) complex_expression <AndExpression> / or_expression
|
13
|
+
end
|
14
|
+
|
15
|
+
rule or_expression
|
16
|
+
expression space? ('OR' / 'or') space? (or_expression / expression) <OrExpression> / expression
|
17
|
+
end
|
18
|
+
|
19
|
+
rule expression
|
20
|
+
parentheses_expression / not_expression / comparative_expression / anywhere_expression
|
21
|
+
end
|
22
|
+
|
23
|
+
rule parentheses_expression
|
24
|
+
'(' complex_expression ')' <ParenthesesExpression>
|
25
|
+
end
|
26
|
+
|
27
|
+
rule not_expression
|
28
|
+
('NOT' space / 'not' space / '-') (comparative_expression / anywhere_expression) <NotExpression>
|
29
|
+
end
|
30
|
+
|
31
|
+
rule comparative_expression
|
32
|
+
simple_column space? comparison_operator space? value <ComparativeExpression>
|
33
|
+
end
|
34
|
+
|
35
|
+
rule comparison_operator
|
36
|
+
':' <IncludesOperator> / '=' <EqualOperator> / '!=' <UnequalOperator> / '>=' <GreaterEqualOperator> / '>' <GreaterOperator> / '<=' <LessEqualOperator> / '<' <LessOperator>
|
37
|
+
end
|
38
|
+
|
39
|
+
rule anywhere_expression
|
40
|
+
("'" ([^\']* <AnywhereExpression>) "'") / ('"' ([^\"]* <AnywhereExpression>) '"') / [^\s()]+ <AnywhereExpression>
|
41
|
+
end
|
42
|
+
|
43
|
+
rule simple_column
|
44
|
+
[a-zA-Z0-9_.]+ <Column>
|
45
|
+
end
|
46
|
+
|
47
|
+
rule value
|
48
|
+
"'" [^\']* "'" <SingleQuotedValue> / '"' [^\"]* '"' <DoubleQuotedValue> / [^\s()]+ <Value>
|
49
|
+
end
|
50
|
+
|
51
|
+
rule space
|
52
|
+
[\s]+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
data/test/and_test.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class AndTest < AttrSearchable::TestCase
|
5
|
+
def test_and_string
|
6
|
+
expected = FactoryGirl.create(:product, :title => "Expected title", :description => "Description")
|
7
|
+
rejected = FactoryGirl.create(:product, :title => "Rejected title", :description => "Description")
|
8
|
+
|
9
|
+
results = Product.search("title: 'Expected title' description: Description")
|
10
|
+
|
11
|
+
assert_includes results, expected
|
12
|
+
refute_includes results, rejected
|
13
|
+
|
14
|
+
assert_equal results, Product.search("title: 'Expected title' AND description: Description")
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_and_hash
|
18
|
+
expected = FactoryGirl.create(:product, :title => "Expected title", :description => "Description")
|
19
|
+
rejected = FactoryGirl.create(:product, :title => "Rejected title", :description => "Description")
|
20
|
+
|
21
|
+
results = Product.search(:and => [{:title => "Expected title"}, {:description => "Description"}])
|
22
|
+
|
23
|
+
assert_includes results, expected
|
24
|
+
refute_includes results, rejected
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class AttrSearchableTest < AttrSearchable::TestCase
|
5
|
+
def test_associations
|
6
|
+
product = FactoryGirl.create(:product, :comments => [
|
7
|
+
FactoryGirl.create(:comment, :title => "Title1", :message => "Message1"),
|
8
|
+
FactoryGirl.create(:comment, :title => "Title2", :message => "Message2")
|
9
|
+
])
|
10
|
+
|
11
|
+
assert_includes Product.search("comment: Title1 comment: Message1"), product
|
12
|
+
assert_includes Product.search("comment: Title2 comment: Message2"), product
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_multiple
|
16
|
+
product = FactoryGirl.create(:product, :comments => [FactoryGirl.create(:comment, :title => "Title", :message => "Message")])
|
17
|
+
|
18
|
+
assert_includes Product.search("comment: Title"), product
|
19
|
+
assert_includes Product.search("comment: Message"), product
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_default
|
23
|
+
product1 = FactoryGirl.create(:product, :title => "Expected")
|
24
|
+
product2 = FactoryGirl.create(:product, :description => "Expected")
|
25
|
+
|
26
|
+
results = Product.search("Expected")
|
27
|
+
|
28
|
+
assert_includes results, product1
|
29
|
+
assert_includes results, product2
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_custom_default
|
33
|
+
product1 = FactoryGirl.create(:product, :title => "Expected")
|
34
|
+
product2 = FactoryGirl.create(:product, :description => "Expected")
|
35
|
+
product3 = FactoryGirl.create(:product, :brand => "Expected")
|
36
|
+
|
37
|
+
results = with_attr_searchable_options(Product, :primary, :default => true) { Product.search "Expected" }
|
38
|
+
|
39
|
+
assert_includes results, product1
|
40
|
+
assert_includes results, product2
|
41
|
+
refute_includes results, product3
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class BooleanTest < AttrSearchable::TestCase
|
5
|
+
def test_mapping
|
6
|
+
product = FactoryGirl.create(:product, :available => true)
|
7
|
+
|
8
|
+
assert_includes Product.search("available: 1"), product
|
9
|
+
assert_includes Product.search("available: true"), product
|
10
|
+
assert_includes Product.search("available: yes"), product
|
11
|
+
|
12
|
+
product = FactoryGirl.create(:product, :available => false)
|
13
|
+
|
14
|
+
assert_includes Product.search("available: 0"), product
|
15
|
+
assert_includes Product.search("available: false"), product
|
16
|
+
assert_includes Product.search("available: no"), product
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_anywhere
|
20
|
+
product = FactoryGirl.create(:product, :available => true)
|
21
|
+
|
22
|
+
assert_includes Product.search("true"), product
|
23
|
+
refute_includes Product.search("false"), product
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_includes
|
27
|
+
product = FactoryGirl.create(:product, :available => true)
|
28
|
+
|
29
|
+
assert_includes Product.search("available: true"), product
|
30
|
+
refute_includes Product.search("available: false"), product
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_equals
|
34
|
+
product = FactoryGirl.create(:product, :available => true)
|
35
|
+
|
36
|
+
assert_includes Product.search("available = true"), product
|
37
|
+
refute_includes Product.search("available = false"), product
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_equals_not
|
41
|
+
product = FactoryGirl.create(:product, :available => false)
|
42
|
+
|
43
|
+
assert_includes Product.search("available != true"), product
|
44
|
+
refute_includes Product.search("available != false"), product
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/test/database.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
sqlite:
|
3
|
+
adapter: sqlite3
|
4
|
+
database: ":memory:"
|
5
|
+
|
6
|
+
mysql:
|
7
|
+
adapter: mysql2
|
8
|
+
database: attr_searchable
|
9
|
+
username: root
|
10
|
+
encoding: utf8
|
11
|
+
|
12
|
+
postgres:
|
13
|
+
adapter: postgresql
|
14
|
+
database: attr_searchable
|
15
|
+
username: postgres
|
16
|
+
encoding: utf8
|
17
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class DatetimeTest < AttrSearchable::TestCase
|
5
|
+
def test_mapping
|
6
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01 12:30:30"))
|
7
|
+
|
8
|
+
assert_includes Product.search("created_at: 2014"), product
|
9
|
+
assert_includes Product.search("created_at: 2014-05"), product
|
10
|
+
assert_includes Product.search("created_at: 2014-05-01"), product
|
11
|
+
assert_includes Product.search("created_at: '2014-05-01 12:30:30'"), product
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_anywhere
|
15
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
16
|
+
|
17
|
+
assert_includes Product.search("2014-05-01"), product
|
18
|
+
refute_includes Product.search("2014-05-02"), product
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_includes
|
22
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
23
|
+
|
24
|
+
assert_includes Product.search("created_at: 2014-05-01"), product
|
25
|
+
refute_includes Product.search("created_at: 2014-05-02"), product
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_equals
|
29
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
30
|
+
|
31
|
+
assert_includes Product.search("created_at = 2014-05-01"), product
|
32
|
+
refute_includes Product.search("created_at = 2014-05-02"), product
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_equals_not
|
36
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
37
|
+
|
38
|
+
assert_includes Product.search("created_at != 2014-05-02"), product
|
39
|
+
refute_includes Product.search("created_at != 2014-05-01"), product
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_greater
|
43
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
44
|
+
|
45
|
+
assert_includes Product.search("created_at < 2014-05-02"), product
|
46
|
+
refute_includes Product.search("created_at < 2014-05-01"), product
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_greater_equals
|
50
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
51
|
+
|
52
|
+
assert_includes Product.search("created_at >= 2014-05-01"), product
|
53
|
+
refute_includes Product.search("created_at >= 2014-05-02"), product
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_less
|
57
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
|
58
|
+
|
59
|
+
assert_includes Product.search("created_at < 2014-05-02"), product
|
60
|
+
refute_includes Product.search("created_at < 2014-05-01"), product
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_less_equals
|
64
|
+
product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-02"))
|
65
|
+
|
66
|
+
assert_includes Product.search("created_at <= 2014-05-02"), product
|
67
|
+
refute_includes Product.search("created_at <= 2014-05-01"), product
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
data/test/float_test.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class FloatTest < AttrSearchable::TestCase
|
5
|
+
def test_anywhere
|
6
|
+
product = FactoryGirl.create(:product, :price => 10.5, :created_at => Time.now - 1.day)
|
7
|
+
|
8
|
+
assert_includes Product.search("10.5"), product
|
9
|
+
refute_includes Product.search("11.5"), product
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_includes
|
13
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
14
|
+
|
15
|
+
assert_includes Product.search("price: 10.5"), product
|
16
|
+
refute_includes Product.search("price: 11.5"), product
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_equals
|
20
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
21
|
+
|
22
|
+
assert_includes Product.search("price = 10.5"), product
|
23
|
+
refute_includes Product.search("price = 11.5"), product
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_equals_not
|
27
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
28
|
+
|
29
|
+
assert_includes Product.search("price != 11.5"), product
|
30
|
+
refute_includes Product.search("price != 10.5"), product
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_greater
|
34
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
35
|
+
|
36
|
+
assert_includes Product.search("price > 10.4"), product
|
37
|
+
refute_includes Product.search("price < 10.5"), product
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_greater_equals
|
41
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
42
|
+
|
43
|
+
assert_includes Product.search("price >= 10.5"), product
|
44
|
+
refute_includes Product.search("price >= 10.6"), product
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_less
|
48
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
49
|
+
|
50
|
+
assert_includes Product.search("price < 10.6"), product
|
51
|
+
refute_includes Product.search("price < 10.5"), product
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_less_equals
|
55
|
+
product = FactoryGirl.create(:product, :price => 10.5)
|
56
|
+
|
57
|
+
assert_includes Product.search("price <= 10.5"), product
|
58
|
+
refute_includes Product.search("price <= 10.4"), product
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class FulltextTest < AttrSearchable::TestCase
|
5
|
+
def test_complex
|
6
|
+
product1 = FactoryGirl.create(:product, :title => "word1")
|
7
|
+
product2 = FactoryGirl.create(:product, :title => "word2 word3")
|
8
|
+
product3 = FactoryGirl.create(:product, :title => "word2")
|
9
|
+
|
10
|
+
results = Product.search("title:word1 OR (title:word2 -title:word3)")
|
11
|
+
|
12
|
+
assert_includes results, product1
|
13
|
+
refute_includes results, product2
|
14
|
+
assert_includes results, product3
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_mixed
|
18
|
+
expected = FactoryGirl.create(:product, :title => "Expected title", :stock => 1)
|
19
|
+
rejected = FactoryGirl.create(:product, :title => "Expected title", :stock => 0)
|
20
|
+
|
21
|
+
results = Product.search("title:Expected title:Title stock > 0")
|
22
|
+
|
23
|
+
assert_includes results, expected
|
24
|
+
refute_includes results, rejected
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class IntegerTest < AttrSearchable::TestCase
|
5
|
+
def test_anywhere
|
6
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
7
|
+
|
8
|
+
assert_includes Product.search("1"), product
|
9
|
+
refute_includes Product.search("0"), product
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_includes
|
13
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
14
|
+
|
15
|
+
assert_includes Product.search("stock: 1"), product
|
16
|
+
refute_includes Product.search("stock: 10"), product
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_equals
|
20
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
21
|
+
|
22
|
+
assert_includes Product.search("stock = 1"), product
|
23
|
+
refute_includes Product.search("stock = 0"), product
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_equals_not
|
27
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
28
|
+
|
29
|
+
assert_includes Product.search("stock != 0"), product
|
30
|
+
refute_includes Product.search("stock != 1"), product
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_greater
|
34
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
35
|
+
|
36
|
+
assert_includes Product.search("stock > 0"), product
|
37
|
+
refute_includes Product.search("stock < 1"), product
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_greater_equals
|
41
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
42
|
+
|
43
|
+
assert_includes Product.search("stock >= 1"), product
|
44
|
+
refute_includes Product.search("stock >= 2"), product
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_less
|
48
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
49
|
+
|
50
|
+
assert_includes Product.search("stock < 2"), product
|
51
|
+
refute_includes Product.search("stock < 1"), product
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_less_equals
|
55
|
+
product = FactoryGirl.create(:product, :stock => 1)
|
56
|
+
|
57
|
+
assert_includes Product.search("stock <= 1"), product
|
58
|
+
refute_includes Product.search("stock <= 0"), product
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/test/not_test.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class NotTest < AttrSearchable::TestCase
|
5
|
+
def test_not_string
|
6
|
+
expected = FactoryGirl.create(:product, :title => "Expected title")
|
7
|
+
rejected = FactoryGirl.create(:product, :title => "Rejected title")
|
8
|
+
|
9
|
+
results = Product.search("title: Title NOT title: Rejected")
|
10
|
+
|
11
|
+
assert_includes results, expected
|
12
|
+
refute_includes results, rejected
|
13
|
+
|
14
|
+
assert_equal results, Product.search("title: Title -title: Rejected")
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_not_hash
|
18
|
+
expected = FactoryGirl.create(:product, :title => "Expected title")
|
19
|
+
rejected = FactoryGirl.create(:product, :title => "Rejected title")
|
20
|
+
|
21
|
+
results = Product.search(:and => [{:title => "Title"}, {:not => {:title => "Rejected"}}])
|
22
|
+
|
23
|
+
assert_includes results, expected
|
24
|
+
refute_includes results, rejected
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/test/or_test.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class OrTest < AttrSearchable::TestCase
|
5
|
+
def test_or_string
|
6
|
+
product1 = FactoryGirl.create(:product, :title => "Title1")
|
7
|
+
product2 = FactoryGirl.create(:product, :title => "Title2")
|
8
|
+
product3 = FactoryGirl.create(:product, :title => "Title3")
|
9
|
+
|
10
|
+
results = Product.search("title: Title1 OR title: Title2")
|
11
|
+
|
12
|
+
assert_includes results, product1
|
13
|
+
assert_includes results, product2
|
14
|
+
refute_includes results, product3
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_or_hash
|
18
|
+
product1 = FactoryGirl.create(:product, :title => "Title1")
|
19
|
+
product2 = FactoryGirl.create(:product, :title => "Title2")
|
20
|
+
product3 = FactoryGirl.create(:product, :title => "Title3")
|
21
|
+
|
22
|
+
results = Product.search(:or => [{:title => "Title1"}, {:title => "Title2"}])
|
23
|
+
|
24
|
+
assert_includes results, product1
|
25
|
+
assert_includes results, product2
|
26
|
+
refute_includes results, product3
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/test/string_test.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class StringTest < AttrSearchable::TestCase
|
5
|
+
def test_anywhere
|
6
|
+
product = FactoryGirl.create(:product, :title => "Expected title")
|
7
|
+
|
8
|
+
assert_includes Product.search("Expected"), product
|
9
|
+
refute_includes Product.search("Rejected"), product
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_multiple
|
13
|
+
product = FactoryGirl.create(:product, :comments => [FactoryGirl.create(:comment, :title => "Expected title", :message => "Expected message")])
|
14
|
+
|
15
|
+
assert_includes Product.search("Expected"), product
|
16
|
+
refute_includes Product.search("Rejected"), product
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_includes
|
20
|
+
product = FactoryGirl.create(:product, :title => "Expected")
|
21
|
+
|
22
|
+
assert_includes Product.search("title: Expected"), product
|
23
|
+
refute_includes Product.search("title: Rejected"), product
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_includes_with_left_wildcard
|
27
|
+
product = FactoryGirl.create(:product, :title => "Some title")
|
28
|
+
|
29
|
+
assert_includes Product.search("title: Title"), product
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_includes_without_left_wildcard
|
33
|
+
expected = FactoryGirl.create(:product, :brand => "Brand")
|
34
|
+
rejected = FactoryGirl.create(:product, :brand => "Rejected brand")
|
35
|
+
|
36
|
+
results = with_attr_searchable_options(Product, :brand, :left_wildcard => false) { Product.search "brand: Brand" }
|
37
|
+
|
38
|
+
assert_includes results, expected
|
39
|
+
refute_includes results, rejected
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_equals
|
43
|
+
product = FactoryGirl.create(:product, :title => "Expected title")
|
44
|
+
|
45
|
+
assert_includes Product.search("title = 'Expected title'"), product
|
46
|
+
refute_includes Product.search("title = Expected"), product
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_equals_not
|
50
|
+
product = FactoryGirl.create(:product, :title => "Expected")
|
51
|
+
|
52
|
+
assert_includes Product.search("title != Rejected"), product
|
53
|
+
refute_includes Product.search("title != Expected"), product
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_greater
|
57
|
+
product = FactoryGirl.create(:product, :title => "Title B")
|
58
|
+
|
59
|
+
assert_includes Product.search("title > 'Title A'"), product
|
60
|
+
refute_includes Product.search("title > 'Title B'"), product
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_greater_equals
|
64
|
+
product = FactoryGirl.create(:product, :title => "Title A")
|
65
|
+
|
66
|
+
assert_includes Product.search("title >= 'Title A'"), product
|
67
|
+
refute_includes Product.search("title >= 'Title B'"), product
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_less
|
71
|
+
product = FactoryGirl.create(:product, :title => "Title A")
|
72
|
+
|
73
|
+
assert_includes Product.search("title < 'Title B'"), product
|
74
|
+
refute_includes Product.search("title < 'Title A'"), product
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_less_or_greater
|
78
|
+
product = FactoryGirl.create(:product, :title => "Title B")
|
79
|
+
|
80
|
+
assert_includes Product.search("title <= 'Title B'"), product
|
81
|
+
refute_includes Product.search("title <= 'Title A'"), product
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|