searchgasm 0.9.6 → 0.9.7

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.
Files changed (66) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +34 -21
  3. data/{README.mdown → README.rdoc} +96 -63
  4. data/Rakefile +1 -1
  5. data/examples/README.rdoc +4 -0
  6. data/lib/searchgasm/active_record/associations.rb +40 -42
  7. data/lib/searchgasm/active_record/base.rb +75 -61
  8. data/lib/searchgasm/condition/base.rb +127 -0
  9. data/lib/searchgasm/condition/begins_with.rb +20 -0
  10. data/lib/searchgasm/condition/child_of.rb +11 -0
  11. data/lib/searchgasm/condition/contains.rb +20 -0
  12. data/lib/searchgasm/condition/descendant_of.rb +24 -0
  13. data/lib/searchgasm/condition/does_not_equal.rb +28 -0
  14. data/lib/searchgasm/condition/ends_with.rb +20 -0
  15. data/lib/searchgasm/condition/equals.rb +20 -0
  16. data/lib/searchgasm/condition/greater_than.rb +25 -0
  17. data/lib/searchgasm/condition/greater_than_or_equal_to.rb +20 -0
  18. data/lib/searchgasm/condition/inclusive_descendant_of.rb +13 -0
  19. data/lib/searchgasm/condition/keywords.rb +33 -0
  20. data/lib/searchgasm/condition/less_than.rb +25 -0
  21. data/lib/searchgasm/condition/less_than_or_equal_to.rb +20 -0
  22. data/lib/searchgasm/condition/sibling_of.rb +16 -0
  23. data/lib/searchgasm/condition/tree.rb +16 -0
  24. data/lib/searchgasm/conditions/base.rb +221 -0
  25. data/lib/searchgasm/conditions/protection.rb +30 -0
  26. data/lib/searchgasm/config.rb +137 -0
  27. data/lib/searchgasm/helpers/form_helper.rb +159 -0
  28. data/lib/searchgasm/helpers/search_helper.rb +178 -0
  29. data/lib/searchgasm/helpers/utilities_helper.rb +125 -0
  30. data/lib/searchgasm/search/base.rb +73 -179
  31. data/lib/searchgasm/search/conditions.rb +42 -166
  32. data/lib/searchgasm/search/ordering.rb +149 -0
  33. data/lib/searchgasm/search/pagination.rb +69 -0
  34. data/lib/searchgasm/search/protection.rb +61 -0
  35. data/lib/searchgasm/utilities.rb +30 -0
  36. data/lib/searchgasm/version.rb +44 -47
  37. data/lib/searchgasm.rb +57 -21
  38. data/searchgasm.gemspec +71 -46
  39. data/test/test_active_record_associations.rb +1 -1
  40. data/test/test_active_record_base.rb +4 -4
  41. data/test/test_condition.rb +143 -0
  42. data/test/{test_searchgasm_conditions.rb → test_conditions_base.rb} +43 -33
  43. data/test/test_search_base.rb +189 -0
  44. data/test/test_search_ordering.rb +91 -0
  45. data/test/test_search_pagination.rb +56 -0
  46. data/test/test_search_protection.rb +35 -0
  47. metadata +70 -45
  48. data/lib/searchgasm/search/condition.rb +0 -105
  49. data/lib/searchgasm/search/condition_types/begins_with_condition.rb +0 -26
  50. data/lib/searchgasm/search/condition_types/child_of_condition.rb +0 -17
  51. data/lib/searchgasm/search/condition_types/contains_condition.rb +0 -26
  52. data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +0 -30
  53. data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +0 -34
  54. data/lib/searchgasm/search/condition_types/ends_with_condition.rb +0 -26
  55. data/lib/searchgasm/search/condition_types/equals_condition.rb +0 -26
  56. data/lib/searchgasm/search/condition_types/greater_than_condition.rb +0 -31
  57. data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +0 -26
  58. data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +0 -19
  59. data/lib/searchgasm/search/condition_types/keywords_condition.rb +0 -39
  60. data/lib/searchgasm/search/condition_types/less_than_condition.rb +0 -31
  61. data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +0 -26
  62. data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +0 -22
  63. data/lib/searchgasm/search/condition_types/tree_condition.rb +0 -20
  64. data/lib/searchgasm/search/utilities.rb +0 -34
  65. data/test/test_searchgasm_base.rb +0 -185
  66. data/test/test_searchgasm_condition_types.rb +0 -143
@@ -0,0 +1,221 @@
1
+ module Searchgasm
2
+ module Conditions # :nodoc:
3
+ # = Conditions
4
+ #
5
+ # Represents a collection of conditions and performs various tasks on that collection. For information on each condition see Searchgasm::Condition.
6
+ # Each condition has its own file and class and the source for each condition is pretty self explanatory.
7
+ class Base
8
+ include Utilities
9
+
10
+ attr_accessor :klass, :relationship_name, :scope
11
+
12
+ class << self
13
+ # Registers a condition as an available condition for a column or a class.
14
+ #
15
+ # === Example
16
+ #
17
+ # config/initializers/searchgasm.rb
18
+ # # Actual function for MySQL databases only
19
+ # class SoundsLike < Searchgasm::Condition::Base
20
+ # class << self
21
+ # # I pass you the column, you tell me what you want the method to be called.
22
+ # # If you don't want to add this condition for that column, return nil
23
+ # # It defaults to "#{column.name}_sounds_like". So if thats what you want you don't even need to do this.
24
+ # def name_for_column(column)
25
+ # super
26
+ # end
27
+ #
28
+ # # Only do this if you want aliases for your condition
29
+ # def aliases_for_column(column)
30
+ # ["#{column.name}_sounds", "#{column.name}_similar_to"]
31
+ # end
32
+ # end
33
+ #
34
+ # # You can return an array or a string. NOT a hash, because all of these conditions
35
+ # # need to eventually get merged together. The array or string can be anything you would put in
36
+ # # the :conditions option for ActiveRecord::Base.find()
37
+ # def to_conditions(value)
38
+ # ["#{quoted_table_name}.#{quoted_column_name} SOUNDS LIKE ?", value]
39
+ # end
40
+ # end
41
+ #
42
+ # Searchgasm::Seearch::Conditions.register_condition(SoundsLikeCondition)
43
+ def register_condition(klass)
44
+ raise(ArgumentError, "You can only register conditions that extend Searchgasm::Condition::Base") unless klass.ancestors.include?(Searchgasm::Condition::Base)
45
+ conditions << klass unless conditions.include?(klass)
46
+ end
47
+
48
+ # A list of available condition type classes
49
+ def conditions
50
+ @@conditions ||= []
51
+ end
52
+
53
+ def needed?(klass, conditions) # :nodoc:
54
+ if conditions.is_a?(Hash)
55
+ conditions.stringify_keys.keys.each do |condition|
56
+ return true unless klass.column_names.include?(condition)
57
+ end
58
+ end
59
+
60
+ false
61
+ end
62
+ end
63
+
64
+ def initialize(klass, init_conditions = {})
65
+ self.klass = klass
66
+ add_klass_conditions!
67
+ add_column_conditions!
68
+ add_associations!
69
+ self.conditions = init_conditions
70
+ end
71
+
72
+ # Setup methods for searching
73
+ [:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum].each do |method|
74
+ class_eval <<-"end_eval", __FILE__, __LINE__
75
+ def #{method}(*args)
76
+ self.conditions = args.extract_options!
77
+ args << {:conditions => sanitize}
78
+ klass.#{method}(*args)
79
+ end
80
+ end_eval
81
+ end
82
+
83
+ # A list of includes to use when searching, includes relationships
84
+ def includes
85
+ i = []
86
+ associations.each do |association|
87
+ association_includes = association.includes
88
+ i << (association_includes.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_includes})
89
+ end
90
+ i.blank? ? nil : (i.size == 1 ? i.first : i)
91
+ end
92
+
93
+ # Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
94
+ def sanitize
95
+ conditions = merge_conditions(*objects.collect { |object| object.sanitize })
96
+ return scope if conditions.blank?
97
+ merge_conditions(conditions, scope)
98
+ end
99
+
100
+ # Allows you to set the conditions via a hash. If you do not pass a hash it will set scope instead, so that you can continue to add conditions and ultimately
101
+ # merge it all together at the end.
102
+ def conditions=(conditions)
103
+ case conditions
104
+ when Hash
105
+ assert_valid_conditions(conditions)
106
+ remove_conditions_from_protected_assignement(conditions).each { |condition, value| send("#{condition}=", value) }
107
+ else
108
+ self.scope = conditions
109
+ end
110
+ end
111
+
112
+ # All of the active conditions (conditions that have been set)
113
+ def conditions
114
+ conditions_hash = {}
115
+ objects.each do |object|
116
+ case object
117
+ when self.class
118
+ relationship_conditions = object.conditions
119
+ next if relationship_conditions.blank?
120
+ conditions_hash[object.relationship_name.to_sym] = relationship_conditions
121
+ else
122
+ next unless object.explicitly_set_value?
123
+ conditions_hash[object.name.to_sym] = object.value
124
+ end
125
+ end
126
+ conditions_hash
127
+ end
128
+
129
+ private
130
+ def add_associations!
131
+ klass.reflect_on_all_associations.each do |association|
132
+ self.class.class_eval <<-"end_eval", __FILE__, __LINE__
133
+ def #{association.name}
134
+ if @#{association.name}.nil?
135
+ @#{association.name} = self.class.new(#{association.class_name})
136
+ @#{association.name}.relationship_name = "#{association.name}"
137
+ objects << @#{association.name}
138
+ end
139
+ @#{association.name}
140
+ end
141
+
142
+ def #{association.name}=(conditions); #{association.name}.conditions = conditions; end
143
+ def reset_#{association.name}!; objects.delete(#{association.name}); @#{association.name} = nil; end
144
+ end_eval
145
+ end
146
+ end
147
+
148
+ def add_column_conditions!
149
+ klass.columns.each do |column|
150
+ self.class.conditions.each do |condition_klass|
151
+ name = condition_klass.name_for_column(column)
152
+ next if name.blank?
153
+ add_condition!(condition_klass, name, column)
154
+ condition_klass.aliases_for_column(column).each { |alias_name| add_condition_alias!(alias_name, name) }
155
+ end
156
+ end
157
+ end
158
+
159
+ def add_condition!(condition, name, column = nil)
160
+ condition_names << name
161
+ self.class.class_eval <<-"end_eval", __FILE__, __LINE__
162
+ def #{name}_object
163
+ if @#{name}.nil?
164
+ @#{name} = #{condition.name}.new(klass#{column.nil? ? "" : ", \"#{column.name}\""})
165
+ objects << @#{name}
166
+ end
167
+ @#{name}
168
+ end
169
+
170
+ def #{name}; #{name}_object.value; end
171
+ def #{name}=(value); #{name}_object.value = value; end
172
+ def reset_#{name}!; objects.delete(#{name}_object); @#{name} = nil; end
173
+ end_eval
174
+ end
175
+
176
+ def add_condition_alias!(alias_name, name)
177
+ condition_names << alias_name
178
+ self.class.class_eval do
179
+ alias_method alias_name, name
180
+ alias_method "#{alias_name}=", "#{name}="
181
+ end
182
+ end
183
+
184
+ def add_klass_conditions!
185
+ self.class.conditions.each do |condition|
186
+ name = condition.name_for_klass(klass)
187
+ next if name.blank?
188
+ add_condition!(condition, name)
189
+ condition.aliases_for_klass(klass).each { |alias_name| add_condition_alias!(alias_name, name) }
190
+ end
191
+ end
192
+
193
+ def assert_valid_conditions(conditions)
194
+ keys = condition_names.collect { |condition_name| condition_name.to_sym }
195
+ keys += klass.reflect_on_all_associations.collect { |association| association.name }
196
+ conditions.symbolize_keys.assert_valid_keys(keys)
197
+ end
198
+
199
+ def associations
200
+ objects.select { |object| object.is_a?(self.class) }
201
+ end
202
+
203
+ def condition_names
204
+ @condition_names ||= []
205
+ end
206
+
207
+ def objects
208
+ @objects ||= []
209
+ end
210
+
211
+ def remove_conditions_from_protected_assignement(conditions)
212
+ return conditions if klass.accessible_conditions.nil? && klass.protected_conditions.nil?
213
+ if klass.accessible_conditions
214
+ conditions.reject { |condition, value| !klass.accessible_conditions.include?(condition.to_s) }
215
+ elsif klass.protected_conditions
216
+ conditions.reject { |condition, value| klass.protected_conditions.include?(condition.to_s) }
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,30 @@
1
+ module Searchgasm
2
+ module Conditions
3
+ # = Conditions Protection
4
+ #
5
+ # Adds protection from SQL injections. Just set protect = true and it will limit what kind of conditions it will accept.
6
+ module Protection
7
+ def self.included(klass)
8
+ klass.class_eval do
9
+ attr_accessor :protect
10
+ alias_method_chain :conditions=, :protection
11
+ end
12
+ end
13
+
14
+ def conditions_with_protection=(conditions)
15
+ unless conditions.is_a?(Hash)
16
+ if protect?
17
+ return if conditions.blank?
18
+ raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected")
19
+ end
20
+ end
21
+
22
+ self.conditions_without_protection = conditions
23
+ end
24
+
25
+ def protect?
26
+ protect == true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,137 @@
1
+ module Searchgasm
2
+ # = Config
3
+ # Adds default configuration for all of searchgasm. Just make sure you set your config before you use Searchgasm.
4
+ # For rails the best place to do this is in config/initializers. Create a file in there called searchgasm.rb with the following content:
5
+ #
6
+ # === Example
7
+ #
8
+ # # config/iniitializers/searchgasm.rb
9
+ # Searchgasm::Config.configure do |config|
10
+ # config.you_option_here = your_value # see methods below
11
+ # end
12
+ class Config
13
+ class << self
14
+ # Convenience method for setting configuration
15
+ # See example at top of class.
16
+ def configure
17
+ yield self
18
+ end
19
+
20
+ def asc_indicator # :nodoc:
21
+ @asc_indicator ||= "&nbsp;&#9650;"
22
+ end
23
+
24
+ # The indicator that is used when the sort of a column is ascending
25
+ #
26
+ # * <tt>Default:</tt> &nbsp;&#9650;
27
+ # * <tt>Accepts:</tt> String or a Proc.
28
+ #
29
+ # === Examples
30
+ #
31
+ # config.asc_indicator = "(ASC)"
32
+ # config.asc_indicator = Proc.new { |template| template.image_tag("asc.jpg") }
33
+ def asc_indicator=(value)
34
+ @asc_indicator = value
35
+ end
36
+
37
+ def desc_indicator # :nodoc:
38
+ @desc_indicator ||= "&nbsp;&#9660;"
39
+ end
40
+
41
+ # See asc_indicator=
42
+ def desc_indicator=(value)
43
+ @desc_indicator = value
44
+ end
45
+
46
+ def pages_type # :nodoc:
47
+ @pages_type ||= :select
48
+ end
49
+
50
+ # The default value for the :type option in the pages helper.
51
+ #
52
+ # * <tt>Default:</tt> :select
53
+ # * <tt>Accepts:</tt> :select, :links
54
+ def pages_type=(value)
55
+ @pages_type = value.to_sym
56
+ end
57
+
58
+ def per_page_choices # :nodoc:
59
+ @per_page_choices ||= [10, 25, 50, 100, 150, 200, nil]
60
+ end
61
+
62
+ # The choices used in the per_page helper
63
+ #
64
+ # * <tt>Default:</tt> [10, 25, 50, 100, 150, 200, nil]
65
+ # * <tt>Accepts:</tt> Array
66
+ #
67
+ # nil means "Show all"
68
+ def per_page_choices=(value)
69
+ @per_page_choices = value
70
+ end
71
+
72
+ def per_page_type # :nodoc:
73
+ @per_page_type ||= :select
74
+ end
75
+
76
+ # The default value for the :type option in the per_page helper.
77
+ #
78
+ # * <tt>Default:</tt> :select
79
+ # * <tt>Accepts:</tt> :select, :links
80
+ def per_page_type=(value)
81
+ @per_page_type = value.to_sym
82
+ end
83
+
84
+ def hidden_fields # :nodoc:
85
+ @hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page])
86
+ end
87
+
88
+ # Which hidden fields to automatically include when creating a form with a Searchgasm object. See Searchgasm::Helpers::FormHelper for more info.
89
+ #
90
+ # * <tt>Default:</tt> [:order_by, :order_as, :per_page]
91
+ # * <tt>Accepts:</tt> Array, nil, false
92
+ def hidden_fields=(value)
93
+ @hidden_fields = value
94
+ end
95
+
96
+ def remote_helpers # :nodoc:
97
+ @remote_helpers ||= false
98
+ end
99
+
100
+ # Tells all helpers to default to using remote links (AJAX) instead of normal links.
101
+ #
102
+ # * <tt>Default:</tt> false
103
+ # * <tt>Accepts:</tt> Boolean
104
+ #
105
+ # nil means "Show all"
106
+ def remote_helpers=(value)
107
+ @remote_helpers = value
108
+ end
109
+
110
+ def remote_helpers? # :nodoc:
111
+ remote_helpers == true
112
+ end
113
+
114
+ def search_scope # :nodoc:
115
+
116
+ end
117
+
118
+ def search_obj_name # :nodoc:
119
+ @search_obj_name ||= :@search
120
+ end
121
+
122
+ # The instance variable name you use to assign your search to. This allows the helpers to grab your Searchgasm object without having
123
+ # to specify it everywhere.
124
+ #
125
+ # * <tt>Default:</tt> :@search
126
+ # * <tt>Accepts:</tt> String or Symbol.
127
+ #
128
+ # === Examples
129
+ #
130
+ # config.search_obj_name = :@search
131
+ # config.search_obj_name = "@search"
132
+ def search_obj_name=(value)
133
+ @search_obj_name = value
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,159 @@
1
+ module Searchgasm
2
+ module Helpers
3
+ # = Form Helper
4
+ #
5
+ # Enables you to use form_for and fields_for just like you do with an ActiveRecord object.
6
+ #
7
+ # === Examples
8
+ #
9
+ # Let's assume @search is searching Address
10
+ #
11
+ # form_for(@search) # is equivalent to form_for(:search, @search, :url => addresses_path)
12
+ # form_for([@current_user, @search]) # is equivalent to form_for(:search, @search, :url => user_addresses_path(@current_user))
13
+ # form_for([:admin, @search]) # is equivalent to form_for(:search, @search, :url => admin_addresses_path)
14
+ # form_for(:search, @search, :url => whatever_path)
15
+ #
16
+ # The goal was to mimic ActiveRecord. You can also pass a Searchgasm::Conditions::Base object as well and it will function the same way.
17
+ #
18
+ # === Automatic hidden fields generation
19
+ #
20
+ # If you pass a Searchgasm::Search::Base object it automatically adds the :order_by, :order_as, and :per_page hidden fields. This is done so that when someone
21
+ # creates a new search, their options are remembered. It keeps the search consisten and is much more user friendly. If you want to override this you can pass the
22
+ # following options. Or you can set this up in your configuration, see Searchgasm::Config for more details.
23
+ #
24
+ # === Options
25
+ #
26
+ # * <tt>:hidden_fields</tt> --- Array, a list of hidden fields to include. Defaults to [:order_by, :order_as, :per_page]. Pass false, nil, or a blank array to not include any.
27
+ module FormHelper
28
+ module Shared # :nodoc:
29
+ private
30
+ def searchgasm_object?(object)
31
+ object.is_a?(Search::Base) || object.is_a?(Conditions::Base)
32
+ end
33
+
34
+ def find_searchgasm_object(args)
35
+ case args.first
36
+ when String, Symbol
37
+ search_object = searchgasm_object?(args[1]) ? args[1] : instance_variable_get("@#{args.first}")
38
+ when Array
39
+ search_object = args.first.last
40
+ else
41
+ search_object = args.first
42
+ end
43
+
44
+ searchgasm_object?(search_object) ? search_object : nil
45
+ end
46
+
47
+ def searchgasm_args(args, search_object, for_helper = nil)
48
+ args = args.dup
49
+ first = args.shift
50
+
51
+ # Setup args
52
+ case first
53
+ when String, Symbol
54
+ args.unshift(search_object).unshift(first)
55
+ else
56
+ name = search_object.is_a?(Conditions::Base) ? (search_object.relationship_name || :conditions) : :search
57
+ args.unshift(search_object).unshift(name)
58
+ end
59
+
60
+ if for_helper != :fields_for
61
+ options = args.extract_options!
62
+ options[:html] ||= {}
63
+ options[:html][:method] ||= :get
64
+ options[:method] ||= options[:html][:method] if for_helper == :remote_form_for
65
+ options[:html][:id] ||= searchgasm_form_id(search_object)
66
+
67
+ # Setup options
68
+ case first
69
+ when Array
70
+ first.pop
71
+ first << search_object.klass.new
72
+ options[:url] ||= polymorphic_path(first)
73
+ else
74
+ options[:url] ||= polymorphic_path(search_object.klass.new)
75
+ end
76
+
77
+ args << options
78
+ end
79
+
80
+ args
81
+ end
82
+
83
+ def insert_searchgasm_fields(args, search_object)
84
+ return unless search_object.is_a?(Search::Base)
85
+ name = args.first
86
+ options = args.extract_options!
87
+ (options.delete(:hidden_fields) || Config.hidden_fields).each do |option|
88
+ concat(hidden_field(name, option, :object => search_object, :value => (option == :order_by ? searchgasm_order_by_value(search_object.order_by) : search_object.send(option))))
89
+ end
90
+ args << options
91
+ end
92
+ end
93
+
94
+ module Base # :nodoc:
95
+ include Shared
96
+
97
+ def fields_for_with_searchgasm(*args, &block)
98
+ search_object = find_searchgasm_object(args)
99
+ if search_object
100
+ new_args = searchgasm_args(args, search_object, :fields_for)
101
+ insert_searchgasm_fields(new_args, search_object)
102
+ fields_for_without_searchgasm(*new_args, &block)
103
+ else
104
+ fields_for_without_searchgasm(*args, &block)
105
+ end
106
+ end
107
+
108
+ def form_for_with_searchgasm(*args, &block)
109
+ search_object = find_searchgasm_object(args)
110
+ if search_object
111
+ form_for_without_searchgasm(*searchgasm_args(args, search_object, :form_for), &block)
112
+ else
113
+ form_for_without_searchgasm(*args, &block)
114
+ end
115
+ end
116
+
117
+ def remote_form_for_with_searchgasm(*args, &block)
118
+ search_object = find_searchgasm_object(args)
119
+ if search_object
120
+ remote_form_for_without_searchgasm(*searchgasm_args(args, search_object, :remote_form_for), &block)
121
+ else
122
+ remote_form_for_without_searchgasm(*args, &block)
123
+ end
124
+ end
125
+ end
126
+
127
+ module FormBuilder # :nodoc:
128
+ include Shared
129
+
130
+ def fields_for_with_searchgasm(*args, &block)
131
+ search_object = find_searchgasm_object(args)
132
+ if search_object
133
+ new_args = searchgasm_args(args, search_object, :fields_for)
134
+ insert_searchgasm_fields(new_args, search_object)
135
+ fields_for_without_searchgasm(*new_args, &block)
136
+ else
137
+ fields_for_without_searchgasm(*args, &block)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ if defined?(ActionView)
146
+ ActionView::Base.send(:include, Searchgasm::Helpers::FormHelper::Base)
147
+
148
+ ActionView::Base.class_eval do
149
+ alias_method_chain :fields_for, :searchgasm
150
+ alias_method_chain :form_for, :searchgasm
151
+ alias_method_chain :remote_form_for, :searchgasm
152
+ end
153
+
154
+ ActionView::Helpers::FormBuilder.send(:include, Searchgasm::Helpers::FormHelper::FormBuilder)
155
+
156
+ ActionView::Helpers::FormBuilder.class_eval do
157
+ alias_method_chain :fields_for, :searchgasm
158
+ end
159
+ end