schof-searchlogic 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/CHANGELOG.rdoc +302 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +157 -0
  4. data/README.rdoc +461 -0
  5. data/Rakefile +13 -0
  6. data/TODO.rdoc +4 -0
  7. data/init.rb +1 -0
  8. data/lib/searchlogic.rb +100 -0
  9. data/lib/searchlogic/active_record/associations.rb +52 -0
  10. data/lib/searchlogic/active_record/base.rb +224 -0
  11. data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +176 -0
  12. data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +172 -0
  13. data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +80 -0
  14. data/lib/searchlogic/condition/base.rb +165 -0
  15. data/lib/searchlogic/condition/begins_with.rb +17 -0
  16. data/lib/searchlogic/condition/blank.rb +21 -0
  17. data/lib/searchlogic/condition/child_of.rb +11 -0
  18. data/lib/searchlogic/condition/descendant_of.rb +11 -0
  19. data/lib/searchlogic/condition/ends_with.rb +17 -0
  20. data/lib/searchlogic/condition/equals.rb +33 -0
  21. data/lib/searchlogic/condition/greater_than.rb +15 -0
  22. data/lib/searchlogic/condition/greater_than_or_equal_to.rb +15 -0
  23. data/lib/searchlogic/condition/inclusive_descendant_of.rb +10 -0
  24. data/lib/searchlogic/condition/keywords.rb +47 -0
  25. data/lib/searchlogic/condition/less_than.rb +15 -0
  26. data/lib/searchlogic/condition/less_than_or_equal_to.rb +15 -0
  27. data/lib/searchlogic/condition/like.rb +15 -0
  28. data/lib/searchlogic/condition/nested_set.rb +17 -0
  29. data/lib/searchlogic/condition/nil.rb +21 -0
  30. data/lib/searchlogic/condition/not_begin_with.rb +20 -0
  31. data/lib/searchlogic/condition/not_blank.rb +19 -0
  32. data/lib/searchlogic/condition/not_end_with.rb +20 -0
  33. data/lib/searchlogic/condition/not_equal.rb +27 -0
  34. data/lib/searchlogic/condition/not_have_keywords.rb +20 -0
  35. data/lib/searchlogic/condition/not_like.rb +20 -0
  36. data/lib/searchlogic/condition/not_nil.rb +19 -0
  37. data/lib/searchlogic/condition/sibling_of.rb +14 -0
  38. data/lib/searchlogic/conditions/any_or_all.rb +42 -0
  39. data/lib/searchlogic/conditions/base.rb +244 -0
  40. data/lib/searchlogic/conditions/groups.rb +74 -0
  41. data/lib/searchlogic/conditions/magic_methods.rb +286 -0
  42. data/lib/searchlogic/conditions/multiparameter_attributes.rb +105 -0
  43. data/lib/searchlogic/conditions/protection.rb +36 -0
  44. data/lib/searchlogic/config.rb +31 -0
  45. data/lib/searchlogic/config/helpers.rb +338 -0
  46. data/lib/searchlogic/config/search.rb +53 -0
  47. data/lib/searchlogic/core_ext/hash.rb +75 -0
  48. data/lib/searchlogic/core_ext/object.rb +19 -0
  49. data/lib/searchlogic/helpers/control_types/link.rb +310 -0
  50. data/lib/searchlogic/helpers/control_types/links.rb +242 -0
  51. data/lib/searchlogic/helpers/control_types/remote_link.rb +87 -0
  52. data/lib/searchlogic/helpers/control_types/remote_links.rb +72 -0
  53. data/lib/searchlogic/helpers/control_types/remote_select.rb +36 -0
  54. data/lib/searchlogic/helpers/control_types/select.rb +82 -0
  55. data/lib/searchlogic/helpers/form.rb +208 -0
  56. data/lib/searchlogic/helpers/utilities.rb +197 -0
  57. data/lib/searchlogic/modifiers/absolute.rb +15 -0
  58. data/lib/searchlogic/modifiers/acos.rb +11 -0
  59. data/lib/searchlogic/modifiers/asin.rb +11 -0
  60. data/lib/searchlogic/modifiers/atan.rb +11 -0
  61. data/lib/searchlogic/modifiers/avg.rb +15 -0
  62. data/lib/searchlogic/modifiers/base.rb +27 -0
  63. data/lib/searchlogic/modifiers/ceil.rb +15 -0
  64. data/lib/searchlogic/modifiers/char_length.rb +15 -0
  65. data/lib/searchlogic/modifiers/cos.rb +15 -0
  66. data/lib/searchlogic/modifiers/cot.rb +15 -0
  67. data/lib/searchlogic/modifiers/count.rb +11 -0
  68. data/lib/searchlogic/modifiers/day_of_month.rb +15 -0
  69. data/lib/searchlogic/modifiers/day_of_week.rb +15 -0
  70. data/lib/searchlogic/modifiers/day_of_year.rb +15 -0
  71. data/lib/searchlogic/modifiers/degrees.rb +11 -0
  72. data/lib/searchlogic/modifiers/exp.rb +15 -0
  73. data/lib/searchlogic/modifiers/floor.rb +15 -0
  74. data/lib/searchlogic/modifiers/hex.rb +11 -0
  75. data/lib/searchlogic/modifiers/hour.rb +11 -0
  76. data/lib/searchlogic/modifiers/log.rb +15 -0
  77. data/lib/searchlogic/modifiers/log10.rb +11 -0
  78. data/lib/searchlogic/modifiers/log2.rb +11 -0
  79. data/lib/searchlogic/modifiers/lower.rb +15 -0
  80. data/lib/searchlogic/modifiers/ltrim.rb +15 -0
  81. data/lib/searchlogic/modifiers/md5.rb +11 -0
  82. data/lib/searchlogic/modifiers/microseconds.rb +11 -0
  83. data/lib/searchlogic/modifiers/milliseconds.rb +11 -0
  84. data/lib/searchlogic/modifiers/minute.rb +15 -0
  85. data/lib/searchlogic/modifiers/month.rb +15 -0
  86. data/lib/searchlogic/modifiers/octal.rb +15 -0
  87. data/lib/searchlogic/modifiers/radians.rb +11 -0
  88. data/lib/searchlogic/modifiers/round.rb +11 -0
  89. data/lib/searchlogic/modifiers/rtrim.rb +15 -0
  90. data/lib/searchlogic/modifiers/second.rb +15 -0
  91. data/lib/searchlogic/modifiers/sign.rb +11 -0
  92. data/lib/searchlogic/modifiers/sin.rb +11 -0
  93. data/lib/searchlogic/modifiers/square_root.rb +15 -0
  94. data/lib/searchlogic/modifiers/sum.rb +11 -0
  95. data/lib/searchlogic/modifiers/tan.rb +15 -0
  96. data/lib/searchlogic/modifiers/trim.rb +15 -0
  97. data/lib/searchlogic/modifiers/upper.rb +15 -0
  98. data/lib/searchlogic/modifiers/week.rb +11 -0
  99. data/lib/searchlogic/modifiers/year.rb +11 -0
  100. data/lib/searchlogic/search/base.rb +148 -0
  101. data/lib/searchlogic/search/conditions.rb +53 -0
  102. data/lib/searchlogic/search/ordering.rb +244 -0
  103. data/lib/searchlogic/search/pagination.rb +121 -0
  104. data/lib/searchlogic/search/protection.rb +89 -0
  105. data/lib/searchlogic/search/searching.rb +32 -0
  106. data/lib/searchlogic/shared/utilities.rb +56 -0
  107. data/lib/searchlogic/shared/virtual_classes.rb +39 -0
  108. data/lib/searchlogic/version.rb +79 -0
  109. data/searchlogic.gemspec +41 -0
  110. data/test/active_record_tests/associations_test.rb +94 -0
  111. data/test/active_record_tests/base_test.rb +115 -0
  112. data/test/condition_tests/base_test.rb +54 -0
  113. data/test/condition_tests/begins_with_test.rb +11 -0
  114. data/test/condition_tests/blank_test.rb +31 -0
  115. data/test/condition_tests/child_of_test.rb +17 -0
  116. data/test/condition_tests/descendant_of_test.rb +12 -0
  117. data/test/condition_tests/ends_with_test.rb +11 -0
  118. data/test/condition_tests/equals_test.rb +28 -0
  119. data/test/condition_tests/greater_than_or_equal_to_test.rb +11 -0
  120. data/test/condition_tests/greater_than_test.rb +11 -0
  121. data/test/condition_tests/inclusive_descendant_of_test.rb +12 -0
  122. data/test/condition_tests/keywords_test.rb +23 -0
  123. data/test/condition_tests/less_than_or_equal_to_test.rb +11 -0
  124. data/test/condition_tests/less_than_test.rb +11 -0
  125. data/test/condition_tests/like_test.rb +11 -0
  126. data/test/condition_tests/nil_test.rb +31 -0
  127. data/test/condition_tests/not_begin_with_test.rb +8 -0
  128. data/test/condition_tests/not_blank_test.rb +8 -0
  129. data/test/condition_tests/not_end_with_test.rb +8 -0
  130. data/test/condition_tests/not_equal_test.rb +19 -0
  131. data/test/condition_tests/not_have_keywords_test.rb +8 -0
  132. data/test/condition_tests/not_like_test.rb +8 -0
  133. data/test/condition_tests/not_nil_test.rb +13 -0
  134. data/test/condition_tests/sibling_of_test.rb +15 -0
  135. data/test/conditions_tests/any_or_all_test.rb +23 -0
  136. data/test/conditions_tests/base_test.rb +185 -0
  137. data/test/conditions_tests/groups_test.rb +68 -0
  138. data/test/conditions_tests/magic_methods_test.rb +36 -0
  139. data/test/conditions_tests/multiparameter_attributes_test.rb +15 -0
  140. data/test/conditions_tests/protection_test.rb +18 -0
  141. data/test/config_test.rb +23 -0
  142. data/test/fixtures/accounts.yml +12 -0
  143. data/test/fixtures/animals.yml +7 -0
  144. data/test/fixtures/orders.yml +12 -0
  145. data/test/fixtures/user_groups.yml +5 -0
  146. data/test/fixtures/users.yml +45 -0
  147. data/test/libs/awesome_nested_set.rb +545 -0
  148. data/test/libs/awesome_nested_set/compatability.rb +29 -0
  149. data/test/libs/awesome_nested_set/helper.rb +40 -0
  150. data/test/libs/awesome_nested_set/named_scope.rb +140 -0
  151. data/test/libs/rexml_fix.rb +14 -0
  152. data/test/modifier_tests/day_of_month_test.rb +16 -0
  153. data/test/search_tests/base_test.rb +241 -0
  154. data/test/search_tests/conditions_test.rb +21 -0
  155. data/test/search_tests/ordering_test.rb +167 -0
  156. data/test/search_tests/pagination_test.rb +74 -0
  157. data/test/search_tests/protection_test.rb +26 -0
  158. data/test/test_helper.rb +116 -0
  159. metadata +385 -0
@@ -0,0 +1,208 @@
1
+ module Searchlogic
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 how ActiveRecord objects are treated. You can also pass a Searchlogic::Conditions::Base object as well and it will function the same way.
17
+ #
18
+ # === Automatic hidden fields generation
19
+ #
20
+ # If you pass a Searchlogic::Search::Base object it automatically adds the :order_by, :order_as, :priority_order_by, :priority_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 consistent 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 Searchlogic::Config for more details.
23
+ #
24
+ # Lastly some light javascript is added to the "onsubmit" action. You will notice that the control type helpers add in hidden fields in the page, as a way to declare its "state". The form
25
+ # finds these elements, gets their values and updates its hidden fields so that the correct values will be submitted during the search. The end result is having these search options remembered.
26
+ #
27
+ # === Options
28
+ #
29
+ # * <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.
30
+ module Form
31
+ module Shared # :nodoc:
32
+ private
33
+ def searchlogic_object?(object)
34
+ object.is_a?(Search::Base) || object.is_a?(Conditions::Base)
35
+ end
36
+
37
+ def find_searchlogic_object(args)
38
+ search_object = nil
39
+
40
+ case args.first
41
+ when String, Symbol
42
+ begin
43
+ search_object = searchlogic_object?(args[1]) ? args[1] : instance_variable_get("@#{args.first}")
44
+ rescue Exception
45
+ end
46
+ when Array
47
+ search_object = args.first.last
48
+ else
49
+ search_object = args.first
50
+ end
51
+
52
+ searchlogic_object?(search_object) ? search_object : nil
53
+ end
54
+
55
+ def extract_searchlogic_options!(args)
56
+ options = args.extract_options!
57
+ searchlogic_options = {}
58
+ [:hidden_fields].each { |option| searchlogic_options[option] = options.has_key?(option) ? options.delete(option) : Config.helpers.send(option) }
59
+ searchlogic_options[:hidden_fields] = [searchlogic_options[:hidden_fields]].flatten.compact
60
+ args << options
61
+ searchlogic_options
62
+ end
63
+
64
+ def searchlogic_args(args, search_object, search_options, for_helper = nil)
65
+ args = args.dup
66
+ first = args.shift
67
+
68
+ # Rearrange args with name first, search_obj second
69
+ case first
70
+ when String, Symbol
71
+ args.unshift(search_object).unshift(first)
72
+ else
73
+ name = search_object.is_a?(Conditions::Base) ? (search_object.object_name || :conditions) : :search
74
+ args.unshift(search_object).unshift(name)
75
+ end
76
+
77
+ # Now that we are consistent, get the name
78
+ name = args.first
79
+
80
+ # Add in some form magic to keep searching consisten and user friendly
81
+ if for_helper != :fields_for
82
+ options = args.extract_options!
83
+
84
+ # Set some defaults
85
+ options[:html] ||= {}
86
+ options[:html][:method] ||= :get
87
+ options[:method] ||= options[:html][:method] if for_helper == :remote_form_for
88
+ #options[:html][:id] ||= searchlogic_form_id(search_object)
89
+
90
+ if !search_options[:hidden_fields].blank?
91
+ options[:html][:onsubmit] ||= ""
92
+ options[:html][:onsubmit] += ";"
93
+
94
+ javascript = ""
95
+ javascript += "if(typeof(Prototype) != 'undefined') {" if Config.helpers.javascript_library.blank?
96
+ search_options[:hidden_fields].each { |field| javascript += "field = $('#{name}_#{field}'); if(field) { $('#{name}_#{field}_#{options.object_id}').value = field.value; }" } if Config.helpers.javascript_library.blank? || Config.helpers.javascript_library == :prototype
97
+ javascript += "} else if(typeof(jQuery) != 'undefined') {" if Config.helpers.javascript_library.blank?
98
+ search_options[:hidden_fields].each { |field| javascript += "field = $('##{name}_#{field}'); if(field) { $('##{name}_#{field}_#{options.object_id}').val(field.val()); }" } if Config.helpers.javascript_library.blank? || Config.helpers.javascript_library == :jquery
99
+ javascript += "}" if Config.helpers.javascript_library.blank?
100
+
101
+ options[:html][:onsubmit] += javascript
102
+ end
103
+
104
+ # Setup options
105
+ case first
106
+ when Array
107
+ first.pop
108
+ first << search_object.klass.new
109
+ options[:url] ||= polymorphic_path(first)
110
+ else
111
+ options[:url] ||= polymorphic_path(search_object.klass.new)
112
+ end
113
+
114
+ args << options
115
+ end
116
+
117
+ args
118
+ end
119
+
120
+ def insert_searchlogic_fields(args, search_object, search_options, &block)
121
+ return unless search_object.is_a?(Search::Base)
122
+ name = args.first
123
+ options = args.extract_options!
124
+ html = ""
125
+ search_options[:hidden_fields].each do |field|
126
+ html << hidden_field(name, field, :object => search_object, :id => "#{name}_#{field}_#{options.object_id}", :value => (field == :order_by ? searchlogic_base64_value(search_object.order_by) : search_object.send(field)))
127
+ end
128
+
129
+ # For edge rails and older version compatibility, passing a binding to concat was deprecated
130
+ begin
131
+ concat(content_tag(:div, html, :style => 'margin:0;padding:0'))
132
+ rescue ArgumentError, NameError
133
+ concat(content_tag(:div, html, :style => 'margin:0;padding:0'), block.binding)
134
+ end
135
+ args << options
136
+ end
137
+ end
138
+
139
+ module Base # :nodoc:
140
+ include Shared
141
+
142
+ def fields_for_with_searchlogic(*args, &block)
143
+ search_object = find_searchlogic_object(args)
144
+ if search_object
145
+ searchlogic_options = extract_searchlogic_options!(args)
146
+ new_args = searchlogic_args(args, search_object, searchlogic_options, :fields_for)
147
+ insert_searchlogic_fields(new_args, search_object, searchlogic_options, &block)
148
+ fields_for_without_searchlogic(*new_args, &block)
149
+ else
150
+ fields_for_without_searchlogic(*args, &block)
151
+ end
152
+ end
153
+
154
+ def form_for_with_searchlogic(*args, &block)
155
+ search_object = find_searchlogic_object(args)
156
+ if search_object
157
+ searchlogic_options = extract_searchlogic_options!(args)
158
+ form_for_without_searchlogic(*searchlogic_args(args, search_object, searchlogic_options, :form_for), &block)
159
+ else
160
+ form_for_without_searchlogic(*args, &block)
161
+ end
162
+ end
163
+
164
+ def remote_form_for_with_searchlogic(*args, &block)
165
+ search_object = find_searchlogic_object(args)
166
+ if search_object
167
+ searchlogic_options = extract_searchlogic_options!(args)
168
+ remote_form_for_without_searchlogic(*searchlogic_args(args, search_object, searchlogic_options, :remote_form_for), &block)
169
+ else
170
+ remote_form_for_without_searchlogic(*args, &block)
171
+ end
172
+ end
173
+ end
174
+
175
+ module FormBuilder # :nodoc:
176
+ include Shared
177
+
178
+ def fields_for_with_searchlogic(*args, &block)
179
+ search_object = find_searchlogic_object(args)
180
+ if search_object
181
+ searchlogic_options = extract_searchlogic_options!(args)
182
+ new_args = searchlogic_args(args, search_object, searchlogic_options, :fields_for)
183
+ insert_searchlogic_fields(new_args, search_object, searchlogic_options, &block)
184
+ fields_for_without_searchlogic(*new_args, &block)
185
+ else
186
+ fields_for_without_searchlogic(*args, &block)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ if defined?(ActionView)
195
+ ActionView::Base.send(:include, Searchlogic::Helpers::Form::Base)
196
+
197
+ ActionView::Base.class_eval do
198
+ alias_method_chain :fields_for, :searchlogic
199
+ alias_method_chain :form_for, :searchlogic
200
+ alias_method_chain :remote_form_for, :searchlogic
201
+ end
202
+
203
+ ActionView::Helpers::FormBuilder.send(:include, Searchlogic::Helpers::Form::FormBuilder)
204
+
205
+ ActionView::Helpers::FormBuilder.class_eval do
206
+ alias_method_chain :fields_for, :searchlogic
207
+ end
208
+ end
@@ -0,0 +1,197 @@
1
+ module Searchlogic
2
+ module Helpers #:nodoc:
3
+ module Utilities
4
+ # Builds a hash of params for creating a url and preserves any existing params. You can pass this into url_for and build your url. Although most rails helpers accept a hash.
5
+ #
6
+ # Let's take the page_link helper. Here is the code behind that helper:
7
+ #
8
+ # link_to("Page 2", searchlogic_params(:search_params => {:page => 2}))
9
+ #
10
+ # That's pretty much it. So if you wanted to roll your own link to execute a search, go for it. It's pretty simple. Pass conditions instead of the page, set how the search will be ordered, etc.
11
+ #
12
+ # <b>Be careful</b> when taking this approach though. Searchlogic helps you out when you use form_for. For example, when you use the per_page_select helper, it adds in a hidden form field with the value of the page. So when
13
+ # your search form is submitted it searches the document for that element, finds the current value, which is the current per_page value, and includes that in the search. So when a user searches the per_page
14
+ # value stays consistent. If you use the searchlogic_params you are on your own. I am always curious how people are using searchlogic. So if you are building your own helpers contact me and maybe I can help you
15
+ # and add in a helper for you, making it an *official* feature.
16
+ #
17
+ # === Options
18
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
19
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
20
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
21
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
22
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
23
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
24
+ def searchlogic_params(options = {})
25
+ add_searchlogic_defaults!(options)
26
+ options[:search_params] ||= {}
27
+ options[:literal_search_params] ||= {}
28
+
29
+ options[:params] ||= {}
30
+ params_copy = params.deep_dup.with_indifferent_access
31
+ search_params = options[:params_scope].blank? ? params_copy : params_copy.delete(options[:params_scope])
32
+ search_params ||= {}
33
+ search_params = search_params.with_indifferent_access
34
+ search_params.delete(:commit)
35
+ search_params.delete(:page)
36
+ search_params.deep_delete_duplicate_keys(options[:literal_search_params])
37
+ search_params.deep_delete(options[:exclude_search_params])
38
+
39
+ if options[:search_params]
40
+
41
+ #raise params_copy.inspect if options[:search_params][:order_by] == :id
42
+ search_params.deep_merge!(options[:search_params])
43
+
44
+ if options[:search_params][:order_by] && !options[:search_params][:order_as]
45
+ search_params[:order_as] = (searchlogic_ordering_by?(options[:search_params][:order_by], options) && options[:search_obj].asc?) ? "DESC" : "ASC"
46
+ end
47
+
48
+ [:order_by, :priority_order_by].each { |base64_field| search_params[base64_field] = searchlogic_base64_value(search_params[base64_field]) if search_params.has_key?(base64_field) }
49
+ end
50
+
51
+ new_params = params_copy
52
+ new_params.deep_merge!(options[:params])
53
+ new_params.deep_delete(options[:exclude_params])
54
+
55
+ if options[:params_scope].blank? || search_params.blank?
56
+ new_params
57
+ else
58
+ new_params.merge(options[:params_scope] => search_params)
59
+ end
60
+ end
61
+
62
+ # Similar to searchlogic_hash, but instead returns a string url. The reason this exists is to assist in creating urls in javascript. It's the muscle behind all of the select helpers that searchlogic provides.
63
+ # Take the instance where you want to do:
64
+ #
65
+ # :onchange => "window.location = '#{url_for(searchlogic_params)}&my_param=' + this.value;"
66
+ #
67
+ # Well the above obviously won't work. Do you need to apped the url with a ? or a &? What about that tricky :params_scope? That's where this is handy, beacuse it does all of the params string building for you. Check it out:
68
+ #
69
+ # :onchange => "window.location = '" + searchlogic_url(:literal_search_params => {:per_page => "' + escape(this.value) + '"}) + "';"
70
+ #
71
+ # or what about something a little more tricky?
72
+ #
73
+ # :onchange => "window.location = '" + searchlogic_url(:literal_search_params => {:conditions => {:name_contains => "' + escape(this.value) + '"}}) + "';"
74
+ #
75
+ # I have personally used this for an event calendar. Above the calendar there was a drop down for each month. Here is the code:
76
+ #
77
+ # :onchange => "window.location = '" + searchlogic_url(:literal_search_params => {:conditions => {:occurs_at_after => "' + escape(this.value) + '"}}) + "';"
78
+ #
79
+ # Now when the user changes the month in the drop down it just runs a new search that sets my conditions to occurs_at_after = selected month. Then in my controller I set occurs_at_before = occurs_at_after.at_end_of_month.
80
+ #
81
+ # === Options
82
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
83
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
84
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
85
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
86
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
87
+ # * <tt>:literal_search_params</tt> -- default: nil, Additional search params to add to the url, but are not escaped. So you can add javascript into the URL: :literal_search_params => {:per_page => "' + escape(this.value) + '"}
88
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
89
+ def searchlogic_url(options = {})
90
+ search_params = searchlogic_params(options)
91
+ url = url_for(search_params)
92
+ literal_param_strings = literal_param_strings(options[:literal_search_params], options[:params_scope].blank? ? "" : "#{options[:params_scope]}")
93
+ url += (url.last == "?" ? "" : (url.include?("?") ? "&amp;" : "?")) + literal_param_strings.join("&amp;")
94
+ end
95
+
96
+ # When you set up a search form using form_for for remote_form_for searchlogic adds in some *magic* for you.
97
+ #
98
+ # Take the instance where a user orders the data by something other than the default, and then does a search. The user would expect the search to remember what the user selected to order the data by, right?
99
+ # What searchlogic does is add in some hidden fields, somewhere in the page, the represent the searchlogic "state". These are automatically added for you when you use the searchlogic helpers.
100
+ # Such as: page_links, page_link, order_by_link, per_page_select, etc. So if you are using those you do not need to worry about this helper.
101
+ #
102
+ # If for some reason you do not use any of these you need to put the searchlogic state on your page somewhere. Somewhere where the state will *always* be up-to-date, which would be most likely be in the
103
+ # partial that renders your search results (assuming you are using AJAX). Otherwise when the user starts a new search, the state will be reset. Meaning the order_by, per_page, etc will all be reset.
104
+ #
105
+ # === Options
106
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
107
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
108
+ def searchlogic_state(options = {})
109
+ return "" if @added_searchlogic_state
110
+ add_searchlogic_defaults!(options)
111
+ html = ""
112
+ (Search::Base::SPECIAL_FIND_OPTIONS - [:page, :priority_order]).each do |option|
113
+ value = options[:search_obj].send(option)
114
+ html += hidden_field(options[:params_scope], option, :value => (option == :order_by ? searchlogic_base64_value(value) : value))
115
+ end
116
+ @added_searchlogic_state = true
117
+ html
118
+ end
119
+
120
+ private
121
+ # Adds default options for all helper methods.
122
+ def add_searchlogic_defaults!(options)
123
+ options[:params_scope] = :search unless options.has_key?(:params_scope)
124
+ options[:search_obj] ||= instance_variable_get("@#{options[:params_scope]}")
125
+ raise(ArgumentError, "@search object could not be inferred, please specify: :search_obj => @search or :params_scope => :search_obj_name") unless options[:search_obj].is_a?(Searchlogic::Search::Base)
126
+ options
127
+ end
128
+
129
+ # Adds default options for all control type helper methods.
130
+ def add_searchlogic_control_defaults!(options)
131
+ add_searchlogic_defaults!(options)
132
+ options[:html] ||= {}
133
+ options
134
+ end
135
+
136
+ def searchlogic_add_class!(html_options, new_class)
137
+ new_class = new_class.to_s
138
+ html_options[:class] ||= ""
139
+ classes = html_options[:class].split(" ")
140
+ classes << new_class unless classes.include?(new_class)
141
+ html_options[:class] = classes.join(" ")
142
+ end
143
+
144
+ def searchlogic_base64_value(order_by)
145
+ case order_by
146
+ when String, Symbol
147
+ order_by
148
+ when Array, Hash
149
+ [Marshal.dump(order_by)].pack("m")
150
+ end
151
+ end
152
+
153
+ def searchlogic_ordering_by?(order_by, options)
154
+ stringified_search_order_by = deep_stringify(options[:search_obj].order_by)
155
+ stringified_order_by = deep_stringify(order_by)
156
+ (options[:search_obj].order_by.blank? && options[:search_obj].klass.primary_key == stringified_order_by) || stringified_search_order_by == stringified_order_by
157
+ end
158
+
159
+ def literal_param_strings(literal_params, prefix)
160
+ param_strings = []
161
+
162
+ literal_params.each do |k, v|
163
+ param_string = prefix.blank? ? k.to_s : "#{prefix}[#{k}]"
164
+ case v
165
+ when Hash
166
+ literal_param_strings(v, param_string).each do |literal_param_string|
167
+ param_strings << literal_param_string
168
+ end
169
+ else
170
+ param_strings << (CGI.escape(param_string) + "=#{v}")
171
+ end
172
+ end
173
+
174
+ param_strings
175
+ end
176
+
177
+ def deep_stringify(obj)
178
+ case obj
179
+ when String
180
+ obj
181
+ when Symbol
182
+ obj.to_s
183
+ when Array
184
+ obj.collect { |item| deep_stringify(item) }
185
+ when Hash
186
+ new_obj = {}
187
+ obj.each { |key, value| new_obj[key.to_s] = deep_stringify(value) }
188
+ new_obj
189
+ else
190
+ obj
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ ActionController::Base.helper(Searchlogic::Helpers::Utilities) if defined?(ActionController)
@@ -0,0 +1,15 @@
1
+ module Searchlogic
2
+ module Modifiers
3
+ class Absolute < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["abs"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Searchlogic
2
+ module Modifiers
3
+ class Acos < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Searchlogic
2
+ module Modifiers
3
+ class Asin < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Searchlogic
2
+ module Modifiers
3
+ class Atan < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end