searchlogic 1.5.3

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 (125) hide show
  1. data/CHANGELOG.rdoc +228 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +123 -0
  4. data/README.rdoc +383 -0
  5. data/Rakefile +15 -0
  6. data/TODO.rdoc +6 -0
  7. data/examples/README.rdoc +4 -0
  8. data/init.rb +1 -0
  9. data/lib/searchlogic.rb +89 -0
  10. data/lib/searchlogic/active_record/associations.rb +52 -0
  11. data/lib/searchlogic/active_record/base.rb +218 -0
  12. data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +172 -0
  13. data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +168 -0
  14. data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +75 -0
  15. data/lib/searchlogic/condition/base.rb +159 -0
  16. data/lib/searchlogic/condition/begins_with.rb +17 -0
  17. data/lib/searchlogic/condition/blank.rb +21 -0
  18. data/lib/searchlogic/condition/child_of.rb +11 -0
  19. data/lib/searchlogic/condition/descendant_of.rb +24 -0
  20. data/lib/searchlogic/condition/ends_with.rb +17 -0
  21. data/lib/searchlogic/condition/equals.rb +27 -0
  22. data/lib/searchlogic/condition/greater_than.rb +15 -0
  23. data/lib/searchlogic/condition/greater_than_or_equal_to.rb +15 -0
  24. data/lib/searchlogic/condition/inclusive_descendant_of.rb +11 -0
  25. data/lib/searchlogic/condition/keywords.rb +47 -0
  26. data/lib/searchlogic/condition/less_than.rb +15 -0
  27. data/lib/searchlogic/condition/less_than_or_equal_to.rb +15 -0
  28. data/lib/searchlogic/condition/like.rb +15 -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 +26 -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/condition/tree.rb +17 -0
  39. data/lib/searchlogic/conditions/base.rb +484 -0
  40. data/lib/searchlogic/conditions/protection.rb +36 -0
  41. data/lib/searchlogic/config.rb +31 -0
  42. data/lib/searchlogic/config/helpers.rb +289 -0
  43. data/lib/searchlogic/config/search.rb +53 -0
  44. data/lib/searchlogic/core_ext/hash.rb +75 -0
  45. data/lib/searchlogic/helpers/control_types/link.rb +310 -0
  46. data/lib/searchlogic/helpers/control_types/links.rb +241 -0
  47. data/lib/searchlogic/helpers/control_types/remote_link.rb +87 -0
  48. data/lib/searchlogic/helpers/control_types/remote_links.rb +72 -0
  49. data/lib/searchlogic/helpers/control_types/remote_select.rb +36 -0
  50. data/lib/searchlogic/helpers/control_types/select.rb +82 -0
  51. data/lib/searchlogic/helpers/form.rb +208 -0
  52. data/lib/searchlogic/helpers/utilities.rb +197 -0
  53. data/lib/searchlogic/modifiers/absolute.rb +15 -0
  54. data/lib/searchlogic/modifiers/acos.rb +11 -0
  55. data/lib/searchlogic/modifiers/asin.rb +11 -0
  56. data/lib/searchlogic/modifiers/atan.rb +11 -0
  57. data/lib/searchlogic/modifiers/base.rb +27 -0
  58. data/lib/searchlogic/modifiers/ceil.rb +15 -0
  59. data/lib/searchlogic/modifiers/char_length.rb +15 -0
  60. data/lib/searchlogic/modifiers/cos.rb +15 -0
  61. data/lib/searchlogic/modifiers/cot.rb +15 -0
  62. data/lib/searchlogic/modifiers/day_of_month.rb +15 -0
  63. data/lib/searchlogic/modifiers/day_of_week.rb +15 -0
  64. data/lib/searchlogic/modifiers/day_of_year.rb +15 -0
  65. data/lib/searchlogic/modifiers/degrees.rb +11 -0
  66. data/lib/searchlogic/modifiers/exp.rb +15 -0
  67. data/lib/searchlogic/modifiers/floor.rb +15 -0
  68. data/lib/searchlogic/modifiers/hex.rb +11 -0
  69. data/lib/searchlogic/modifiers/hour.rb +11 -0
  70. data/lib/searchlogic/modifiers/log.rb +15 -0
  71. data/lib/searchlogic/modifiers/log10.rb +11 -0
  72. data/lib/searchlogic/modifiers/log2.rb +11 -0
  73. data/lib/searchlogic/modifiers/lower.rb +15 -0
  74. data/lib/searchlogic/modifiers/ltrim.rb +15 -0
  75. data/lib/searchlogic/modifiers/md5.rb +11 -0
  76. data/lib/searchlogic/modifiers/microseconds.rb +11 -0
  77. data/lib/searchlogic/modifiers/milliseconds.rb +11 -0
  78. data/lib/searchlogic/modifiers/minute.rb +15 -0
  79. data/lib/searchlogic/modifiers/month.rb +15 -0
  80. data/lib/searchlogic/modifiers/octal.rb +15 -0
  81. data/lib/searchlogic/modifiers/radians.rb +11 -0
  82. data/lib/searchlogic/modifiers/round.rb +11 -0
  83. data/lib/searchlogic/modifiers/rtrim.rb +15 -0
  84. data/lib/searchlogic/modifiers/second.rb +15 -0
  85. data/lib/searchlogic/modifiers/sign.rb +11 -0
  86. data/lib/searchlogic/modifiers/sin.rb +11 -0
  87. data/lib/searchlogic/modifiers/square_root.rb +15 -0
  88. data/lib/searchlogic/modifiers/tan.rb +15 -0
  89. data/lib/searchlogic/modifiers/trim.rb +15 -0
  90. data/lib/searchlogic/modifiers/upper.rb +15 -0
  91. data/lib/searchlogic/modifiers/week.rb +11 -0
  92. data/lib/searchlogic/modifiers/year.rb +11 -0
  93. data/lib/searchlogic/search/base.rb +148 -0
  94. data/lib/searchlogic/search/conditions.rb +53 -0
  95. data/lib/searchlogic/search/ordering.rb +244 -0
  96. data/lib/searchlogic/search/pagination.rb +121 -0
  97. data/lib/searchlogic/search/protection.rb +89 -0
  98. data/lib/searchlogic/search/searching.rb +31 -0
  99. data/lib/searchlogic/shared/utilities.rb +50 -0
  100. data/lib/searchlogic/shared/virtual_classes.rb +39 -0
  101. data/lib/searchlogic/version.rb +79 -0
  102. data/searchlogic.gemspec +39 -0
  103. data/test/fixtures/accounts.yml +15 -0
  104. data/test/fixtures/cats.yml +3 -0
  105. data/test/fixtures/dogs.yml +3 -0
  106. data/test/fixtures/orders.yml +14 -0
  107. data/test/fixtures/user_groups.yml +13 -0
  108. data/test/fixtures/users.yml +36 -0
  109. data/test/test_active_record_associations.rb +81 -0
  110. data/test/test_active_record_base.rb +93 -0
  111. data/test/test_condition_base.rb +52 -0
  112. data/test/test_condition_types.rb +143 -0
  113. data/test/test_conditions_base.rb +242 -0
  114. data/test/test_conditions_protection.rb +16 -0
  115. data/test/test_config.rb +23 -0
  116. data/test/test_helper.rb +134 -0
  117. data/test/test_search_base.rb +227 -0
  118. data/test/test_search_conditions.rb +19 -0
  119. data/test/test_search_ordering.rb +165 -0
  120. data/test/test_search_pagination.rb +72 -0
  121. data/test/test_search_protection.rb +24 -0
  122. data/test_libs/acts_as_tree.rb +98 -0
  123. data/test_libs/ordered_hash.rb +9 -0
  124. data/test_libs/rexml_fix.rb +14 -0
  125. metadata +317 -0
@@ -0,0 +1,36 @@
1
+ module Searchlogic
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_reader :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 pass SQL as conditions while the search is being protected, you can only pass a hash")
19
+ end
20
+ end
21
+
22
+ self.conditions_without_protection = conditions
23
+ end
24
+
25
+ def protect=(value)
26
+ associations.each { |name, obj| obj.protect = value }
27
+ @protect = value
28
+ end
29
+
30
+ def protect?
31
+ protect == true
32
+ end
33
+ alias_method :protected?, :protect?
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ module Searchlogic
2
+ # = Config
3
+ # Adds default configuration for all of searchlogic. For rails the best place to do this is in config/initializers. Create a file in there called searchlogic.rb with the following content:
4
+ #
5
+ # === Example
6
+ #
7
+ # # config/iniitializers/searchlogic.rb
8
+ # Searchlogic::Config.configure do |config|
9
+ # config.search.per_page = 25
10
+ # config.helpers.order_by_link_asc_indicator = "My indicator"
11
+ # end
12
+ #
13
+ # For a list of all configuration options see Searchlogic::Config::Search and Searchlogic::Config::Helpers
14
+ class Config
15
+ class << self
16
+ # Convenience method for setting configuration
17
+ # See example at top of class.
18
+ def configure
19
+ yield self
20
+ end
21
+
22
+ def search # :nodoc:
23
+ Search
24
+ end
25
+
26
+ def helpers # :nodoc:
27
+ Helpers
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,289 @@
1
+ module Searchlogic
2
+ class Config
3
+ # = Helpers Configuration
4
+ # Provide configuration for searchlogic's rails helpers
5
+ #
6
+ # === Example
7
+ # Searchlogic::Config.configure do |config|
8
+ # config.helpers.order_by_link_asc_indicator = "ASC"
9
+ # end
10
+ class Helpers
11
+ class << self
12
+ # Which hidden fields to automatically include when creating a form with a Searchlogic object. See Searchlogic::Helpers::Form for more info.
13
+ #
14
+ # * <tt>Default:</tt> [:order_by, :order_as, :per_page]
15
+ # * <tt>Accepts:</tt> Array, nil, false
16
+ def hidden_fields
17
+ @hidden_fields ||= (Searchlogic::Search::Base::SPECIAL_FIND_OPTIONS - [:page, :priority_order])
18
+ end
19
+ attr_writer :hidden_fields
20
+
21
+ # Searchlogic does some javascript magic when you use the form helpers with a Searchlogic object. To make configuration easier Searchlogic checks for the existence of Prototype and jQuery and uses the first
22
+ # one it finds. To cut back on the javascript output you can specify your library here.
23
+ #
24
+ # * <tt>Default:</tt> nil
25
+ # * <tt>Accepts:</tt> :prototype or :jquery
26
+ def javascript_library
27
+ @javascript_library
28
+ end
29
+ attr_writer :javascript_library
30
+
31
+ # The class name for used in the order_as_link helper
32
+ #
33
+ # * <tt>Default:</tt> "order_as"
34
+ # * <tt>Accepts:</tt> String
35
+ def order_as_link_class_name
36
+ @order_as_link_class_name ||= "order_as"
37
+ end
38
+ attr_writer :order_as_link_class_name
39
+
40
+ # The class name for used in the order_as_select helper
41
+ #
42
+ # * <tt>Default:</tt> "order_as"
43
+ # * <tt>Accepts:</tt> String
44
+ def order_as_select_class_name
45
+ @order_as_select_class_name ||= "order_as"
46
+ end
47
+ attr_writer :order_as_select_class_name
48
+
49
+ # The indicator that is used when the sort of a column is ascending
50
+ #
51
+ # * <tt>Default:</tt> &nbsp;&#9650;
52
+ # * <tt>Accepts:</tt> String or a Proc.
53
+ #
54
+ # === Examples
55
+ #
56
+ # config.asc_indicator = "(ASC)"
57
+ # config.asc_indicator = Proc.new { |template| template.image_tag("asc.jpg") }
58
+ def order_by_link_asc_indicator
59
+ @order_by_link_asc_indicator ||= "&nbsp;&#9650;"
60
+ end
61
+ attr_writer :order_by_link_asc_indicator
62
+
63
+ # The class name for used in the order_by_link helper
64
+ #
65
+ # * <tt>Default:</tt> "order_by"
66
+ # * <tt>Accepts:</tt> String
67
+ def order_by_link_class_name
68
+ @order_by_link_class_name ||= "order_by"
69
+ end
70
+ attr_writer :order_by_link_class_name
71
+
72
+ # See order_by_link_asc_indicator=
73
+ def order_by_link_desc_indicator
74
+ @order_by_link_desc_indicator ||= "&nbsp;&#9660;"
75
+ end
76
+ attr_writer :order_by_link_desc_indicator
77
+
78
+ # The class name used in order_by_links for the link that it is currently ordering by
79
+ #
80
+ # * <tt>Default:</tt> "# The class name for used in the page_link helper
81
+ #
82
+ # * <tt>Default:</tt> "page"
83
+ # * <tt>Accepts:</tt> String
84
+ def order_by_links_ordering_by_class_name
85
+ @order_by_links_ordering_by_class_name ||= "ordering_by"
86
+ end
87
+ attr_writer :order_by_links_ordering_by_class_name
88
+
89
+ # The class name for used in the order_by_select helper
90
+ #
91
+ # * <tt>Default:</tt> "order_by"
92
+ # * <tt>Accepts:</tt> String
93
+ def order_by_select_class_name
94
+ @order_by_select_class_name ||= "order_by"
95
+ end
96
+ attr_writer :order_by_select_class_name
97
+
98
+ # The class name for used in the page_link helper
99
+ #
100
+ # * <tt>Default:</tt> "page"
101
+ # * <tt>Accepts:</tt> String
102
+ def page_link_class_name
103
+ @page_link_class_name ||= "page"
104
+ end
105
+ attr_writer :page_link_class_name
106
+
107
+ # The choices used in the per_page_links helper. Works just like per_page_select_choices.
108
+ def per_page_links_choices
109
+ @per_page_links_choices ||= per_page_select_choices
110
+ end
111
+ attr_writer :per_page_links_choices
112
+
113
+ # The class that the current page link gets.
114
+ #
115
+ # * <tt>Default:</tt> "current_page"
116
+ # * <tt>Accepts:</tt> String, set to nil to disable
117
+ def page_links_current_page_class_name
118
+ @page_links_current_page_class_name ||= "current_page"
119
+ end
120
+ attr_writer :page_links_current_page_class_name
121
+
122
+ # The class that disabled page links get. Including the current page, prev page, next page, first page, and last page.
123
+ #
124
+ # * <tt>Default:</tt> "disabled_page"
125
+ # * <tt>Accepts:</tt> String, set to nil to disable
126
+ def page_links_disabled_class_name
127
+ @page_links_disabled_class_name ||= "disabled_page"
128
+ end
129
+ attr_writer :page_links_disabled_class_name
130
+
131
+ # The default for the :first option for the page_links helper.
132
+ #
133
+ # * <tt>Default:</tt> nil
134
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
135
+ def page_links_first
136
+ @page_links_first
137
+ end
138
+ attr_writer :page_links_first
139
+
140
+ # The default for the :inner_spread option for the page_links helper.
141
+ #
142
+ # * <tt>Default:</tt> 3
143
+ # * <tt>Accepts:</tt> Any integer >= 1, set to nil to show all pages
144
+ def page_links_inner_spread
145
+ @page_links_inner_spread ||= 3
146
+ end
147
+ attr_writer :page_links_inner_spread
148
+
149
+ # The class for the first page link
150
+ #
151
+ # * <tt>Default:</tt> "first_page"
152
+ # * <tt>Accepts:</tt> String, nil to disable
153
+ def page_links_first_page_class_name
154
+ @page_links_first_page_class_name ||= "first_page"
155
+ end
156
+ attr_writer :page_links_first_page_class_name
157
+
158
+ # The default for the :last option for the page_links helper.
159
+ #
160
+ # * <tt>Default:</tt> nil
161
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
162
+ def page_links_last
163
+ @page_links_last
164
+ end
165
+ attr_writer :page_links_last
166
+
167
+ # The class for the last page link
168
+ #
169
+ # * <tt>Default:</tt> "last_page"
170
+ # * <tt>Accepts:</tt> String, nil to disable
171
+ def page_links_last_page_class_name
172
+ @page_links_last_page_class_name ||= "last_page"
173
+ end
174
+ attr_writer :page_links_last_page_class_name
175
+
176
+ # The default for the :next option for the page_links helper.
177
+ #
178
+ # * <tt>Default:</tt> "Next >"
179
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
180
+ def page_links_next
181
+ @page_links_next ||= "Next &gt;"
182
+ end
183
+ attr_writer :page_links_next
184
+
185
+ # The class for the next page link
186
+ #
187
+ # * <tt>Default:</tt> "next_page"
188
+ # * <tt>Accepts:</tt> String, nil to disable
189
+ def page_links_next_page_class_name
190
+ @page_links_next_page_class_name ||= "next_page"
191
+ end
192
+ attr_writer :page_links_next_page_class_name
193
+
194
+ # The default for the :outer_spread option for the page_links helper.
195
+ #
196
+ # * <tt>Default:</tt> 2
197
+ # * <tt>Accepts:</tt> Any integer >= 1, set to nil to display, 0 to only show the "..." separator
198
+ def page_links_outer_spread
199
+ @page_links_outer_spread ||= 1
200
+ end
201
+ attr_writer :page_links_outer_spread
202
+
203
+ # The class for the previous page link
204
+ #
205
+ # * <tt>Default:</tt> "prev_page"
206
+ # * <tt>Accepts:</tt> String, nil to disable
207
+ def page_links_prev_page_class_name
208
+ @page_links_prev_page_class_name ||= "prev_page"
209
+ end
210
+ attr_writer :page_links_prev_page_class_name
211
+
212
+ # The default for the :prev option for the page_links helper.
213
+ #
214
+ # * <tt>Default:</tt> "< Prev"
215
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
216
+ def page_links_prev
217
+ @page_links_prev ||= "&lt; Prev"
218
+ end
219
+ attr_writer :page_links_prev
220
+
221
+ # The class name for used in the page_seect helper
222
+ #
223
+ # * <tt>Default:</tt> "page"
224
+ # * <tt>Accepts:</tt> String
225
+ def page_select_class_name
226
+ @page_select_class_name ||= "page"
227
+ end
228
+ attr_writer :page_select_class_name
229
+
230
+ # The class name for used in the per_page_link helper
231
+ #
232
+ # * <tt>Default:</tt> "per_page"
233
+ # * <tt>Accepts:</tt> String
234
+ def per_page_link_class_name
235
+ @per_page_link_class_name ||= "per_page"
236
+ end
237
+ attr_writer :per_page_link_class_name
238
+
239
+ # The choices used in the per_page_select helper
240
+ #
241
+ # * <tt>Default:</tt> [["10 per page", 10], ["25 per page", 25], ["50 per page", 50], ["100 per page", 100], ["150 per page", 150], ["200 per page", 200], ["Show all", nil]]
242
+ # * <tt>Accepts:</tt> Array
243
+ def per_page_select_choices
244
+ return @per_page_select_choices if @per_page_select_choices
245
+ @per_page_select_choices = []
246
+ [10, 25, 50, 100, 150, 200].each { |choice| @per_page_select_choices << ["#{choice} per page", choice] }
247
+ @per_page_select_choices << ["Show all", nil]
248
+ end
249
+ attr_writer :per_page_select_choices
250
+
251
+ # The class name for used in the per_page_select helper
252
+ #
253
+ # * <tt>Default:</tt> "per_page"
254
+ # * <tt>Accepts:</tt> String
255
+ def per_page_select_class_name
256
+ @per_page_select_class_name ||= "per_page"
257
+ end
258
+ attr_writer :per_page_select_class_name
259
+
260
+ # The default value for the :activate_text option for priority_order_by_link
261
+ #
262
+ # * <tt>Default:</tt> "Show %s first"
263
+ # * <tt>Accepts:</tt> String with substitutions, using rubys % method for strings
264
+ def priority_order_by_link_activate_text
265
+ @priority_order_by_link_activate_text ||= "Show %s first"
266
+ end
267
+ attr_writer :priority_order_by_link_activate_text
268
+
269
+ # The class name for used in the priority_order_by_link helper
270
+ #
271
+ # * <tt>Default:</tt> "priority_order_by"
272
+ # * <tt>Accepts:</tt> String
273
+ def priority_order_by_link_class_name
274
+ @priority_order_by_link_class_name ||= "priority_order_by"
275
+ end
276
+ attr_writer :priority_order_by_link_class_name
277
+
278
+ # The default value for the :deactivate_text option for priority_order_by_link
279
+ #
280
+ # * <tt>Default:</tt> "Dont' show %s first"
281
+ # * <tt>Accepts:</tt> String with substitutions, using rubys % method for strings
282
+ def priority_order_by_link_deactivate_text
283
+ @priority_order_by_link_deactivate_text ||= "Don't show %s first"
284
+ end
285
+ attr_writer :priority_order_by_link_deactivate_text
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,53 @@
1
+ module Searchlogic
2
+ class Config
3
+ # = Search Configuration
4
+ # Provides configuration for searchlogic
5
+ #
6
+ # === Example
7
+ # Searchlogic::Config.configure do |config|
8
+ # config.search.per_page = 50
9
+ # end
10
+ class Search
11
+ class << self
12
+ # The default for per page. This is only applicaple for protected searches. Meaning you start the search with new_search or new_conditions.
13
+ # The reason for this not to disturb regular queries such as Whatever.find(:all). You would not expect that to be limited.
14
+ #
15
+ # * <tt>Default:</tt> The 2nd option in your per_page_choices, default of 25
16
+ # * <tt>Accepts:</tt> Any value in your per_page choices, nil or a blank string means "show all"
17
+ def per_page
18
+ return @per_page if @set_per_page
19
+ per_page = Helpers.per_page_select_choices[1]
20
+ per_page = per_page.last if per_page.is_a?(Array)
21
+ @per_page = per_page
22
+ end
23
+
24
+ def per_page=(value)
25
+ @set_per_page = true
26
+ @per_page = value
27
+ end
28
+
29
+ # If you are using ActiveRecord < 2.2.0 then ActiveRecord does not remove duplicates when using the :joins option, when it should. To fix this problem searchlogic does this for you. Searchlogic tries to act
30
+ # just like ActiveRecord, but in this instance it doesn't make sense.
31
+ #
32
+ # As a result, Searchlogic removes all duplicates results in *ALL* search / calculation queries. It does this by forcing the DISTINCT or GROUP BY operation in your SQL. Which might come as a surprise to you
33
+ # since it is not the "norm". If you don't want searchlogic to do this, set this to false.
34
+ #
35
+ # * <tt>Default:</tt> true
36
+ # * <tt>Accepts:</tt> Boolean
37
+ def remove_duplicates
38
+ return @remove_duplicates if @set_remove_duplicates
39
+ @remove_duplicates ||= true
40
+ end
41
+
42
+ def remove_duplicates? # :nodoc:
43
+ remove_duplicates == true
44
+ end
45
+
46
+ def remove_duplicates=(value) # :nodoc:
47
+ @set_remove_duplicates = true
48
+ @remove_duplicates = value
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,75 @@
1
+ module Searchlogic
2
+ module CoreExt # :nodoc: all
3
+ module Hash
4
+ def deep_dup
5
+ new_hash = {}
6
+
7
+ self.each do |k, v|
8
+ case v
9
+ when Hash
10
+ new_hash[k] = v.deep_dup
11
+ else
12
+ new_hash[k] = v
13
+ end
14
+ end
15
+
16
+ new_hash
17
+ end
18
+
19
+ def deep_delete_duplicate_keys(hash)
20
+ hash.each do |k, v|
21
+ if v.is_a?(Hash) && self[k]
22
+ self[k].deep_delete_duplicate_keys(v)
23
+ delete(k) if self[k].blank?
24
+ else
25
+ delete(k)
26
+ end
27
+ end
28
+
29
+ self
30
+ end
31
+
32
+ def deep_delete(value)
33
+ case value
34
+ when Array
35
+ value.each { |v| deep_delete(v) }
36
+ when Hash
37
+ value.each do |k, v|
38
+ next unless self[k].is_a?(Hash)
39
+
40
+ case v
41
+ when Hash, Array
42
+ self[k].deep_delete(v)
43
+ when String, Symbol
44
+ self[k].delete(v)
45
+ end
46
+ end
47
+ when String, Symbol
48
+ delete(value)
49
+ end
50
+ end
51
+
52
+ def deep_merge(other_hash)
53
+ self.merge(other_hash) do |key, oldval, newval|
54
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
55
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
56
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
57
+ end
58
+ end
59
+
60
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
61
+ # Modifies the receiver in place.
62
+ def deep_merge!(other_hash)
63
+ replace(deep_merge(other_hash))
64
+ end
65
+
66
+ # assert_valid_keys was killing performance. Array.flatten was the culprit, so I rewrote this method, got a 35% performance increase
67
+ def fast_assert_valid_keys(valid_keys)
68
+ unknown_keys = keys - valid_keys
69
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ Hash.send(:include, Searchlogic::CoreExt::Hash)