searchgasm 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +34 -21
- data/{README.mdown → README.rdoc} +96 -63
- data/Rakefile +1 -1
- data/examples/README.rdoc +4 -0
- data/lib/searchgasm/active_record/associations.rb +40 -42
- data/lib/searchgasm/active_record/base.rb +75 -61
- data/lib/searchgasm/condition/base.rb +127 -0
- data/lib/searchgasm/condition/begins_with.rb +20 -0
- data/lib/searchgasm/condition/child_of.rb +11 -0
- data/lib/searchgasm/condition/contains.rb +20 -0
- data/lib/searchgasm/condition/descendant_of.rb +24 -0
- data/lib/searchgasm/condition/does_not_equal.rb +28 -0
- data/lib/searchgasm/condition/ends_with.rb +20 -0
- data/lib/searchgasm/condition/equals.rb +20 -0
- data/lib/searchgasm/condition/greater_than.rb +25 -0
- data/lib/searchgasm/condition/greater_than_or_equal_to.rb +20 -0
- data/lib/searchgasm/condition/inclusive_descendant_of.rb +13 -0
- data/lib/searchgasm/condition/keywords.rb +33 -0
- data/lib/searchgasm/condition/less_than.rb +25 -0
- data/lib/searchgasm/condition/less_than_or_equal_to.rb +20 -0
- data/lib/searchgasm/condition/sibling_of.rb +16 -0
- data/lib/searchgasm/condition/tree.rb +16 -0
- data/lib/searchgasm/conditions/base.rb +221 -0
- data/lib/searchgasm/conditions/protection.rb +30 -0
- data/lib/searchgasm/config.rb +137 -0
- data/lib/searchgasm/helpers/form_helper.rb +159 -0
- data/lib/searchgasm/helpers/search_helper.rb +178 -0
- data/lib/searchgasm/helpers/utilities_helper.rb +125 -0
- data/lib/searchgasm/search/base.rb +73 -179
- data/lib/searchgasm/search/conditions.rb +42 -166
- data/lib/searchgasm/search/ordering.rb +149 -0
- data/lib/searchgasm/search/pagination.rb +69 -0
- data/lib/searchgasm/search/protection.rb +61 -0
- data/lib/searchgasm/utilities.rb +30 -0
- data/lib/searchgasm/version.rb +44 -47
- data/lib/searchgasm.rb +57 -21
- data/searchgasm.gemspec +71 -46
- data/test/test_active_record_associations.rb +1 -1
- data/test/test_active_record_base.rb +4 -4
- data/test/test_condition.rb +143 -0
- data/test/{test_searchgasm_conditions.rb → test_conditions_base.rb} +43 -33
- data/test/test_search_base.rb +189 -0
- data/test/test_search_ordering.rb +91 -0
- data/test/test_search_pagination.rb +56 -0
- data/test/test_search_protection.rb +35 -0
- metadata +70 -45
- data/lib/searchgasm/search/condition.rb +0 -105
- data/lib/searchgasm/search/condition_types/begins_with_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/child_of_condition.rb +0 -17
- data/lib/searchgasm/search/condition_types/contains_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +0 -30
- data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +0 -34
- data/lib/searchgasm/search/condition_types/ends_with_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/equals_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/greater_than_condition.rb +0 -31
- data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +0 -19
- data/lib/searchgasm/search/condition_types/keywords_condition.rb +0 -39
- data/lib/searchgasm/search/condition_types/less_than_condition.rb +0 -31
- data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +0 -22
- data/lib/searchgasm/search/condition_types/tree_condition.rb +0 -20
- data/lib/searchgasm/search/utilities.rb +0 -34
- data/test/test_searchgasm_base.rb +0 -185
- data/test/test_searchgasm_condition_types.rb +0 -143
@@ -1,76 +1,90 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
1
|
+
module Searchgasm
|
2
|
+
module ActiveRecord #:nodoc: all
|
3
|
+
module Base
|
4
|
+
def calculate_with_searchgasm(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
options = sanitize_options_with_searchgasm(options)
|
7
|
+
args << options
|
8
|
+
calculate_without_searchgasm(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_with_searchgasm(*args)
|
12
|
+
options = args.extract_options!
|
13
|
+
options = sanitize_options_with_searchgasm(options)
|
14
|
+
args << options
|
15
|
+
find_without_searchgasm(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def scope_with_searchgasm(method, key = nil)
|
19
|
+
scope = scope_without_searchgasm(method, key)
|
20
|
+
return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
|
21
|
+
scope
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_conditions(values = {}, &block)
|
25
|
+
conditions = searchgasm_conditions
|
26
|
+
conditions.protect = true
|
27
|
+
conditions.conditions = values
|
28
|
+
yield conditions if block_given?
|
29
|
+
conditions
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_conditions!(values = {}, &block)
|
33
|
+
conditions = searchgasm_conditions(values)
|
34
|
+
yield conditions if block_given?
|
35
|
+
conditions
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_search(options = {}, &block)
|
39
|
+
search = searchgasm_searcher
|
40
|
+
search.protect = true
|
41
|
+
search.options = options
|
42
|
+
yield search if block_given?
|
43
|
+
search
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_search!(options = {}, &block)
|
47
|
+
search = searchgasm_searcher(options)
|
48
|
+
yield search if block_given?
|
49
|
+
search
|
50
|
+
end
|
24
51
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
52
|
+
def conditions_protected(*conditions)
|
53
|
+
write_inheritable_attribute(:conditions_protected, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
|
54
|
+
end
|
55
|
+
|
56
|
+
def protected_conditions
|
57
|
+
read_inheritable_attribute(:conditions_protected)
|
58
|
+
end
|
32
59
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
60
|
+
def conditions_accessible(*conditions)
|
61
|
+
write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
|
62
|
+
end
|
63
|
+
|
64
|
+
def accessible_conditions
|
65
|
+
read_inheritable_attribute(:conditions_accessible)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def sanitize_options_with_searchgasm(options = {})
|
70
|
+
return options unless Searchgasm::Search::Base.needed?(self, options)
|
71
|
+
searchgasm_searcher(options).sanitize
|
37
72
|
end
|
38
73
|
|
39
|
-
def
|
40
|
-
|
41
|
-
search.protect = true
|
42
|
-
search.options = options
|
43
|
-
yield search if block_given?
|
44
|
-
search
|
74
|
+
def searchgasm_conditions(options = {})
|
75
|
+
Searchgasm::Conditions::Base.new(self, options)
|
45
76
|
end
|
46
77
|
|
47
|
-
def
|
48
|
-
|
49
|
-
yield search if block_given?
|
50
|
-
search
|
78
|
+
def searchgasm_searcher(options = {})
|
79
|
+
Searchgasm::Search::Base.new(self, options)
|
51
80
|
end
|
52
|
-
|
53
|
-
private
|
54
|
-
def sanitize_options_with_searchgasm(options = {})
|
55
|
-
return options unless BinaryLogic::Searchgasm::Search::Base.needed?(self, options)
|
56
|
-
searchgasm_searcher(options).sanitize
|
57
|
-
end
|
58
|
-
|
59
|
-
def searchgasm_conditions(options = {})
|
60
|
-
BinaryLogic::Searchgasm::Search::Conditions.new(self, options)
|
61
|
-
end
|
62
|
-
|
63
|
-
def searchgasm_searcher(options = {})
|
64
|
-
BinaryLogic::Searchgasm::Search::Base.new(self, options)
|
65
|
-
end
|
66
|
-
end
|
67
81
|
end
|
68
82
|
end
|
69
83
|
end
|
70
84
|
|
71
|
-
ActiveRecord::Base.send(:extend,
|
85
|
+
ActiveRecord::Base.send(:extend, Searchgasm::ActiveRecord::Base)
|
72
86
|
|
73
|
-
module
|
87
|
+
module ActiveRecord #:nodoc: all
|
74
88
|
class Base
|
75
89
|
class << self
|
76
90
|
alias_method_chain :calculate, :searchgasm
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition # :nodoc:
|
3
|
+
# = Conditions condition
|
4
|
+
#
|
5
|
+
# The base class for creating a condition. Your custom conditions should extend this class.
|
6
|
+
# See Searchgasm::Conditions::Base.register_condition on how to write your own condition.
|
7
|
+
class Base
|
8
|
+
include Utilities
|
9
|
+
|
10
|
+
attr_accessor :column, :klass
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Name of the condition inferred from the class name
|
15
|
+
def condition_name
|
16
|
+
name.split("::").last.gsub(/Condition$/, "").underscore
|
17
|
+
end
|
18
|
+
|
19
|
+
# I pass you a column you tell me what to call the condition. If you don't want to use this condition for the column
|
20
|
+
# just return nil
|
21
|
+
def name_for_column(column)
|
22
|
+
"#{column.name}_#{condition_name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Alias methods for the column condition.
|
26
|
+
def aliases_for_column(column)
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sane as name_for_column but for the class as a whole. For example the tree methods apply to the class as a whole and not
|
31
|
+
# specific columns. Any condition that applies to columns should probably return nil here.
|
32
|
+
def name_for_klass(klass)
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Alias methods for the klass condition
|
37
|
+
def aliases_for_klass(klass)
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
# A utility method for using in name_for_column. For example the keywords condition only applied to string columns, the great than condition doesnt.
|
42
|
+
def string_column?(column)
|
43
|
+
[:string, :text].include?(column.type)
|
44
|
+
end
|
45
|
+
|
46
|
+
# A utility method for using in name_for_column. For example you wouldn't want a string column to use the greater thann condition, but you would for an integer column.
|
47
|
+
def comparable_column?(column)
|
48
|
+
[:integer, :float, :decimal, :datetime, :timestamp, :time, :date].include?(column.type)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(klass, column = nil)
|
53
|
+
self.klass = klass
|
54
|
+
self.column = column.is_a?(String) ? klass.columns_hash[column] : column
|
55
|
+
end
|
56
|
+
|
57
|
+
# Allows nils to be meaninful values
|
58
|
+
def explicitly_set_value=(value)
|
59
|
+
@explicitly_set_value = value
|
60
|
+
end
|
61
|
+
|
62
|
+
# Need this if someone wants to actually use nil in a meaningful way
|
63
|
+
def explicitly_set_value?
|
64
|
+
@explicitly_set_value == true
|
65
|
+
end
|
66
|
+
|
67
|
+
# In most cases a blank value should be ignored, except for conditions like equals. A blank value is meaningful there, but a blank value for they keyswords condition is not.
|
68
|
+
def ignore_blanks?
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
# A convenience method for the name of the method for that specific column or klass
|
73
|
+
def name
|
74
|
+
column ? self.class.name_for_column(column) : self.class.name_for_klass(klass)
|
75
|
+
end
|
76
|
+
|
77
|
+
# A convenience method for the name of this condition
|
78
|
+
def condition_name
|
79
|
+
self.class.condition_name
|
80
|
+
end
|
81
|
+
|
82
|
+
# Quotes a column name properly for sql.
|
83
|
+
def quote_column_name(column_name)
|
84
|
+
klass.connection.quote_column_name(column_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
# A convenience method for using when writing your sql in to_conditions. This is the proper way to use a column name in a query for most databases
|
88
|
+
def quoted_column_name
|
89
|
+
quote_column_name(column.name)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Quotes a table name properly for sql
|
93
|
+
def quote_table_name(table_name)
|
94
|
+
klass.connection.quote_table_name(table_name)
|
95
|
+
end
|
96
|
+
|
97
|
+
# A convenience method for using when writing your sql in to_conditions. This is the proper way to use a table name in a query for most databases
|
98
|
+
def quoted_table_name
|
99
|
+
quote_table_name(klass.table_name)
|
100
|
+
end
|
101
|
+
|
102
|
+
# You should refrain from overwriting this method, it performs various tasks before callign your to_conditions method, allowing you to keep to_conditions simple.
|
103
|
+
def sanitize(alt_value = nil) # :nodoc:
|
104
|
+
return unless explicitly_set_value?
|
105
|
+
v = alt_value || value
|
106
|
+
if v.is_a?(Array) && !["equals", "does_not_equal"].include?(condition_name)
|
107
|
+
merge_conditions(*v.collect { |i| sanitize(i) })
|
108
|
+
else
|
109
|
+
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)
|
110
|
+
to_conditions(v)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# The value for the condition
|
115
|
+
def value
|
116
|
+
@value.is_a?(String) ? column.type_cast(@value) : @value
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sets the value for the condition, will ignore place if ignore_blanks?
|
120
|
+
def value=(v)
|
121
|
+
return if ignore_blanks? && v.blank?
|
122
|
+
self.explicitly_set_value = true
|
123
|
+
@value = v
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class BeginsWith < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless string_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
["#{column.name}_bw", "#{column.name}_starts_with", "#{column.name}_start"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_conditions(value)
|
16
|
+
["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "#{value}%"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class ChildOf < Tree
|
4
|
+
def to_conditions(value)
|
5
|
+
parent_association = klass.reflect_on_association(:parent)
|
6
|
+
foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
|
7
|
+
["#{quoted_table_name}.#{quote_column_name(foreign_key_name)} = ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class Contains < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless string_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
["#{column.name}_like", "#{column.name}_has"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_conditions(value)
|
16
|
+
["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}%"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class DescendantOf < Tree
|
4
|
+
def to_conditions(value)
|
5
|
+
# Wish I knew how to do this in SQL
|
6
|
+
root = (value.is_a?(klass) ? value : klass.find(value)) rescue return
|
7
|
+
strs = []
|
8
|
+
subs = []
|
9
|
+
all_children_ids(root).each do |child_id|
|
10
|
+
strs << "#{quoted_table_name}.#{quote_column_name(klass.primary_key)} = ?"
|
11
|
+
subs << child_id
|
12
|
+
end
|
13
|
+
[strs.join(" OR "), *subs]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def all_children_ids(record)
|
18
|
+
ids = record.children.collect { |child| child.send(klass.primary_key) }
|
19
|
+
record.children.each { |child| ids += all_children_ids(child) }
|
20
|
+
ids
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class DoesNotEqual < Base
|
4
|
+
class << self
|
5
|
+
def aliases_for_column(column)
|
6
|
+
["#{column.name}_is_not", "#{column.name}_not"]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def ignore_blanks?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_conditions(value)
|
15
|
+
# Delegate to equals and then change
|
16
|
+
condition = Equals.new(klass, column)
|
17
|
+
condition.value = value
|
18
|
+
|
19
|
+
sql = condition.sanitize
|
20
|
+
sql.gsub!(/ IS /, " IS NOT ")
|
21
|
+
sql.gsub!(/ BETWEEN /, " NOT BETWEEN ")
|
22
|
+
sql.gsub!(/ IN /, " NOT IN ")
|
23
|
+
sql.gsub!(/=/, "!=")
|
24
|
+
sql
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class EndsWith < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless string_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
["#{column.name}_ew", "#{column.name}_ends", "#{column.name}_end"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_conditions(value)
|
16
|
+
["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class Equals < Base
|
4
|
+
class << self
|
5
|
+
def aliases_for_column(column)
|
6
|
+
["#{column.name}", "#{column.name}_is"]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def ignore_blanks?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_conditions(value)
|
15
|
+
# Let ActiveRecord handle this
|
16
|
+
klass.send(:sanitize_sql_hash_for_conditions, {column.name => value})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class GreaterThan < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless comparable_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
column_names = [column.name]
|
12
|
+
column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
|
13
|
+
|
14
|
+
aliases = []
|
15
|
+
column_names.each { |column_name| aliases += ["#{column_name}_gt", "#{column_name}_after"] }
|
16
|
+
aliases
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_conditions(value)
|
21
|
+
["#{quoted_table_name}.#{quoted_column_name} > ?", value]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class GreaterThanOrEqualTo < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless comparable_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
["#{column.name}_gte", "#{column.name}_at_least"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_conditions(value)
|
16
|
+
["#{quoted_table_name}.#{quoted_column_name} >= ?", value]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class InclusiveDescendantOf < Tree
|
4
|
+
include Searchgasm::Utilities
|
5
|
+
|
6
|
+
def to_conditions(value)
|
7
|
+
condition = DescendantOf.new(klass, column)
|
8
|
+
condition.value = value
|
9
|
+
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)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class Keywords < Base
|
4
|
+
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
|
5
|
+
|
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}_kwords", "#{column.name}_kw"]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_conditions(value)
|
18
|
+
strs = []
|
19
|
+
subs = []
|
20
|
+
|
21
|
+
search_parts = value.gsub(/,/, " ").split(/ /).collect { |word| word.downcase.gsub(/[^[:alnum:]]/, ''); }.uniq.select { |word| !BLACKLISTED_WORDS.include?(word.downcase) && !word.blank? }
|
22
|
+
return if search_parts.blank?
|
23
|
+
|
24
|
+
search_parts.each do |search_part|
|
25
|
+
strs << "#{quoted_table_name}.#{quoted_column_name} LIKE ?"
|
26
|
+
subs << "%#{search_part}%"
|
27
|
+
end
|
28
|
+
|
29
|
+
[strs.join(" AND "), *subs]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class LessThan < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless comparable_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
column_names = [column.name]
|
12
|
+
column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
|
13
|
+
|
14
|
+
aliases = []
|
15
|
+
column_names.each { |column_name| aliases += ["#{column_name}_lt", "#{column_name}_before"] }
|
16
|
+
aliases
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_conditions(value)
|
21
|
+
["#{quoted_table_name}.#{quoted_column_name} < ?", value]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class LessThanOrEqualTo < Base
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
return unless comparable_column?(column)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases_for_column(column)
|
11
|
+
["#{column.name}_lte", "#{column.name}_at_most"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_conditions(value)
|
16
|
+
["#{quoted_table_name}.#{quoted_column_name} <= ?", value]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class SiblingOf < Tree
|
4
|
+
include Searchgasm::Utilities
|
5
|
+
|
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
|
+
parent_id = (value.is_a?(klass) ? value : klass.find(value)).send(foreign_key_name)
|
10
|
+
condition = ChildOf.new(klass, column)
|
11
|
+
condition.value = parent_id
|
12
|
+
merge_conditions(["#{quoted_table_name}.#{quote_column_name(klass.primary_key)} != ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)], condition.sanitize)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Condition
|
3
|
+
class Tree < Base # :nodoc:
|
4
|
+
class << self
|
5
|
+
def name_for_column(column)
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def name_for_klass(klass)
|
10
|
+
return unless klass.reflect_on_association(:parent) && klass.reflect_on_association(:children)
|
11
|
+
condition_name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|