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 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 for fields\_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
@@ -23,6 +23,4 @@ module BinaryLogic
23
23
  Conditions.register_condition(ConditionTypes::BeginsWithCondition)
24
24
  end
25
25
  end
26
- end
27
-
28
- BinaryLogic::Searchgasm::Search::Conditions.register_condition(BinaryLogic::Searchgasm::Search::ConditionTypes::BeginsWithCondition)
26
+ 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?(ConditionTypes::Condition)
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 }
@@ -68,7 +68,7 @@ module BinaryLogic
68
68
 
69
69
  MAJOR = 0
70
70
  MINOR = 9
71
- TINY = 4
71
+ TINY = 5
72
72
 
73
73
  # The current version as a Version instance
74
74
  CURRENT = new(MAJOR, MINOR, TINY)
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.4
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.4
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
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