attr_searchable 0.0.1 → 0.0.2

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.
@@ -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
-