searchgasm 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +17 -4
  3. data/README.mdown +19 -16
  4. data/lib/searchgasm/search/base.rb +2 -4
  5. data/lib/searchgasm/search/condition_types/begins_with_condition.rb +28 -0
  6. data/lib/searchgasm/search/condition_types/child_of_condition.rb +17 -0
  7. data/lib/searchgasm/search/condition_types/condition.rb +107 -0
  8. data/lib/searchgasm/search/condition_types/contains_condition.rb +26 -0
  9. data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +30 -0
  10. data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +34 -0
  11. data/lib/searchgasm/search/condition_types/ends_with_condition.rb +26 -0
  12. data/lib/searchgasm/search/condition_types/equals_condition.rb +26 -0
  13. data/lib/searchgasm/search/condition_types/greater_than_condition.rb +31 -0
  14. data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +26 -0
  15. data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +19 -0
  16. data/lib/searchgasm/search/condition_types/keywords_condition.rb +39 -0
  17. data/lib/searchgasm/search/condition_types/less_than_condition.rb +31 -0
  18. data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +26 -0
  19. data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +22 -0
  20. data/lib/searchgasm/search/condition_types/tree_condition.rb +20 -0
  21. data/lib/searchgasm/search/conditions.rb +55 -82
  22. data/lib/searchgasm/version.rb +1 -1
  23. data/lib/searchgasm.rb +23 -2
  24. data/searchgasm.gemspec +36 -10
  25. data/test/test_helper.rb +1 -0
  26. data/test/test_searchgasm_base.rb +32 -0
  27. data/test/test_searchgasm_condition_types.rb +143 -0
  28. data/test/test_searchgasm_conditions.rb +21 -29
  29. metadata +35 -9
  30. data/lib/searchgasm/helpers.rb +0 -100
  31. data/lib/searchgasm/search/condition.rb +0 -168
  32. data/test/test_active_record_protection.rb +0 -0
  33. data/test/test_searchgasm_condition.rb +0 -149
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.9.3. Changed structure of conditions to have their own class. Making it easier to add your own conditions.
2
+
1
3
  v0.9.2. Enhanced protection
2
4
 
3
5
  v0.9.1. Added aliases for datetime, date, time, and timestamp attrs. You could call created_at_after, mow you can also call created_after.
data/Manifest CHANGED
@@ -2,9 +2,23 @@ CHANGELOG
2
2
  init.rb
3
3
  lib/searchgasm/active_record/associations.rb
4
4
  lib/searchgasm/active_record/base.rb
5
- lib/searchgasm/helpers.rb
6
5
  lib/searchgasm/search/base.rb
7
- lib/searchgasm/search/condition.rb
6
+ lib/searchgasm/search/condition_types/begins_with_condition.rb
7
+ lib/searchgasm/search/condition_types/child_of_condition.rb
8
+ lib/searchgasm/search/condition_types/condition.rb
9
+ lib/searchgasm/search/condition_types/contains_condition.rb
10
+ lib/searchgasm/search/condition_types/descendant_of_condition.rb
11
+ lib/searchgasm/search/condition_types/does_not_equal_condition.rb
12
+ lib/searchgasm/search/condition_types/ends_with_condition.rb
13
+ lib/searchgasm/search/condition_types/equals_condition.rb
14
+ lib/searchgasm/search/condition_types/greater_than_condition.rb
15
+ lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb
16
+ lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb
17
+ lib/searchgasm/search/condition_types/keywords_condition.rb
18
+ lib/searchgasm/search/condition_types/less_than_condition.rb
19
+ lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb
20
+ lib/searchgasm/search/condition_types/sibling_of_condition.rb
21
+ lib/searchgasm/search/condition_types/tree_condition.rb
8
22
  lib/searchgasm/search/conditions.rb
9
23
  lib/searchgasm/search/utilities.rb
10
24
  lib/searchgasm/version.rb
@@ -20,8 +34,7 @@ test/libs/acts_as_tree.rb
20
34
  test/libs/rexml_fix.rb
21
35
  test/test_active_record_associations.rb
22
36
  test/test_active_record_base.rb
23
- test/test_active_record_protection.rb
24
37
  test/test_helper.rb
25
38
  test/test_searchgasm_base.rb
26
- test/test_searchgasm_condition.rb
39
+ test/test_searchgasm_condition_types.rb
27
40
  test/test_searchgasm_conditions.rb
data/README.mdown CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
4
4
 
5
- Searchgasm was a super secret tool of mine until I decided to share with the world. It has saved me tons of time, allowed me to write less code, made searching painless, and kept my controllers DRY.
5
+ Searchgasm was a super secret tool of mine until I decided to share with the world. It has saved me tons of time, allowed me to write less code, made searching painless, and kept my controllers DRY and simple.
6
6
 
7
7
  It originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
8
8
 
9
- I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. This doesn't step on the toes or dig into he internals of ActiveRecord. It uses what ActiveRecord provides publicly. Letting ActiveRecord do all of the hard work and keeping this plugin solid and less brittle.
9
+ I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. It doesn't dig into the ActiveRecord internals, it only uses what is publicly available. It jumps in and helps out only when needed, otherwise it sits back and lets ActiveRecord do all of the work. Between that and the extensive tests this is a solid plugin.
10
10
 
11
11
  Here's where you get aroused...
12
12
 
@@ -18,7 +18,7 @@ Here's where you get aroused...
18
18
  @users, @users_count = @search.all, @search.count
19
19
  end
20
20
 
21
- Now your view:
21
+ Now your view, isn't it nice to use form builder again?
22
22
 
23
23
  # app/views/users/index.html.erb
24
24
  <%= form_for :conditions, @search.conditions, :url => users_path do |f| %>
@@ -63,7 +63,9 @@ Now go into your console and try out any of these example with your own models.
63
63
  :page => 3 # offset 60
64
64
  )
65
65
 
66
- ## Detailed Example w/ object based searching
66
+ Instead of using the "all" method you could use any search method: first, find(:all), find(:first), count, sum, average, etc
67
+
68
+ ## Detailed Example w/ object based searching (great for form\_for for fields\_for)
67
69
 
68
70
  # Instantiate
69
71
  @search = User.new_search(
@@ -137,7 +139,7 @@ If you want to be hardcore:
137
139
  search.per_page = 20
138
140
  search.all
139
141
 
140
- ## Search with conditions only (great for form\_for or fields\_for)
142
+ ## Search with conditions only
141
143
 
142
144
  conditions = User.new_conditions(:age_gt => 18)
143
145
  conditions.first_name_contains = "Ben"
@@ -177,16 +179,15 @@ For tree data structures you get a few nifty methods. Let's assume Users is a tr
177
179
  User.all(:conditions => {:sibling_of => User.roots.first})
178
180
  User.all(:conditions => {:sibling_of => User.roots.first.id})
179
181
 
180
- # Descendent of (includes all recursive children: children, grand children, great grand children, etc)
181
- User.all(:conditions => {:descendent_of => User.roots.first})
182
- User.all(:conditions => {:descendent_of => User.roots.first.id})
182
+ # Descendant of (includes all recursive children: children, grand children, great grand children, etc)
183
+ User.all(:conditions => {:descendant_of => User.roots.first})
184
+ User.all(:conditions => {:descendant_of => User.roots.first.id})
183
185
 
184
- # Inclusive descendent_of. Same as above but includes the root
185
- User.all(:conditions => {:inclusive_descendent_of => User.roots.first})
186
- User.all(:conditions => {:inclusive_descendent_of => User.roots.first.id})
186
+ # Inclusive descendant_of. Same as above but includes the root
187
+ User.all(:conditions => {:inclusive_descendant_of => User.roots.first})
188
+ User.all(:conditions => {:inclusive_descendant_of => User.roots.first.id})
187
189
 
188
190
 
189
-
190
191
  ## Available anywhere (relationships & named scopes)
191
192
 
192
193
  Not only can you use searchgasm when searching, but you can use it when setting up relationships or named scopes:
@@ -198,7 +199,7 @@ Not only can you use searchgasm when searching, but you can use it when setting
198
199
 
199
200
  ## Always use protection...against SQL injections
200
201
 
201
- If there is one thing we all know, it's to always use protection. That's why searchgasm protects you by default. The new\_search and new\_conditions methods are protected by default. This means that various checks are done to ensure it is not possible to perform any type of SQL injection. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection prepare to accept the consequences. All that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!. That was an awkward paragraph. Let's move on.
202
+ If there is one thing we all know, it's to always use protection. That's why searchgasm protects you by default. The new\_search and new\_conditions methods are protected by default. This means that various checks are done to ensure it is not possible to perform any type of SQL injection. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection prepare to accept the consequences. All that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!.
202
203
 
203
204
  ### Protected from SQL injections
204
205
 
@@ -229,16 +230,18 @@ Depending on the type, each column comes preloaded with a bunch of nifty conditi
229
230
  => :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
230
231
 
231
232
  tree data structures (see above "searching trees")
232
- => :child_of, :sibling_of, :descendent_of, :inclusive_descendent_of
233
+ => :child_of, :sibling_of, :descendant_of, :inclusive_descendant_of
233
234
 
234
235
  Some of these conditions come with aliases, so you have your choice how to call the conditions. For example you can use "greater\_than" or "gt":
235
236
 
236
237
  :equals; => :is
237
238
  :does_not_equal => :is_not, :not
238
- :begins_with => :starts_with
239
- :contains => :like
239
+ :begins_with => :starts_with, :bw, :start
240
+ :contains => :like, :has
241
+ :ends_with => :ew, :ends, :end
240
242
  :greater_than => :gt, :after
241
243
  :greater_than_or_equal_to => :at_least, :gte
244
+ :keywords => :kwords, :kw
242
245
  :less_than => :lt, :before
243
246
  :less_than_or_equal_to => :at_most, :lte
244
247
 
@@ -20,12 +20,10 @@ module BinaryLogic
20
20
  end
21
21
 
22
22
  (::ActiveRecord::Base.valid_find_options - [:conditions]).each do |option|
23
- src = <<-SRC
23
+ class_eval <<-end_eval
24
24
  def #{option}(sanitize = false); options[:#{option}]; end
25
25
  def #{option}=(value); self.options[:#{option}] = value; end
26
- SRC
27
-
28
- class_eval src
26
+ end_eval
29
27
  end
30
28
 
31
29
  alias_method :per_page, :limit
@@ -0,0 +1,28 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class BeginsWithCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless string_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_bw", "#{column.name}_starts_with", "#{column.name}_start"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "#{value}%"]
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::BeginsWithCondition)
24
+ end
25
+ end
26
+ end
27
+
28
+ BinaryLogic::Searchgasm::Search::Conditions.register_condition(BinaryLogic::Searchgasm::Search::ConditionTypes::BeginsWithCondition)
@@ -0,0 +1,17 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class ChildOfCondition < TreeCondition
6
+ def to_conditions(value)
7
+ parent_association = klass.reflect_on_association(:parent)
8
+ foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
9
+ ["#{quoted_table_name}.#{quote_column_name(foreign_key_name)} = ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)]
10
+ end
11
+ end
12
+ end
13
+
14
+ Conditions.register_condition(ConditionTypes::ChildOfCondition)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class Condition
6
+ include Utilities
7
+
8
+ attr_accessor :column, :klass
9
+ attr_reader :value
10
+
11
+ class << self
12
+ def condition_name
13
+ name.split("::").last.scan(/(.*)Condition/)[0][0].underscore
14
+ end
15
+
16
+ def name_for_column(column)
17
+ "#{column.name}_#{condition_name}"
18
+ end
19
+
20
+ def aliases_for_column(column)
21
+ []
22
+ end
23
+
24
+ def name_for_klass(klass)
25
+ nil
26
+ end
27
+
28
+ def aliases_for_klass(klass)
29
+ []
30
+ end
31
+
32
+ def string_column?(column)
33
+ [:string, :text].include?(column.type)
34
+ end
35
+
36
+ def comparable_column?(column)
37
+ [:integer, :float, :decimal, :datetime, :timestamp, :time, :date].include?(column.type)
38
+ end
39
+ end
40
+
41
+ def initialize(klass, column = nil)
42
+ self.klass = klass
43
+ self.column = column.is_a?(String) ? klass.columns_hash[column] : column
44
+ end
45
+
46
+ def explicitly_set_value=(value)
47
+ @explicitly_set_value = value
48
+ end
49
+
50
+ # Need this if someone wants to actually use nil in a meaningful way
51
+ def explicitly_set_value?
52
+ @explicitly_set_value == true
53
+ end
54
+
55
+ def ignore_blanks?
56
+ true
57
+ end
58
+
59
+ def name
60
+ column ? self.class.name_for_column(column) : self.class.name_for_klass(klass)
61
+ end
62
+
63
+ def condition_name
64
+ self.class.condition_name
65
+ end
66
+
67
+ def quote_column_name(column_name)
68
+ klass.connection.quote_column_name(column_name)
69
+ end
70
+
71
+ def quoted_column_name
72
+ quote_column_name(column.name)
73
+ end
74
+
75
+ def quote_table_name(table_name)
76
+ klass.connection.quote_table_name(table_name)
77
+ end
78
+
79
+ def quoted_table_name
80
+ quote_table_name(klass.table_name)
81
+ end
82
+
83
+ def sanitize(alt_value = nil)
84
+ return unless explicitly_set_value?
85
+ v = alt_value || value
86
+ if v.is_a?(Array) && !["equals", "does_not_equal"].include?(condition_name)
87
+ merge_conditions(*v.collect { |i| sanitize(i) })
88
+ else
89
+ v = v.utc if column && [:time, :timestamp, :datetime].include?(column.type) && klass.time_zone_aware_attributes && !klass.skip_time_zone_conversion_for_attributes.include?(column.name.to_sym)
90
+ to_conditions(v)
91
+ end
92
+ end
93
+
94
+ def value
95
+ @value.is_a?(String) ? column.type_cast(@value) : @value
96
+ end
97
+
98
+ def value=(v)
99
+ return if ignore_blanks? && v.blank?
100
+ self.explicitly_set_value = true
101
+ @value = v
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,26 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class ContainsCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless string_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_like", "#{column.name}_has"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}%"]
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::ContainsCondition)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class DescendantOfCondition < TreeCondition
6
+ def to_conditions(value)
7
+ # Wish I knew how to do this in SQL
8
+ root = (value.is_a?(klass) ? value : klass.find(value)) rescue return
9
+ strs = []
10
+ subs = []
11
+ all_children_ids(root).each do |child_id|
12
+ strs << "#{quoted_table_name}.#{quote_column_name(klass.primary_key)} = ?"
13
+ subs << child_id
14
+ end
15
+ [strs.join(" OR "), *subs]
16
+ end
17
+
18
+ private
19
+ def all_children_ids(record)
20
+ ids = record.children.collect { |child| child.send(klass.primary_key) }
21
+ record.children.each { |child| ids += all_children_ids(child) }
22
+ ids
23
+ end
24
+ end
25
+ end
26
+
27
+ Conditions.register_condition(ConditionTypes::DescendantOfCondition)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class DoesNotEqualCondition < Condition
6
+ class << self
7
+ def aliases_for_column(column)
8
+ ["#{column.name}_is_not", "#{column.name}_not"]
9
+ end
10
+ end
11
+
12
+ def ignore_blanks?
13
+ false
14
+ end
15
+
16
+ def to_conditions(value)
17
+ # Delegate to equals and then change
18
+ condition = EqualsCondition.new(klass, column)
19
+ condition.value = value
20
+
21
+ sql = condition.sanitize
22
+ sql.gsub!(/ IS /, " IS NOT ")
23
+ sql.gsub!(/ BETWEEN /, " NOT BETWEEN ")
24
+ sql.gsub!(/ IN /, " NOT IN ")
25
+ sql.gsub!(/=/, "!=")
26
+ sql
27
+ end
28
+ end
29
+ end
30
+
31
+ Conditions.register_condition(ConditionTypes::DoesNotEqualCondition)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class EndsWithCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless string_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_ew", "#{column.name}_ends", "#{column.name}_end"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}"]
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::EndsWithCondition)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class EqualsCondition < Condition
6
+ class << self
7
+ def aliases_for_column(column)
8
+ ["#{column.name}", "#{column.name}_is"]
9
+ end
10
+ end
11
+
12
+ def ignore_blanks?
13
+ false
14
+ end
15
+
16
+ def to_conditions(value)
17
+ # Let ActiveRecord handle this
18
+ klass.send(:sanitize_sql_hash_for_conditions, {column.name => value})
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::EqualsCondition)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class GreaterThanCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless comparable_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ column_names = [column.name]
14
+ column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
15
+
16
+ aliases = []
17
+ column_names.each { |column_name| aliases += ["#{column_name}_gt", "#{column_name}_after"] }
18
+ aliases
19
+ end
20
+ end
21
+
22
+ def to_conditions(value)
23
+ ["#{quoted_table_name}.#{quoted_column_name} > ?", value]
24
+ end
25
+ end
26
+ end
27
+
28
+ Conditions.register_condition(ConditionTypes::GreaterThanCondition)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class GreaterThanOrEqualToCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless comparable_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_gte", "#{column.name}_at_least"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ ["#{quoted_table_name}.#{quoted_column_name} >= ?", value]
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::GreaterThanOrEqualToCondition)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class InclusiveDescendantOfCondition < TreeCondition
6
+ include Search::Utilities
7
+
8
+ def to_conditions(value)
9
+ condition = DescendantOfCondition.new(klass, column)
10
+ condition.value = value
11
+ merge_conditions(["#{quoted_table_name}.#{quote_column_name(klass.primary_key)} = ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)], condition.sanitize, :any => true)
12
+ end
13
+ end
14
+ end
15
+
16
+ Conditions.register_condition(ConditionTypes::InclusiveDescendantOfCondition)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class KeywordsCondition < Condition
6
+ BLACKLISTED_WORDS = ('a'..'z').to_a + ["about", "an", "are", "as", "at", "be", "by", "com", "de", "en", "for", "from", "how", "in", "is", "it", "la", "of", "on", "or", "that", "the", "the", "this", "to", "und", "was", "what", "when", "where", "who", "will", "with", "www"] # from ranks.nl
7
+
8
+ class << self
9
+ def name_for_column(column)
10
+ return unless string_column?(column)
11
+ super
12
+ end
13
+
14
+ def aliases_for_column(column)
15
+ ["#{column.name}_kwords", "#{column.name}_kw"]
16
+ end
17
+ end
18
+
19
+ def to_conditions(value)
20
+ strs = []
21
+ subs = []
22
+
23
+ search_parts = value.gsub(/,/, " ").split(/ /).collect { |word| word.downcase.gsub(/[^[:alnum:]]/, ''); }.uniq.select { |word| !BLACKLISTED_WORDS.include?(word.downcase) && !word.blank? }
24
+ search_parts.each do |search_part|
25
+ strs << "#{quoted_table_name}.#{quoted_column_name} LIKE ?"
26
+ subs << "%#{search_part}%"
27
+ end
28
+
29
+ return if strs.blank?
30
+
31
+ [strs.join(" AND "), *subs]
32
+ end
33
+ end
34
+ end
35
+
36
+ Conditions.register_condition(ConditionTypes::KeywordsCondition)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class LessThanCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless comparable_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ column_names = [column.name]
14
+ column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
15
+
16
+ aliases = []
17
+ column_names.each { |column_name| aliases += ["#{column_name}_lt", "#{column_name}_before"] }
18
+ aliases
19
+ end
20
+ end
21
+
22
+ def to_conditions(value)
23
+ ["#{quoted_table_name}.#{quoted_column_name} < ?", value]
24
+ end
25
+ end
26
+ end
27
+
28
+ Conditions.register_condition(ConditionTypes::LessThanCondition)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class LessThanOrEqualToCondition < Condition
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless comparable_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_lte", "#{column.name}_at_most"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ ["#{quoted_table_name}.#{quoted_column_name} <= ?", value]
19
+ end
20
+ end
21
+ end
22
+
23
+ Conditions.register_condition(ConditionTypes::LessThanOrEqualToCondition)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module BinaryLogic
2
+ module Searchgasm
3
+ module Search
4
+ module ConditionTypes
5
+ class SiblingOfCondition < TreeCondition
6
+ include Search::Utilities
7
+
8
+ def to_conditions(value)
9
+ parent_association = klass.reflect_on_association(:parent)
10
+ foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
11
+ parent_id = (value.is_a?(klass) ? value : klass.find(value)).send(foreign_key_name)
12
+ condition = ChildOfCondition.new(klass, column)
13
+ condition.value = parent_id
14
+ merge_conditions(["#{quoted_table_name}.#{quote_column_name(klass.primary_key)} != ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)], condition.sanitize)
15
+ end
16
+ end
17
+ end
18
+
19
+ Conditions.register_condition(ConditionTypes::SiblingOfCondition)
20
+ end
21
+ end
22
+ end