attr_searchable 0.0.1
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.
- 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
|
+
|