searchgasm 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +1 -1
- data/README.mdown +45 -7
- data/lib/searchgasm/search/condition.rb +105 -0
- data/lib/searchgasm/search/condition_types/begins_with_condition.rb +1 -3
- data/lib/searchgasm/search/conditions.rb +13 -2
- data/lib/searchgasm/version.rb +1 -1
- data/lib/searchgasm.rb +1 -1
- data/searchgasm.gemspec +4 -4
- data/test/test_searchgasm_base.rb +4 -0
- data/test/test_searchgasm_conditions.rb +16 -0
- metadata +3 -3
- data/lib/searchgasm/search/condition_types/condition.rb +0 -107
data/CHANGELOG
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
v0.9.5. Enhanced searching with conditions only. Updated read me to include example on adding your own conditions.
|
2
|
+
|
1
3
|
v0.9.4. Cleaned up search methods, removed reset! method for base and conditions.
|
2
4
|
|
3
5
|
v0.9.3. Changed structure of conditions to have their own class. Making it easier to add your own conditions.
|
data/Manifest
CHANGED
@@ -3,9 +3,9 @@ init.rb
|
|
3
3
|
lib/searchgasm/active_record/associations.rb
|
4
4
|
lib/searchgasm/active_record/base.rb
|
5
5
|
lib/searchgasm/search/base.rb
|
6
|
+
lib/searchgasm/search/condition.rb
|
6
7
|
lib/searchgasm/search/condition_types/begins_with_condition.rb
|
7
8
|
lib/searchgasm/search/condition_types/child_of_condition.rb
|
8
|
-
lib/searchgasm/search/condition_types/condition.rb
|
9
9
|
lib/searchgasm/search/condition_types/contains_condition.rb
|
10
10
|
lib/searchgasm/search/condition_types/descendant_of_condition.rb
|
11
11
|
lib/searchgasm/search/condition_types/does_not_equal_condition.rb
|
data/README.mdown
CHANGED
@@ -65,7 +65,7 @@ Now go into your console and try out any of these example with your own models.
|
|
65
65
|
|
66
66
|
Instead of using the "all" method you could use any search method: first, find(:all), find(:first), count, sum, average, etc
|
67
67
|
|
68
|
-
## Detailed Example w/ object based searching (great for form\_for
|
68
|
+
## Detailed Example w/ object based searching (great for form\_for or fields\_for)
|
69
69
|
|
70
70
|
# Instantiate
|
71
71
|
@search = User.new_search(
|
@@ -103,12 +103,12 @@ Take the @search object and pass it right into form\_for or fields\_for (see abo
|
|
103
103
|
|
104
104
|
Using the object from above:
|
105
105
|
|
106
|
-
search.average('id')
|
107
|
-
search.count
|
108
|
-
search.maximum('id')
|
109
|
-
search.minimum('id')
|
110
|
-
search.sum('id')
|
111
|
-
search.calculate(:sum, 'id') # any of the above calculations
|
106
|
+
@search.average('id')
|
107
|
+
@search.count
|
108
|
+
@search.maximum('id')
|
109
|
+
@search.minimum('id')
|
110
|
+
@search.sum('id')
|
111
|
+
@search.calculate(:sum, 'id') # any of the above calculations
|
112
112
|
|
113
113
|
Or do it from your model:
|
114
114
|
|
@@ -250,6 +250,44 @@ Some of these conditions come with aliases, so you have your choice how to call
|
|
250
250
|
|
251
251
|
You will notice above there is "contains" and "keywords". The difference is that "keywords" is an enhanced search. It acts like a real keyword search. It finds those keywords, in any order, and blacklists meaningless words such as "and", "the", etc. "contains" finds the EXACT string in the column you are searching, spaces and all.
|
252
252
|
|
253
|
+
### Roll your own conditions
|
254
|
+
|
255
|
+
I didn't include this function because its MySQL specific, and it's probably rarely used, but MySQL supports a "SOUNDS LIKE" function.
|
256
|
+
|
257
|
+
I want to use it, so let's add it:
|
258
|
+
|
259
|
+
# config/initializers/searchgasm.rb
|
260
|
+
# Actual function for MySQL databases only
|
261
|
+
class SoundsLikeCondition < Searchgasm::Condition
|
262
|
+
class << self
|
263
|
+
# I pass you the column, you tell me what you want the method to be called.
|
264
|
+
# If you don't want to add this condition for that column, return nil
|
265
|
+
# It defaults to "#{column.name}_sounds_like". So if thats what you want you don't even need to do this.
|
266
|
+
def name_for_column(column)
|
267
|
+
super
|
268
|
+
end
|
269
|
+
|
270
|
+
# Only do this if you want aliases for your condition
|
271
|
+
def aliases_for_column(column)
|
272
|
+
["#{column.name}_sounds", "#{column.name}_similar_to"]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def to_conditions(value)
|
277
|
+
["#{quoted_table_name}.#{quoted_column_name} SOUNDS LIKE ?", value]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
Searchgasm::Condition.register_condition(SoundsLikeCondition)
|
282
|
+
|
283
|
+
Now test it out:
|
284
|
+
|
285
|
+
search = User.new_search
|
286
|
+
search.first_name_sounds_like = "Ben"
|
287
|
+
search.all # will return any user that has a first name that sounds like "Ben"
|
288
|
+
|
289
|
+
Pretty nifty, huh? You can create any condition ultimately creating any SQL you want. The sky is the limit.
|
290
|
+
|
253
291
|
## Credits
|
254
292
|
|
255
293
|
Author: [Ben Johnson](http://github.com/binarylogic) of [Binary Logic](http://www.binarylogic.com)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module BinaryLogic
|
2
|
+
module Searchgasm
|
3
|
+
module Search
|
4
|
+
class Condition
|
5
|
+
include Utilities
|
6
|
+
|
7
|
+
attr_accessor :column, :klass
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def condition_name
|
12
|
+
name.split("::").last.scan(/(.*)Condition/)[0][0].underscore
|
13
|
+
end
|
14
|
+
|
15
|
+
def name_for_column(column)
|
16
|
+
"#{column.name}_#{condition_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def aliases_for_column(column)
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
|
23
|
+
def name_for_klass(klass)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def aliases_for_klass(klass)
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def string_column?(column)
|
32
|
+
[:string, :text].include?(column.type)
|
33
|
+
end
|
34
|
+
|
35
|
+
def comparable_column?(column)
|
36
|
+
[:integer, :float, :decimal, :datetime, :timestamp, :time, :date].include?(column.type)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(klass, column = nil)
|
41
|
+
self.klass = klass
|
42
|
+
self.column = column.is_a?(String) ? klass.columns_hash[column] : column
|
43
|
+
end
|
44
|
+
|
45
|
+
def explicitly_set_value=(value)
|
46
|
+
@explicitly_set_value = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Need this if someone wants to actually use nil in a meaningful way
|
50
|
+
def explicitly_set_value?
|
51
|
+
@explicitly_set_value == true
|
52
|
+
end
|
53
|
+
|
54
|
+
def ignore_blanks?
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def name
|
59
|
+
column ? self.class.name_for_column(column) : self.class.name_for_klass(klass)
|
60
|
+
end
|
61
|
+
|
62
|
+
def condition_name
|
63
|
+
self.class.condition_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def quote_column_name(column_name)
|
67
|
+
klass.connection.quote_column_name(column_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def quoted_column_name
|
71
|
+
quote_column_name(column.name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def quote_table_name(table_name)
|
75
|
+
klass.connection.quote_table_name(table_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def quoted_table_name
|
79
|
+
quote_table_name(klass.table_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def sanitize(alt_value = nil)
|
83
|
+
return unless explicitly_set_value?
|
84
|
+
v = alt_value || value
|
85
|
+
if v.is_a?(Array) && !["equals", "does_not_equal"].include?(condition_name)
|
86
|
+
merge_conditions(*v.collect { |i| sanitize(i) })
|
87
|
+
else
|
88
|
+
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)
|
89
|
+
to_conditions(v)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def value
|
94
|
+
@value.is_a?(String) ? column.type_cast(@value) : @value
|
95
|
+
end
|
96
|
+
|
97
|
+
def value=(v)
|
98
|
+
return if ignore_blanks? && v.blank?
|
99
|
+
self.explicitly_set_value = true
|
100
|
+
@value = v
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -8,8 +8,8 @@ module BinaryLogic
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
def register_condition(klass)
|
11
|
-
raise(ArgumentError, "You can only register conditions that extend BinaryLogic::Searchgasm::Search::ConditionTypes::Condition") unless klass.ancestors.include?(
|
12
|
-
conditions << klass
|
11
|
+
raise(ArgumentError, "You can only register conditions that extend BinaryLogic::Searchgasm::Search::ConditionTypes::Condition") unless klass.ancestors.include?(Condition)
|
12
|
+
conditions << klass unless conditions.include?(klass)
|
13
13
|
end
|
14
14
|
|
15
15
|
def conditions
|
@@ -25,6 +25,17 @@ module BinaryLogic
|
|
25
25
|
self.value = init_values
|
26
26
|
end
|
27
27
|
|
28
|
+
# Setup methods for searching
|
29
|
+
[:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum].each do |method|
|
30
|
+
class_eval <<-end_eval
|
31
|
+
def #{method}(*args)
|
32
|
+
self.value = args.extract_options!
|
33
|
+
args << {:conditions => sanitize}
|
34
|
+
klass.#{method}(*args)
|
35
|
+
end
|
36
|
+
end_eval
|
37
|
+
end
|
38
|
+
|
28
39
|
def assert_valid_values(values)
|
29
40
|
keys = condition_names.collect { |condition_name| condition_name.to_sym }
|
30
41
|
keys += klass.reflect_on_all_associations.collect { |association| association.name }
|
data/lib/searchgasm/version.rb
CHANGED
data/lib/searchgasm.rb
CHANGED
@@ -5,11 +5,11 @@ require "searchgasm/active_record/associations"
|
|
5
5
|
|
6
6
|
require "searchgasm/version"
|
7
7
|
require "searchgasm/search/utilities"
|
8
|
+
require "searchgasm/search/condition"
|
8
9
|
require "searchgasm/search/conditions"
|
9
10
|
require "searchgasm/search/base"
|
10
11
|
|
11
12
|
# Regular conidtion types
|
12
|
-
require "searchgasm/search/condition_types/condition"
|
13
13
|
require "searchgasm/search/condition_types/begins_with_condition"
|
14
14
|
require "searchgasm/search/condition_types/contains_condition"
|
15
15
|
require "searchgasm/search/condition_types/does_not_equal_condition"
|
data/searchgasm.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Searchgasm-0.9.
|
2
|
+
# Gem::Specification for Searchgasm-0.9.5
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
--- !ruby/object:Gem::Specification
|
6
6
|
name: searchgasm
|
7
7
|
version: !ruby/object:Gem::Version
|
8
|
-
version: 0.9.
|
8
|
+
version: 0.9.5
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Ben Johnson of Binary Logic
|
@@ -56,9 +56,9 @@ extra_rdoc_files:
|
|
56
56
|
- lib/searchgasm/active_record/associations.rb
|
57
57
|
- lib/searchgasm/active_record/base.rb
|
58
58
|
- lib/searchgasm/search/base.rb
|
59
|
+
- lib/searchgasm/search/condition.rb
|
59
60
|
- lib/searchgasm/search/condition_types/begins_with_condition.rb
|
60
61
|
- lib/searchgasm/search/condition_types/child_of_condition.rb
|
61
|
-
- lib/searchgasm/search/condition_types/condition.rb
|
62
62
|
- lib/searchgasm/search/condition_types/contains_condition.rb
|
63
63
|
- lib/searchgasm/search/condition_types/descendant_of_condition.rb
|
64
64
|
- lib/searchgasm/search/condition_types/does_not_equal_condition.rb
|
@@ -83,9 +83,9 @@ files:
|
|
83
83
|
- lib/searchgasm/active_record/associations.rb
|
84
84
|
- lib/searchgasm/active_record/base.rb
|
85
85
|
- lib/searchgasm/search/base.rb
|
86
|
+
- lib/searchgasm/search/condition.rb
|
86
87
|
- lib/searchgasm/search/condition_types/begins_with_condition.rb
|
87
88
|
- lib/searchgasm/search/condition_types/child_of_condition.rb
|
88
|
-
- lib/searchgasm/search/condition_types/condition.rb
|
89
89
|
- lib/searchgasm/search/condition_types/contains_condition.rb
|
90
90
|
- lib/searchgasm/search/condition_types/descendant_of_condition.rb
|
91
91
|
- lib/searchgasm/search/condition_types/does_not_equal_condition.rb
|
@@ -76,6 +76,10 @@ class TestSearchgasmBase < Test::Unit::TestCase
|
|
76
76
|
|
77
77
|
search.conditions = {:name_like => "Binary"}
|
78
78
|
assert_kind_of BinaryLogic::Searchgasm::Search::Conditions, search.conditions
|
79
|
+
|
80
|
+
conditions = BinaryLogic::Searchgasm::Search::Conditions.new(Account, :id_greater_than => 8)
|
81
|
+
search.conditions = conditions
|
82
|
+
assert_equal conditions, search.conditions
|
79
83
|
end
|
80
84
|
|
81
85
|
def test_include
|
@@ -140,6 +140,22 @@ class TestSearchgasmConditions < Test::Unit::TestCase
|
|
140
140
|
assert_equal scope2, conditions.scope
|
141
141
|
end
|
142
142
|
|
143
|
+
def test_searching
|
144
|
+
conditions = BinaryLogic::Searchgasm::Search::Conditions.new(Account)
|
145
|
+
conditions.name_contains = "Binary"
|
146
|
+
assert_equal Account.find(1, 3), conditions.all
|
147
|
+
assert_equal Account.find(1, 3), conditions.find(:all)
|
148
|
+
assert_equal Account.find(1), conditions.first
|
149
|
+
assert_equal Account.find(1), conditions.find(:first)
|
150
|
+
assert_equal 2, conditions.average('id')
|
151
|
+
assert_equal 2, conditions.calculate(:avg, 'id')
|
152
|
+
assert_equal 3, conditions.calculate(:max, 'id')
|
153
|
+
assert_equal 2, conditions.count
|
154
|
+
assert_equal 3, conditions.maximum('id')
|
155
|
+
assert_equal 1, conditions.minimum('id')
|
156
|
+
assert_equal 4, conditions.sum('id')
|
157
|
+
end
|
158
|
+
|
143
159
|
def test_protection
|
144
160
|
assert_raise(ArgumentError) { Account.new_conditions("(DELETE FROM users)") }
|
145
161
|
assert_nothing_raised { Account.build_conditions!("(DELETE FROM users)") }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchgasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -53,9 +53,9 @@ extra_rdoc_files:
|
|
53
53
|
- lib/searchgasm/active_record/associations.rb
|
54
54
|
- lib/searchgasm/active_record/base.rb
|
55
55
|
- lib/searchgasm/search/base.rb
|
56
|
+
- lib/searchgasm/search/condition.rb
|
56
57
|
- lib/searchgasm/search/condition_types/begins_with_condition.rb
|
57
58
|
- lib/searchgasm/search/condition_types/child_of_condition.rb
|
58
|
-
- lib/searchgasm/search/condition_types/condition.rb
|
59
59
|
- lib/searchgasm/search/condition_types/contains_condition.rb
|
60
60
|
- lib/searchgasm/search/condition_types/descendant_of_condition.rb
|
61
61
|
- lib/searchgasm/search/condition_types/does_not_equal_condition.rb
|
@@ -80,9 +80,9 @@ files:
|
|
80
80
|
- lib/searchgasm/active_record/associations.rb
|
81
81
|
- lib/searchgasm/active_record/base.rb
|
82
82
|
- lib/searchgasm/search/base.rb
|
83
|
+
- lib/searchgasm/search/condition.rb
|
83
84
|
- lib/searchgasm/search/condition_types/begins_with_condition.rb
|
84
85
|
- lib/searchgasm/search/condition_types/child_of_condition.rb
|
85
|
-
- lib/searchgasm/search/condition_types/condition.rb
|
86
86
|
- lib/searchgasm/search/condition_types/contains_condition.rb
|
87
87
|
- lib/searchgasm/search/condition_types/descendant_of_condition.rb
|
88
88
|
- lib/searchgasm/search/condition_types/does_not_equal_condition.rb
|
@@ -1,107 +0,0 @@
|
|
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
|