attr_searchable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,7 +20,6 @@ matrix:
20
20
  allow_failures:
21
21
  - rvm: rbx-2
22
22
  - rvm: jruby
23
- fast_finish: true
24
23
 
25
24
  gemfile:
26
25
  - gemfiles/3.2.gemfile
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Build Status](https://secure.travis-ci.org/mrkamel/attr_searchable.png?branch=master)](http://travis-ci.org/mrkamel/attr_searchable)
4
4
  [![Code Climate](https://codeclimate.com/github/mrkamel/attr_searchable.png)](https://codeclimate.com/github/mrkamel/attr_searchable)
5
5
  [![Dependency Status](https://gemnasium.com/mrkamel/attr_searchable.png?travis)](https://gemnasium.com/mrkamel/attr_searchable)
6
+ [![Gem Version](https://badge.fury.io/rb/attr_searchable.svg)](http://badge.fury.io/rb/attr_searchable)
6
7
 
7
8
  AttrSearchable extends your ActiveRecord models to support fulltext search
8
9
  engine like queries via simple query strings and hash-based queries. Assume you
@@ -35,7 +36,7 @@ Book.search(:or => [{:query => "Rowling -Potter"}, {:query => "Tolkien -Rings"}]
35
36
 
36
37
  ## Installation
37
38
 
38
- Add this line to your application's Gemfile:
39
+ For Rails/ActiveRecord 3 (or 4), add this line to your application's Gemfile:
39
40
 
40
41
  gem 'attr_searchable'
41
42
 
@@ -142,8 +143,8 @@ attributes having fulltext indices to:
142
143
 
143
144
  ```ruby
144
145
  Book.search("Harry Potter")
145
- # MySQL: ... WHERE MATCH(books.title) AGAINST('+Harry +Potter' IN BOOLEAN MODE) OR MATCH(books.author) AGAINST('+Harry +Potter' IN BOOLEAN MODE)
146
- # PostgreSQL: ... WHERE to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Harry & Potter') OR to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Harry & Potter')
146
+ # MySQL: ... WHERE (MATCH(books.title) AGAINST('+Harry' IN BOOLEAN MODE) OR MATCH(books.author) AGAINST('+Harry' IN BOOLEAN MODE)) AND (MATCH(books.title) AGAINST ('+Potter' IN BOOLEAN MODE) OR MATCH(books.author) AGAINST('+Potter' IN BOOLEAN MODE))
147
+ # PostgreSQL: ... WHERE (to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Harry') OR to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Harry')) AND (to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Potter') OR to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Potter'))
147
148
  ```
148
149
 
149
150
  Obviously, theses queries won't always return the same results as wildcard
@@ -165,8 +166,8 @@ Now AttrSearchable can optimize the following, not yet optimal query:
165
166
 
166
167
  ```ruby
167
168
  BookSearch("Rowling OR Tolkien stock > 1")
168
- # MySQL: ... WHERE (MATCH(books.author) AGAINST('+Rowling' IN BOOLEAN MODE) OR MATCH(books.title) AGAINST('+Tolkien' IN BOOLEAN MODE)) AND books.stock > 1
169
- # PostgreSQL: ... WHERE (to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Rowling') OR to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Tolkien')) AND books.stock > 1
169
+ # MySQL: ... WHERE ((MATCH(books.author) AGAINST('+Rowling' IN BOOLEAN MODE) OR MATCH(books.title) AGAINST('+Rowling' IN BOOLEAN MODE)) OR (MATCH(books.author) AGAINST('+Tolkien' IN BOOLEAN MODE) OR MATCH(books.title) AGAINST('+Tolkien' IN BOOLEAN MODE))) AND books.stock > 1
170
+ # PostgreSQL: ... WHERE ((to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Rowling') OR to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Rowling')) OR (to_tsvector('simple', books.author) @@ to_tsquery('simple', 'Tolkien') OR to_tsvector('simple', books.title) @@ to_tsquery('simple', 'Tolkien'))) AND books.stock > 1
170
171
  ```
171
172
 
172
173
  to the following, more performant query:
@@ -330,6 +331,16 @@ instead of
330
331
 
331
332
  Thus, if you use fulltext indices, you better avoid chaining.
332
333
 
334
+ ## Debugging
335
+
336
+ AttrSearchable conveniently hides certain errors, like parse errors, and
337
+ instead returns an empty relation. However, if you need to debug certain
338
+ cases, use `Model#unsafe_search`, which will raise them.
339
+
340
+ ```ruby
341
+ Book.unsafe_search("stock: None") # => raise AttrSearchable::IncompatibleDatatype
342
+ ```
343
+
333
344
  ## Contributing
334
345
 
335
346
  1. Fork it
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = AttrSearchable::VERSION
9
9
  spec.authors = ["Benjamin Vetter"]
10
10
  spec.email = ["vetter@flakks.com"]
11
- spec.description = %q{Complex search-engine like query support for activerecord}
12
- spec.summary = %q{Easily perform complex search-engine like queries on your activerecord models}
11
+ spec.description = %q{Search-engine like fulltext query support for ActiveRecord}
12
+ spec.summary = %q{Easily perform complex search-engine like fulltext queries on your ActiveRecord models}
13
13
  spec.homepage = "https://github.com/mrkamel/attr_searchable"
14
14
  spec.license = "MIT"
15
15
 
@@ -8,11 +8,14 @@ require "treetop"
8
8
  Treetop.load File.expand_path("../attr_searchable_grammar.treetop", __FILE__)
9
9
 
10
10
  module AttrSearchable
11
- class Error < StandardError; end
12
- class UnknownColumn < Error; end
13
- class NoSearchableAttributes < Error; end
14
- class IncompatibleDatatype < Error; end
15
- class ParseError < Error; end
11
+ class SpecificationError < StandardError; end
12
+ class UnknownAttribute < SpecificationError; end
13
+
14
+ class RuntimeError < StandardError; end
15
+ class UnknownColumn < RuntimeError; end
16
+ class NoSearchableAttributes < RuntimeError; end
17
+ class IncompatibleDatatype < RuntimeError; end
18
+ class ParseError < RuntimeError; end
16
19
 
17
20
  module Parser
18
21
  def self.parse(arg, model)
@@ -26,7 +29,7 @@ module AttrSearchable
26
29
  def self.parse_string(string, model)
27
30
  node = AttrSearchableGrammarParser.new.parse(string) || raise(ParseError)
28
31
  node.model = model
29
- node.to_arel
32
+ node.evaluate
30
33
  end
31
34
  end
32
35
 
@@ -61,15 +64,19 @@ module AttrSearchable
61
64
  self.searchable_attribute_options[key.to_s] = (self.searchable_attribute_options[key.to_s] || {}).merge(options)
62
65
  end
63
66
 
64
- def search(arg)
67
+ def search(*args)
68
+ unsafe_search *args
69
+ rescue AttrSearchable::RuntimeError
70
+ respond_to?(:none) ? none : where("1 = 0")
71
+ end
72
+
73
+ def unsafe_search(arg)
65
74
  return respond_to?(:scoped) ? scoped : all if arg.blank?
66
75
 
67
76
  scope = respond_to?(:search_scope) ? search_scope : nil
68
77
  scope ||= eager_load(searchable_attributes.values.flatten.uniq.collect { |column| column.split(".").first.to_sym } - [name.tableize.to_sym])
69
78
 
70
- scope.where AttrSearchable::Parser.parse(arg, self).optimize!
71
- rescue AttrSearchable::Error
72
- respond_to?(:none) ? none : where("1 = 0")
79
+ scope.where AttrSearchable::Parser.parse(arg, self).optimize!.to_sql(self)
73
80
  end
74
81
  end
75
82
  end
@@ -5,19 +5,83 @@ module AttrSearchable
5
5
  module ToSql
6
6
  if ::Arel::VERSION >= "4.0.1"
7
7
  def visit_AttrSearchableGrammar_Nodes_And(o, a)
8
- visit ::Arel::Nodes::Grouping.new(o.to_arel), a
8
+ visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::And.new [res, cur] }), a
9
9
  end
10
10
 
11
11
  def visit_AttrSearchableGrammar_Nodes_Or(o, a)
12
- visit ::Arel::Nodes::Grouping.new(o.to_arel), a
12
+ visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::Or.new res, cur }), a
13
+ end
14
+
15
+ def visit_AttrSearchableGrammar_Nodes_Equality(o, a)
16
+ visit ::Arel::Nodes::Equality.new(o.left, o.right), a
17
+ end
18
+
19
+ def visit_AttrSearchableGrammar_Nodes_NotEqual(o, a)
20
+ visit ::Arel::Nodes::NotEqual.new(o.left, o.right), a
21
+ end
22
+
23
+ def visit_AttrSearchableGrammar_Nodes_LessThan(o, a)
24
+ visit ::Arel::Nodes::LessThan.new(o.left, o.right), a
25
+ end
26
+
27
+ def visit_AttrSearchableGrammar_Nodes_LessThanOrEqual(o, a)
28
+ visit ::Arel::Nodes::LessThanOrEqual.new(o.left, o.right), a
29
+ end
30
+
31
+ def visit_AttrSearchableGrammar_Nodes_GreaterThan(o, a)
32
+ visit ::Arel::Nodes::GreaterThan.new(o.left, o.right), a
33
+ end
34
+
35
+ def visit_AttrSearchableGrammar_Nodes_GreaterThanOrEqual(o, a)
36
+ visit ::Arel::Nodes::GreaterThanOrEqual.new(o.left, o.right), a
37
+ end
38
+
39
+ def visit_AttrSearchableGrammar_Nodes_Not(o, a)
40
+ visit ::Arel::Nodes::Not.new(o.object), a
41
+ end
42
+
43
+ def visit_AttrSearchableGrammar_Nodes_Matches(o, a)
44
+ visit ::Arel::Nodes::Matches.new(o.left, o.right), a
13
45
  end
14
46
  else
15
47
  def visit_AttrSearchableGrammar_Nodes_And(o)
16
- visit ::Arel::Nodes::Grouping.new(o.to_arel)
48
+ visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::And.new [res, cur] })
17
49
  end
18
50
 
19
51
  def visit_AttrSearchableGrammar_Nodes_Or(o)
20
- visit ::Arel::Nodes::Grouping.new(o.to_arel)
52
+ visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::Or.new res, cur })
53
+ end
54
+
55
+ def visit_AttrSearchableGrammar_Nodes_Equality(o)
56
+ visit ::Arel::Nodes::Equality.new(o.left, o.right)
57
+ end
58
+
59
+ def visit_AttrSearchableGrammar_Nodes_NotEqual(o)
60
+ visit ::Arel::Nodes::NotEqual.new(o.left, o.right)
61
+ end
62
+
63
+ def visit_AttrSearchableGrammar_Nodes_LessThan(o)
64
+ visit ::Arel::Nodes::LessThan.new(o.left, o.right)
65
+ end
66
+
67
+ def visit_AttrSearchableGrammar_Nodes_LessThanOrEqual(o)
68
+ visit ::Arel::Nodes::LessThanOrEqual.new(o.left, o.right)
69
+ end
70
+
71
+ def visit_AttrSearchableGrammar_Nodes_GreaterThan(o)
72
+ visit ::Arel::Nodes::GreaterThan.new(o.left, o.right)
73
+ end
74
+
75
+ def visit_AttrSearchableGrammar_Nodes_GreaterThanOrEqual(o)
76
+ visit ::Arel::Nodes::GreaterThanOrEqual.new(o.left, o.right)
77
+ end
78
+
79
+ def visit_AttrSearchableGrammar_Nodes_Not(o)
80
+ visit ::Arel::Nodes::Not.new(o.object)
81
+ end
82
+
83
+ def visit_AttrSearchableGrammar_Nodes_Matches(o)
84
+ visit ::Arel::Nodes::Matches.new(o.left, o.right)
21
85
  end
22
86
  end
23
87
  end
@@ -29,7 +29,7 @@ class AttrSearchable::HashParser
29
29
  collection = AttrSearchableGrammar::Attributes::Collection.new(@model, key.to_s)
30
30
 
31
31
  if value.is_a?(Hash)
32
- raise AttrSearchable::ParseError unless [:matches, :eq, :not_eq, :gt, :gteq, :lt, :lteq].include?(value.keys.first)
32
+ raise(AttrSearchable::ParseError, "Unknown operator #{value.keys.first}") unless [:matches, :eq, :not_eq, :gt, :gteq, :lt, :lteq].include?(value.keys.first)
33
33
 
34
34
  collection.send value.keys.first, value.values.first.to_s
35
35
  else
@@ -1,3 +1,3 @@
1
1
  module AttrSearchable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -10,23 +10,23 @@ module AttrSearchableGrammar
10
10
  @model || parent.model
11
11
  end
12
12
 
13
- def to_arel
14
- elements.collect(&:to_arel).inject(:and)
13
+ def evaluate
14
+ elements.collect(&:evaluate).inject(:and)
15
15
  end
16
16
 
17
17
  def elements
18
18
  super.select { |element| element.class != Treetop::Runtime::SyntaxNode }
19
19
  end
20
20
 
21
- def arel_collection_for(key)
22
- raise AttrSearchable::UnknownColumn, "Unknown key: #{key}" if model.searchable_attributes[key].nil?
21
+ def collection_for(key)
22
+ raise(AttrSearchable::UnknownColumn, "Unknown column #{key}") if model.searchable_attributes[key].nil?
23
23
 
24
24
  Attributes::Collection.new model, key
25
25
  end
26
26
  end
27
27
 
28
28
  class OperatorNode < Treetop::Runtime::SyntaxNode
29
- def to_arel
29
+ def evaluate
30
30
  text_value
31
31
  end
32
32
  end
@@ -35,59 +35,59 @@ module AttrSearchableGrammar
35
35
  class ParenthesesExpression < BaseNode; end
36
36
 
37
37
  class ComparativeExpression < BaseNode
38
- def to_arel
39
- elements[0].arel_collection.send elements[1].to_arel_method, elements[2].text_value
38
+ def evaluate
39
+ elements[0].collection.send elements[1].method_name, elements[2].text_value
40
40
  end
41
41
  end
42
42
 
43
43
  class IncludesOperator < OperatorNode
44
- def to_arel_method
44
+ def method_name
45
45
  :matches
46
46
  end
47
47
  end
48
48
 
49
49
  class EqualOperator < OperatorNode
50
- def to_arel_method
50
+ def method_name
51
51
  :eq
52
52
  end
53
53
  end
54
54
 
55
55
  class UnequalOperator < OperatorNode
56
- def to_arel_method
56
+ def method_name
57
57
  :not_eq
58
58
  end
59
59
  end
60
60
 
61
61
  class GreaterEqualOperator < OperatorNode
62
- def to_arel_method
62
+ def method_name
63
63
  :gteq
64
64
  end
65
65
  end
66
66
 
67
67
  class GreaterOperator < OperatorNode
68
- def to_arel_method
68
+ def method_name
69
69
  :gt
70
70
  end
71
71
  end
72
72
 
73
73
  class LessEqualOperator < OperatorNode
74
- def to_arel_method
74
+ def method_name
75
75
  :lteq
76
76
  end
77
77
  end
78
78
 
79
79
  class LessOperator < OperatorNode
80
- def to_arel_method
80
+ def method_name
81
81
  :lt
82
82
  end
83
83
  end
84
84
 
85
85
  class AnywhereExpression < BaseNode
86
- def to_arel
86
+ def evaluate
87
87
  keys = model.searchable_attribute_options.select { |key, value| value[:default] == true }.keys
88
88
  keys = model.searchable_attributes.keys if keys.empty?
89
89
 
90
- queries = keys.collect { |key| arel_collection_for key }.select { |attribute| attribute.compatible? text_value }.collect { |attribute| attribute.matches text_value }
90
+ queries = keys.collect { |key| collection_for key }.select { |attribute| attribute.compatible? text_value }.collect { |attribute| attribute.matches text_value }
91
91
 
92
92
  raise AttrSearchable::NoSearchableAttributes unless model.searchable_attributes
93
93
 
@@ -96,26 +96,26 @@ module AttrSearchableGrammar
96
96
  end
97
97
 
98
98
  class AndExpression < BaseNode
99
- def to_arel
100
- [elements.first.to_arel, elements.last.to_arel].inject(:and)
99
+ def evaluate
100
+ [elements.first.evaluate, elements.last.evaluate].inject(:and)
101
101
  end
102
102
  end
103
103
 
104
104
  class OrExpression < BaseNode
105
- def to_arel
106
- [elements.first.to_arel, elements.last.to_arel].inject(:or)
105
+ def evaluate
106
+ [elements.first.evaluate, elements.last.evaluate].inject(:or)
107
107
  end
108
108
  end
109
109
 
110
110
  class NotExpression < BaseNode
111
- def to_arel
112
- elements.first.to_arel.not
111
+ def evaluate
112
+ elements.first.evaluate.not
113
113
  end
114
114
  end
115
115
 
116
116
  class Column < BaseNode
117
- def arel_collection
118
- arel_collection_for text_value
117
+ def collection
118
+ collection_for text_value
119
119
  end
120
120
  end
121
121
 
@@ -7,7 +7,7 @@ module AttrSearchableGrammar
7
7
  attr_reader :model, :key
8
8
 
9
9
  def initialize(model, key)
10
- raise AttrSearchable::IncompatibleDatatype unless model.searchable_attributes[key]
10
+ raise(AttrSearchable::UnknownColumn, "Unknown column #{key}") unless model.searchable_attributes[key]
11
11
 
12
12
  @model = model
13
13
  @key = key
@@ -52,12 +52,16 @@ module AttrSearchableGrammar
52
52
  end
53
53
 
54
54
  def attributes
55
- @attributes ||= model.searchable_attributes[key].collect do |attribute|
56
- table, column = attribute.split(".")
57
- klass = table.classify.constantize
55
+ @attributes ||= model.searchable_attributes[key].collect { |attribute_definition| attribute_for attribute_definition }
56
+ end
58
57
 
59
- Attributes.const_get(klass.columns_hash[column].type.to_s.classify).new(klass.arel_table.alias(klass.table_name)[column], klass, options)
60
- end
58
+ def attribute_for(attribute_definition)
59
+ table, column = attribute_definition.split(".")
60
+ klass = table.classify.constantize
61
+
62
+ raise(AttrSearchable::UnknownAttribute, "Unknown attribute #{attribute_definition}") unless klass.columns_hash[column]
63
+
64
+ Attributes.const_get(klass.columns_hash[column].type.to_s.classify).new(klass.arel_table.alias(klass.table_name)[column], klass, options)
61
65
  end
62
66
  end
63
67
 
@@ -88,7 +92,7 @@ module AttrSearchableGrammar
88
92
 
89
93
  { :eq => "Equality", :not_eq => "NotEqual", :lt => "LessThan", :lteq => "LessThanOrEqual", :gt => "GreaterThan", :gteq => "GreaterThanOrEqual", :matches => "Matches" }.each do |method, class_name|
90
94
  define_method method do |value|
91
- raise AttrSearchable::IncompatibleDatatype unless compatible?(value)
95
+ raise(AttrSearchable::IncompatibleDatatype, "Incompatible datatype for #{value}") unless compatible?(value)
92
96
 
93
97
  AttrSearchableGrammar::Nodes.const_get(class_name).new(@attribute, map(value))
94
98
  end
@@ -152,7 +156,7 @@ module AttrSearchableGrammar
152
156
  time .. time
153
157
  end
154
158
  rescue ArgumentError
155
- raise AttrSearchable::IncompatibleDatatype
159
+ raise AttrSearchable::IncompatibleDatatype, "Incompatible datatype for #{value}"
156
160
  end
157
161
 
158
162
  def map(value)
@@ -188,7 +192,7 @@ module AttrSearchableGrammar
188
192
  return true if value.to_s =~ /^(1|true|yes)$/i
189
193
  return false if value.to_s =~ /^(0|false|no)$/i
190
194
 
191
- raise AttrSearchable::IncompatibleDatatype
195
+ raise AttrSearchable::IncompatibleDatatype, "Incompatible datatype for #{value}"
192
196
  end
193
197
  end
194
198
  end
@@ -46,6 +46,10 @@ module AttrSearchableGrammar
46
46
  finalize!
47
47
  end
48
48
 
49
+ def to_sql(model)
50
+ model.connection.visitor.accept self
51
+ end
52
+
49
53
  def finalize!
50
54
  self
51
55
  end
@@ -55,12 +59,36 @@ module AttrSearchableGrammar
55
59
  end
56
60
  end
57
61
 
58
- ["Equality", "NotEqual", "GreaterThan", "LessThan", "GreaterThanOrEqual", "LessThanOrEqual", "Matches", "Not"].each do |name|
59
- const_set name, Class.new(Arel::Nodes.const_get(name))
60
- const_get(name).send :include, Base
62
+ class Binary
63
+ include Base
64
+
65
+ attr_accessor :left, :right
66
+
67
+ def initialize(left, right)
68
+ @left = left
69
+ @right = right
70
+ end
61
71
  end
62
72
 
63
- class MatchesFulltext < Arel::Nodes::Binary
73
+ class Equality < Binary; end
74
+ class NotEqual < Binary; end
75
+ class GreaterThan < Binary; end
76
+ class GreaterThanOrEqual < Binary; end
77
+ class LessThan < Binary; end
78
+ class LessThanOrEqual < Binary; end
79
+ class Matches < Binary; end
80
+
81
+ class Not
82
+ include Base
83
+
84
+ attr_accessor :object
85
+
86
+ def initialize(object)
87
+ @object = object
88
+ end
89
+ end
90
+
91
+ class MatchesFulltext < Binary
64
92
  include Base
65
93
 
66
94
  def not
@@ -82,7 +110,7 @@ module AttrSearchableGrammar
82
110
 
83
111
  class MatchesFulltextNot < MatchesFulltext; end
84
112
 
85
- class FulltextExpression < Arel::Nodes::Node
113
+ class FulltextExpression
86
114
  include Base
87
115
 
88
116
  attr_reader :collection, :node
@@ -93,7 +121,7 @@ module AttrSearchableGrammar
93
121
  end
94
122
  end
95
123
 
96
- class Collection < Arel::Nodes::Node
124
+ class Collection
97
125
  include Base
98
126
 
99
127
  attr_reader :nodes
@@ -149,18 +177,10 @@ module AttrSearchableGrammar
149
177
 
150
178
  class And < Collection
151
179
  class Fulltext < FulltextCollection; end
152
-
153
- def to_arel
154
- nodes.inject { |res, cur| Arel::Nodes::And.new [res, cur] }
155
- end
156
180
  end
157
181
 
158
182
  class Or < Collection
159
183
  class Fulltext < FulltextCollection; end
160
-
161
- def to_arel
162
- nodes.inject { |res, cur| Arel::Nodes::Or.new res, cur }
163
- end
164
184
  end
165
185
  end
166
186
  end
@@ -3,8 +3,8 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class AndTest < AttrSearchable::TestCase
5
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")
6
+ expected = create(:product, :title => "Expected title", :description => "Description")
7
+ rejected = create(:product, :title => "Rejected title", :description => "Description")
8
8
 
9
9
  results = Product.search("title: 'Expected title' description: Description")
10
10
 
@@ -15,8 +15,8 @@ class AndTest < AttrSearchable::TestCase
15
15
  end
16
16
 
17
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")
18
+ expected = create(:product, :title => "Expected title", :description => "Description")
19
+ rejected = create(:product, :title => "Rejected title", :description => "Description")
20
20
 
21
21
  results = Product.search(:and => [{:title => "Expected title"}, {:description => "Description"}])
22
22
 
@@ -3,9 +3,9 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class AttrSearchableTest < AttrSearchable::TestCase
5
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")
6
+ product = create(:product, :comments => [
7
+ create(:comment, :title => "Title1", :message => "Message1"),
8
+ create(:comment, :title => "Title2", :message => "Message2")
9
9
  ])
10
10
 
11
11
  assert_includes Product.search("comment: Title1 comment: Message1"), product
@@ -13,15 +13,15 @@ class AttrSearchableTest < AttrSearchable::TestCase
13
13
  end
14
14
 
15
15
  def test_multiple
16
- product = FactoryGirl.create(:product, :comments => [FactoryGirl.create(:comment, :title => "Title", :message => "Message")])
16
+ product = create(:product, :comments => [create(:comment, :title => "Title", :message => "Message")])
17
17
 
18
18
  assert_includes Product.search("comment: Title"), product
19
19
  assert_includes Product.search("comment: Message"), product
20
20
  end
21
21
 
22
22
  def test_default
23
- product1 = FactoryGirl.create(:product, :title => "Expected")
24
- product2 = FactoryGirl.create(:product, :description => "Expected")
23
+ product1 = create(:product, :title => "Expected")
24
+ product2 = create(:product, :description => "Expected")
25
25
 
26
26
  results = Product.search("Expected")
27
27
 
@@ -30,9 +30,9 @@ class AttrSearchableTest < AttrSearchable::TestCase
30
30
  end
31
31
 
32
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")
33
+ product1 = create(:product, :title => "Expected")
34
+ product2 = create(:product, :description => "Expected")
35
+ product3 = create(:product, :brand => "Expected")
36
36
 
37
37
  results = with_attr_searchable_options(Product, :primary, :default => true) { Product.search "Expected" }
38
38
 
@@ -40,5 +40,11 @@ class AttrSearchableTest < AttrSearchable::TestCase
40
40
  assert_includes results, product2
41
41
  refute_includes results, product3
42
42
  end
43
+
44
+ def test_count
45
+ create_list :product, 2, :title => "Expected"
46
+
47
+ assert_equal 2, Product.search("Expected").count
48
+ end
43
49
  end
44
50
 
@@ -3,13 +3,13 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class BooleanTest < AttrSearchable::TestCase
5
5
  def test_mapping
6
- product = FactoryGirl.create(:product, :available => true)
6
+ product = create(:product, :available => true)
7
7
 
8
8
  assert_includes Product.search("available: 1"), product
9
9
  assert_includes Product.search("available: true"), product
10
10
  assert_includes Product.search("available: yes"), product
11
11
 
12
- product = FactoryGirl.create(:product, :available => false)
12
+ product = create(:product, :available => false)
13
13
 
14
14
  assert_includes Product.search("available: 0"), product
15
15
  assert_includes Product.search("available: false"), product
@@ -17,31 +17,37 @@ class BooleanTest < AttrSearchable::TestCase
17
17
  end
18
18
 
19
19
  def test_anywhere
20
- product = FactoryGirl.create(:product, :available => true)
20
+ product = create(:product, :available => true)
21
21
 
22
22
  assert_includes Product.search("true"), product
23
23
  refute_includes Product.search("false"), product
24
24
  end
25
25
 
26
26
  def test_includes
27
- product = FactoryGirl.create(:product, :available => true)
27
+ product = create(:product, :available => true)
28
28
 
29
29
  assert_includes Product.search("available: true"), product
30
30
  refute_includes Product.search("available: false"), product
31
31
  end
32
32
 
33
33
  def test_equals
34
- product = FactoryGirl.create(:product, :available => true)
34
+ product = create(:product, :available => true)
35
35
 
36
36
  assert_includes Product.search("available = true"), product
37
37
  refute_includes Product.search("available = false"), product
38
38
  end
39
39
 
40
40
  def test_equals_not
41
- product = FactoryGirl.create(:product, :available => false)
41
+ product = create(:product, :available => false)
42
42
 
43
43
  assert_includes Product.search("available != true"), product
44
44
  refute_includes Product.search("available != false"), product
45
45
  end
46
+
47
+ def test_incompatible_datatype
48
+ assert_raises AttrSearchable::IncompatibleDatatype do
49
+ Product.unsafe_search "available: Value"
50
+ end
51
+ end
46
52
  end
47
53
 
@@ -3,7 +3,7 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class DatetimeTest < AttrSearchable::TestCase
5
5
  def test_mapping
6
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01 12:30:30"))
6
+ product = create(:product, :created_at => Time.parse("2014-05-01 12:30:30"))
7
7
 
8
8
  assert_includes Product.search("created_at: 2014"), product
9
9
  assert_includes Product.search("created_at: 2014-05"), product
@@ -12,59 +12,65 @@ class DatetimeTest < AttrSearchable::TestCase
12
12
  end
13
13
 
14
14
  def test_anywhere
15
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
15
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
16
16
 
17
17
  assert_includes Product.search("2014-05-01"), product
18
18
  refute_includes Product.search("2014-05-02"), product
19
19
  end
20
20
 
21
21
  def test_includes
22
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
22
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
23
23
 
24
24
  assert_includes Product.search("created_at: 2014-05-01"), product
25
25
  refute_includes Product.search("created_at: 2014-05-02"), product
26
26
  end
27
27
 
28
28
  def test_equals
29
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
29
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
30
30
 
31
31
  assert_includes Product.search("created_at = 2014-05-01"), product
32
32
  refute_includes Product.search("created_at = 2014-05-02"), product
33
33
  end
34
34
 
35
35
  def test_equals_not
36
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
36
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
37
37
 
38
38
  assert_includes Product.search("created_at != 2014-05-02"), product
39
39
  refute_includes Product.search("created_at != 2014-05-01"), product
40
40
  end
41
41
 
42
42
  def test_greater
43
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
43
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
44
44
 
45
45
  assert_includes Product.search("created_at < 2014-05-02"), product
46
46
  refute_includes Product.search("created_at < 2014-05-01"), product
47
47
  end
48
48
 
49
49
  def test_greater_equals
50
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
50
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
51
51
 
52
52
  assert_includes Product.search("created_at >= 2014-05-01"), product
53
53
  refute_includes Product.search("created_at >= 2014-05-02"), product
54
54
  end
55
55
 
56
56
  def test_less
57
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-01"))
57
+ product = create(:product, :created_at => Time.parse("2014-05-01"))
58
58
 
59
59
  assert_includes Product.search("created_at < 2014-05-02"), product
60
60
  refute_includes Product.search("created_at < 2014-05-01"), product
61
61
  end
62
62
 
63
63
  def test_less_equals
64
- product = FactoryGirl.create(:product, :created_at => Time.parse("2014-05-02"))
64
+ product = create(:product, :created_at => Time.parse("2014-05-02"))
65
65
 
66
66
  assert_includes Product.search("created_at <= 2014-05-02"), product
67
67
  refute_includes Product.search("created_at <= 2014-05-01"), product
68
68
  end
69
+
70
+ def test_incompatible_datatype
71
+ assert_raises AttrSearchable::IncompatibleDatatype do
72
+ Product.unsafe_search "created_at: Value"
73
+ end
74
+ end
69
75
  end
70
76
 
@@ -0,0 +1,17 @@
1
+
2
+ require File.expand_path("../test_helper", __FILE__)
3
+
4
+ class ErrorTest < AttrSearchable::TestCase
5
+ def test_parse_error
6
+ assert_raises AttrSearchable::ParseError do
7
+ Product.unsafe_search :title => { :unknown_operator => "Value" }
8
+ end
9
+ end
10
+
11
+ def test_unknown_column
12
+ assert_raises AttrSearchable::UnknownColumn do
13
+ Product.unsafe_search "Unknown: Column"
14
+ end
15
+ end
16
+ end
17
+
@@ -3,59 +3,65 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class FloatTest < AttrSearchable::TestCase
5
5
  def test_anywhere
6
- product = FactoryGirl.create(:product, :price => 10.5, :created_at => Time.now - 1.day)
6
+ product = create(:product, :price => 10.5, :created_at => Time.now - 1.day)
7
7
 
8
8
  assert_includes Product.search("10.5"), product
9
9
  refute_includes Product.search("11.5"), product
10
10
  end
11
11
 
12
12
  def test_includes
13
- product = FactoryGirl.create(:product, :price => 10.5)
13
+ product = create(:product, :price => 10.5)
14
14
 
15
15
  assert_includes Product.search("price: 10.5"), product
16
16
  refute_includes Product.search("price: 11.5"), product
17
17
  end
18
18
 
19
19
  def test_equals
20
- product = FactoryGirl.create(:product, :price => 10.5)
20
+ product = create(:product, :price => 10.5)
21
21
 
22
22
  assert_includes Product.search("price = 10.5"), product
23
23
  refute_includes Product.search("price = 11.5"), product
24
24
  end
25
25
 
26
26
  def test_equals_not
27
- product = FactoryGirl.create(:product, :price => 10.5)
27
+ product = create(:product, :price => 10.5)
28
28
 
29
29
  assert_includes Product.search("price != 11.5"), product
30
30
  refute_includes Product.search("price != 10.5"), product
31
31
  end
32
32
 
33
33
  def test_greater
34
- product = FactoryGirl.create(:product, :price => 10.5)
34
+ product = create(:product, :price => 10.5)
35
35
 
36
36
  assert_includes Product.search("price > 10.4"), product
37
37
  refute_includes Product.search("price < 10.5"), product
38
38
  end
39
39
 
40
40
  def test_greater_equals
41
- product = FactoryGirl.create(:product, :price => 10.5)
41
+ product = create(:product, :price => 10.5)
42
42
 
43
43
  assert_includes Product.search("price >= 10.5"), product
44
44
  refute_includes Product.search("price >= 10.6"), product
45
45
  end
46
46
 
47
47
  def test_less
48
- product = FactoryGirl.create(:product, :price => 10.5)
48
+ product = create(:product, :price => 10.5)
49
49
 
50
50
  assert_includes Product.search("price < 10.6"), product
51
51
  refute_includes Product.search("price < 10.5"), product
52
52
  end
53
53
 
54
54
  def test_less_equals
55
- product = FactoryGirl.create(:product, :price => 10.5)
55
+ product = create(:product, :price => 10.5)
56
56
 
57
57
  assert_includes Product.search("price <= 10.5"), product
58
58
  refute_includes Product.search("price <= 10.4"), product
59
59
  end
60
+
61
+ def test_incompatible_datatype
62
+ assert_raises AttrSearchable::IncompatibleDatatype do
63
+ Product.unsafe_search "price: Value"
64
+ end
65
+ end
60
66
  end
61
67
 
@@ -3,9 +3,9 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class FulltextTest < AttrSearchable::TestCase
5
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")
6
+ product1 = create(:product, :title => "word1")
7
+ product2 = create(:product, :title => "word2 word3")
8
+ product3 = create(:product, :title => "word2")
9
9
 
10
10
  results = Product.search("title:word1 OR (title:word2 -title:word3)")
11
11
 
@@ -15,8 +15,8 @@ class FulltextTest < AttrSearchable::TestCase
15
15
  end
16
16
 
17
17
  def test_mixed
18
- expected = FactoryGirl.create(:product, :title => "Expected title", :stock => 1)
19
- rejected = FactoryGirl.create(:product, :title => "Expected title", :stock => 0)
18
+ expected = create(:product, :title => "Expected title", :stock => 1)
19
+ rejected = create(:product, :title => "Expected title", :stock => 0)
20
20
 
21
21
  results = Product.search("title:Expected title:Title stock > 0")
22
22
 
@@ -0,0 +1,97 @@
1
+
2
+ require File.expand_path("../test_helper", __FILE__)
3
+
4
+ class HashTest < AttrSearchable::TestCase
5
+ def test_subquery
6
+ product1 = create(:product, :title => "Title1", :description => "Description")
7
+ product2 = create(:product, :title => "Title2", :description => "Description")
8
+ product3 = create(:product, :title => "TItle3", :description => "Description")
9
+
10
+ results = Product.search(:or => [{ :query => "Description Title1" }, { :query => "Description Title2" }])
11
+
12
+ assert_includes results, product1
13
+ assert_includes results, product2
14
+ refute_includes results, product3
15
+ end
16
+
17
+ def test_matches
18
+ expected = create(:product, :title => "Expected")
19
+ rejected = create(:product, :title => "Rejected")
20
+
21
+ results = Product.search(:title => { :matches => "Expected" })
22
+
23
+ assert_includes results, expected
24
+ refute_includes results, rejected
25
+ end
26
+
27
+ def test_matches_default
28
+ expected = create(:product, :title => "Expected")
29
+ rejected = create(:product, :title => "Rejected")
30
+
31
+ results = Product.search(:title => "Expected")
32
+
33
+ assert_includes results, expected
34
+ refute_includes results, rejected
35
+ end
36
+
37
+ def test_eq
38
+ expected = create(:product, :title => "Expected")
39
+ rejected = create(:product, :title => "Rejected")
40
+
41
+ results = Product.search(:title => { :eq => "Expected" })
42
+
43
+ assert_includes results, expected
44
+ refute_includes results, rejected
45
+ end
46
+
47
+ def test_not_eq
48
+ expected = create(:product, :title => "Expected")
49
+ rejected = create(:product, :title => "Rejected")
50
+
51
+ results = Product.search(:title => { :not_eq => "Rejected" })
52
+
53
+ assert_includes results, expected
54
+ refute_includes results, rejected
55
+ end
56
+
57
+ def test_gt
58
+ expected = create(:product, :stock => 1)
59
+ rejected = create(:product, :stock => 0)
60
+
61
+ results = Product.search(:stock => { :gt => 0 })
62
+
63
+ assert_includes results, expected
64
+ refute_includes results, rejected
65
+ end
66
+
67
+ def test_gteq
68
+ expected = create(:product, :stock => 1)
69
+ rejected = create(:product, :stock => 0)
70
+
71
+ results = Product.search(:stock => { :gteq => 1 })
72
+
73
+ assert_includes results, expected
74
+ refute_includes results, rejected
75
+ end
76
+
77
+ def test_lt
78
+ expected = create(:product, :stock => 0)
79
+ rejected = create(:product, :stock => 1)
80
+
81
+ results = Product.search(:stock => { :lt => 1 })
82
+
83
+ assert_includes results, expected
84
+ refute_includes results, rejected
85
+ end
86
+
87
+ def test_lteq
88
+ expected = create(:product, :stock => 0)
89
+ rejected = create(:product, :stock => 1)
90
+
91
+ results = Product.search(:stock => { :lteq => 0 })
92
+
93
+ assert_includes results, expected
94
+ refute_includes results, rejected
95
+ end
96
+ end
97
+
@@ -3,59 +3,65 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class IntegerTest < AttrSearchable::TestCase
5
5
  def test_anywhere
6
- product = FactoryGirl.create(:product, :stock => 1)
6
+ product = create(:product, :stock => 1)
7
7
 
8
8
  assert_includes Product.search("1"), product
9
9
  refute_includes Product.search("0"), product
10
10
  end
11
11
 
12
12
  def test_includes
13
- product = FactoryGirl.create(:product, :stock => 1)
13
+ product = create(:product, :stock => 1)
14
14
 
15
15
  assert_includes Product.search("stock: 1"), product
16
16
  refute_includes Product.search("stock: 10"), product
17
17
  end
18
18
 
19
19
  def test_equals
20
- product = FactoryGirl.create(:product, :stock => 1)
20
+ product = create(:product, :stock => 1)
21
21
 
22
22
  assert_includes Product.search("stock = 1"), product
23
23
  refute_includes Product.search("stock = 0"), product
24
24
  end
25
25
 
26
26
  def test_equals_not
27
- product = FactoryGirl.create(:product, :stock => 1)
27
+ product = create(:product, :stock => 1)
28
28
 
29
29
  assert_includes Product.search("stock != 0"), product
30
30
  refute_includes Product.search("stock != 1"), product
31
31
  end
32
32
 
33
33
  def test_greater
34
- product = FactoryGirl.create(:product, :stock => 1)
34
+ product = create(:product, :stock => 1)
35
35
 
36
36
  assert_includes Product.search("stock > 0"), product
37
37
  refute_includes Product.search("stock < 1"), product
38
38
  end
39
39
 
40
40
  def test_greater_equals
41
- product = FactoryGirl.create(:product, :stock => 1)
41
+ product = create(:product, :stock => 1)
42
42
 
43
43
  assert_includes Product.search("stock >= 1"), product
44
44
  refute_includes Product.search("stock >= 2"), product
45
45
  end
46
46
 
47
47
  def test_less
48
- product = FactoryGirl.create(:product, :stock => 1)
48
+ product = create(:product, :stock => 1)
49
49
 
50
50
  assert_includes Product.search("stock < 2"), product
51
51
  refute_includes Product.search("stock < 1"), product
52
52
  end
53
53
 
54
54
  def test_less_equals
55
- product = FactoryGirl.create(:product, :stock => 1)
55
+ product = create(:product, :stock => 1)
56
56
 
57
57
  assert_includes Product.search("stock <= 1"), product
58
58
  refute_includes Product.search("stock <= 0"), product
59
59
  end
60
+
61
+ def test_incompatible_datatype
62
+ assert_raises AttrSearchable::IncompatibleDatatype do
63
+ Product.unsafe_search "stock: Value"
64
+ end
65
+ end
60
66
  end
61
67
 
@@ -3,8 +3,8 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class NotTest < AttrSearchable::TestCase
5
5
  def test_not_string
6
- expected = FactoryGirl.create(:product, :title => "Expected title")
7
- rejected = FactoryGirl.create(:product, :title => "Rejected title")
6
+ expected = create(:product, :title => "Expected title")
7
+ rejected = create(:product, :title => "Rejected title")
8
8
 
9
9
  results = Product.search("title: Title NOT title: Rejected")
10
10
 
@@ -15,8 +15,8 @@ class NotTest < AttrSearchable::TestCase
15
15
  end
16
16
 
17
17
  def test_not_hash
18
- expected = FactoryGirl.create(:product, :title => "Expected title")
19
- rejected = FactoryGirl.create(:product, :title => "Rejected title")
18
+ expected = create(:product, :title => "Expected title")
19
+ rejected = create(:product, :title => "Rejected title")
20
20
 
21
21
  results = Product.search(:and => [{:title => "Title"}, {:not => {:title => "Rejected"}}])
22
22
 
@@ -3,9 +3,9 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class OrTest < AttrSearchable::TestCase
5
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")
6
+ product1 = create(:product, :title => "Title1")
7
+ product2 = create(:product, :title => "Title2")
8
+ product3 = create(:product, :title => "Title3")
9
9
 
10
10
  results = Product.search("title: Title1 OR title: Title2")
11
11
 
@@ -15,9 +15,9 @@ class OrTest < AttrSearchable::TestCase
15
15
  end
16
16
 
17
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")
18
+ product1 = create(:product, :title => "Title1")
19
+ product2 = create(:product, :title => "Title2")
20
+ product3 = create(:product, :title => "Title3")
21
21
 
22
22
  results = Product.search(:or => [{:title => "Title1"}, {:title => "Title2"}])
23
23
 
@@ -3,35 +3,35 @@ require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class StringTest < AttrSearchable::TestCase
5
5
  def test_anywhere
6
- product = FactoryGirl.create(:product, :title => "Expected title")
6
+ product = create(:product, :title => "Expected title")
7
7
 
8
8
  assert_includes Product.search("Expected"), product
9
9
  refute_includes Product.search("Rejected"), product
10
10
  end
11
11
 
12
12
  def test_multiple
13
- product = FactoryGirl.create(:product, :comments => [FactoryGirl.create(:comment, :title => "Expected title", :message => "Expected message")])
13
+ product = create(:product, :comments => [create(:comment, :title => "Expected title", :message => "Expected message")])
14
14
 
15
15
  assert_includes Product.search("Expected"), product
16
16
  refute_includes Product.search("Rejected"), product
17
17
  end
18
18
 
19
19
  def test_includes
20
- product = FactoryGirl.create(:product, :title => "Expected")
20
+ product = create(:product, :title => "Expected")
21
21
 
22
22
  assert_includes Product.search("title: Expected"), product
23
23
  refute_includes Product.search("title: Rejected"), product
24
24
  end
25
25
 
26
26
  def test_includes_with_left_wildcard
27
- product = FactoryGirl.create(:product, :title => "Some title")
27
+ product = create(:product, :title => "Some title")
28
28
 
29
29
  assert_includes Product.search("title: Title"), product
30
30
  end
31
31
 
32
32
  def test_includes_without_left_wildcard
33
- expected = FactoryGirl.create(:product, :brand => "Brand")
34
- rejected = FactoryGirl.create(:product, :brand => "Rejected brand")
33
+ expected = create(:product, :brand => "Brand")
34
+ rejected = create(:product, :brand => "Rejected brand")
35
35
 
36
36
  results = with_attr_searchable_options(Product, :brand, :left_wildcard => false) { Product.search "brand: Brand" }
37
37
 
@@ -40,42 +40,42 @@ class StringTest < AttrSearchable::TestCase
40
40
  end
41
41
 
42
42
  def test_equals
43
- product = FactoryGirl.create(:product, :title => "Expected title")
43
+ product = create(:product, :title => "Expected title")
44
44
 
45
45
  assert_includes Product.search("title = 'Expected title'"), product
46
46
  refute_includes Product.search("title = Expected"), product
47
47
  end
48
48
 
49
49
  def test_equals_not
50
- product = FactoryGirl.create(:product, :title => "Expected")
50
+ product = create(:product, :title => "Expected")
51
51
 
52
52
  assert_includes Product.search("title != Rejected"), product
53
53
  refute_includes Product.search("title != Expected"), product
54
54
  end
55
55
 
56
56
  def test_greater
57
- product = FactoryGirl.create(:product, :title => "Title B")
57
+ product = create(:product, :title => "Title B")
58
58
 
59
59
  assert_includes Product.search("title > 'Title A'"), product
60
60
  refute_includes Product.search("title > 'Title B'"), product
61
61
  end
62
62
 
63
63
  def test_greater_equals
64
- product = FactoryGirl.create(:product, :title => "Title A")
64
+ product = create(:product, :title => "Title A")
65
65
 
66
66
  assert_includes Product.search("title >= 'Title A'"), product
67
67
  refute_includes Product.search("title >= 'Title B'"), product
68
68
  end
69
69
 
70
70
  def test_less
71
- product = FactoryGirl.create(:product, :title => "Title A")
71
+ product = create(:product, :title => "Title A")
72
72
 
73
73
  assert_includes Product.search("title < 'Title B'"), product
74
74
  refute_includes Product.search("title < 'Title A'"), product
75
75
  end
76
76
 
77
77
  def test_less_or_greater
78
- product = FactoryGirl.create(:product, :title => "Title B")
78
+ product = create(:product, :title => "Title B")
79
79
 
80
80
  assert_includes Product.search("title <= 'Title B'"), product
81
81
  refute_includes Product.search("title <= 'Title A'"), product
@@ -79,6 +79,8 @@ if DATABASE == "mysql"
79
79
  end
80
80
 
81
81
  class AttrSearchable::TestCase
82
+ include FactoryGirl::Syntax::Methods
83
+
82
84
  def teardown
83
85
  Product.delete_all
84
86
  Comment.delete_all
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_searchable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-11 00:00:00.000000000 Z
12
+ date: 2014-06-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: treetop
@@ -123,7 +123,7 @@ dependencies:
123
123
  - - ! '>='
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
- description: Complex search-engine like query support for activerecord
126
+ description: Search-engine like fulltext query support for ActiveRecord
127
127
  email:
128
128
  - vetter@flakks.com
129
129
  executables: []
@@ -155,13 +155,14 @@ files:
155
155
  - test/boolean_test.rb
156
156
  - test/database.yml
157
157
  - test/datetime_test.rb
158
+ - test/error_test.rb
158
159
  - test/float_test.rb
159
160
  - test/fulltext_test.rb
161
+ - test/hash_test.rb
160
162
  - test/integer_test.rb
161
163
  - test/not_test.rb
162
164
  - test/or_test.rb
163
165
  - test/string_test.rb
164
- - test/sub_query_test.rb
165
166
  - test/test_helper.rb
166
167
  homepage: https://github.com/mrkamel/attr_searchable
167
168
  licenses:
@@ -187,18 +188,20 @@ rubyforge_project:
187
188
  rubygems_version: 1.8.23
188
189
  signing_key:
189
190
  specification_version: 3
190
- summary: Easily perform complex search-engine like queries on your activerecord models
191
+ summary: Easily perform complex search-engine like fulltext queries on your ActiveRecord
192
+ models
191
193
  test_files:
192
194
  - test/and_test.rb
193
195
  - test/attr_searchable_test.rb
194
196
  - test/boolean_test.rb
195
197
  - test/database.yml
196
198
  - test/datetime_test.rb
199
+ - test/error_test.rb
197
200
  - test/float_test.rb
198
201
  - test/fulltext_test.rb
202
+ - test/hash_test.rb
199
203
  - test/integer_test.rb
200
204
  - test/not_test.rb
201
205
  - test/or_test.rb
202
206
  - test/string_test.rb
203
- - test/sub_query_test.rb
204
207
  - test/test_helper.rb
@@ -1,17 +0,0 @@
1
-
2
- require File.expand_path("../test_helper", __FILE__)
3
-
4
- class SubQueryTest < AttrSearchable::TestCase
5
- def test_subquery
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(:or => [{:query => "Title1"}, {:query => "Title2"}])
11
-
12
- assert_includes results, product1
13
- assert_includes results, product2
14
- refute_includes results, product3
15
- end
16
- end
17
-