active_scaffold_san 3.0.18

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 (284) hide show
  1. data/.autotest +27 -0
  2. data/.document +5 -0
  3. data/CHANGELOG +179 -0
  4. data/Gemfile +13 -0
  5. data/Gemfile.lock +20 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README +69 -0
  8. data/Rakefile +53 -0
  9. data/active_scaffold_vho.gemspec +388 -0
  10. data/frontends/default/images/add.gif +0 -0
  11. data/frontends/default/images/arrow_down.gif +0 -0
  12. data/frontends/default/images/arrow_up.gif +0 -0
  13. data/frontends/default/images/close.gif +0 -0
  14. data/frontends/default/images/config.png +0 -0
  15. data/frontends/default/images/cross.png +0 -0
  16. data/frontends/default/images/gears.png +0 -0
  17. data/frontends/default/images/indicator-small.gif +0 -0
  18. data/frontends/default/images/indicator.gif +0 -0
  19. data/frontends/default/images/magnifier.png +0 -0
  20. data/frontends/default/javascripts/jquery/active_scaffold.js +1004 -0
  21. data/frontends/default/javascripts/jquery/jquery.editinplace.js +743 -0
  22. data/frontends/default/javascripts/prototype/active_scaffold.js +1003 -0
  23. data/frontends/default/javascripts/prototype/dhtml_history.js +867 -0
  24. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  25. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  26. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  27. data/frontends/default/stylesheets/stylesheet.css +973 -0
  28. data/frontends/default/views/_action_group.html.erb +20 -0
  29. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  30. data/frontends/default/views/_base_form.html.erb +51 -0
  31. data/frontends/default/views/_create_form.html.erb +8 -0
  32. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  33. data/frontends/default/views/_field_search.html.erb +32 -0
  34. data/frontends/default/views/_form.html.erb +24 -0
  35. data/frontends/default/views/_form_association.html.erb +19 -0
  36. data/frontends/default/views/_form_association_footer.html.erb +40 -0
  37. data/frontends/default/views/_form_attribute.html.erb +15 -0
  38. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  39. data/frontends/default/views/_form_messages.html.erb +5 -0
  40. data/frontends/default/views/_horizontal_subform.html.erb +29 -0
  41. data/frontends/default/views/_horizontal_subform_header.html.erb +10 -0
  42. data/frontends/default/views/_horizontal_subform_record.html.erb +37 -0
  43. data/frontends/default/views/_human_conditions.html.erb +1 -0
  44. data/frontends/default/views/_list.html.erb +18 -0
  45. data/frontends/default/views/_list_actions.html.erb +15 -0
  46. data/frontends/default/views/_list_calculations.html.erb +16 -0
  47. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  48. data/frontends/default/views/_list_header.html.erb +10 -0
  49. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  50. data/frontends/default/views/_list_messages.html.erb +32 -0
  51. data/frontends/default/views/_list_pagination.html.erb +11 -0
  52. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  53. data/frontends/default/views/_list_record.html.erb +14 -0
  54. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  55. data/frontends/default/views/_list_with_header.html.erb +32 -0
  56. data/frontends/default/views/_messages.html.erb +10 -0
  57. data/frontends/default/views/_render_field.js.rjs +10 -0
  58. data/frontends/default/views/_row.html.erb +12 -0
  59. data/frontends/default/views/_search.html.erb +34 -0
  60. data/frontends/default/views/_search_attribute.html.erb +10 -0
  61. data/frontends/default/views/_show.html.erb +8 -0
  62. data/frontends/default/views/_show_columns.html.erb +15 -0
  63. data/frontends/default/views/_update_actions.html.erb +9 -0
  64. data/frontends/default/views/_update_form.html.erb +6 -0
  65. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  66. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  67. data/frontends/default/views/action_confirmation.html.erb +13 -0
  68. data/frontends/default/views/add_existing.js.rjs +17 -0
  69. data/frontends/default/views/add_existing_form.html.erb +5 -0
  70. data/frontends/default/views/create.html.erb +5 -0
  71. data/frontends/default/views/delete.html.erb +13 -0
  72. data/frontends/default/views/destroy.js.rjs +23 -0
  73. data/frontends/default/views/edit_associated.js.rjs +11 -0
  74. data/frontends/default/views/field_search.html.erb +5 -0
  75. data/frontends/default/views/form_messages.js.rjs +1 -0
  76. data/frontends/default/views/list.html.erb +1 -0
  77. data/frontends/default/views/list.js.rjs +1 -0
  78. data/frontends/default/views/on_action_update.js.rjs +8 -0
  79. data/frontends/default/views/on_create.js.rjs +41 -0
  80. data/frontends/default/views/on_mark_all.js.rjs +4 -0
  81. data/frontends/default/views/on_update.js.rjs +28 -0
  82. data/frontends/default/views/search.html.erb +5 -0
  83. data/frontends/default/views/show.html.erb +5 -0
  84. data/frontends/default/views/update.html.erb +8 -0
  85. data/frontends/default/views/update_column.js.rjs +13 -0
  86. data/frontends/default/views/update_row.js.rjs +1 -0
  87. data/init.rb +8 -0
  88. data/lib/active_scaffold.rb +360 -0
  89. data/lib/active_scaffold/actions/common_search.rb +22 -0
  90. data/lib/active_scaffold/actions/core.rb +180 -0
  91. data/lib/active_scaffold/actions/create.rb +149 -0
  92. data/lib/active_scaffold/actions/delete.rb +75 -0
  93. data/lib/active_scaffold/actions/field_search.rb +82 -0
  94. data/lib/active_scaffold/actions/list.rb +184 -0
  95. data/lib/active_scaffold/actions/mark.rb +63 -0
  96. data/lib/active_scaffold/actions/nested.rb +250 -0
  97. data/lib/active_scaffold/actions/search.rb +47 -0
  98. data/lib/active_scaffold/actions/show.rb +61 -0
  99. data/lib/active_scaffold/actions/subform.rb +27 -0
  100. data/lib/active_scaffold/actions/update.rb +151 -0
  101. data/lib/active_scaffold/active_record_permissions.rb +134 -0
  102. data/lib/active_scaffold/attribute_params.rb +211 -0
  103. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  104. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +39 -0
  105. data/lib/active_scaffold/bridges/bridge.rb +59 -0
  106. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  107. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +83 -0
  108. data/lib/active_scaffold/bridges/cancan/bridge.rb +12 -0
  109. data/lib/active_scaffold/bridges/cancan/lib/cancan_bridge.rb +107 -0
  110. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +9 -0
  111. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +33 -0
  112. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +12 -0
  113. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +45 -0
  114. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  115. data/lib/active_scaffold/bridges/date_picker/bridge.rb +24 -0
  116. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +225 -0
  117. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  118. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  119. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  120. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  121. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  122. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  123. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  124. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  125. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  126. data/lib/active_scaffold/bridges/paperclip/bridge.rb +12 -0
  127. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  128. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  129. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  130. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  131. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  132. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  133. data/lib/active_scaffold/bridges/shared/date_bridge.rb +209 -0
  134. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  135. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +61 -0
  136. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  137. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  138. data/lib/active_scaffold/config/base.rb +62 -0
  139. data/lib/active_scaffold/config/core.rb +220 -0
  140. data/lib/active_scaffold/config/create.rb +51 -0
  141. data/lib/active_scaffold/config/delete.rb +34 -0
  142. data/lib/active_scaffold/config/field_search.rb +75 -0
  143. data/lib/active_scaffold/config/form.rb +47 -0
  144. data/lib/active_scaffold/config/list.rb +174 -0
  145. data/lib/active_scaffold/config/mark.rb +22 -0
  146. data/lib/active_scaffold/config/nested.rb +44 -0
  147. data/lib/active_scaffold/config/search.rb +69 -0
  148. data/lib/active_scaffold/config/show.rb +35 -0
  149. data/lib/active_scaffold/config/subform.rb +35 -0
  150. data/lib/active_scaffold/config/update.rb +46 -0
  151. data/lib/active_scaffold/configurable.rb +29 -0
  152. data/lib/active_scaffold/constraints.rb +184 -0
  153. data/lib/active_scaffold/data_structures/action_columns.rb +137 -0
  154. data/lib/active_scaffold/data_structures/action_link.rb +175 -0
  155. data/lib/active_scaffold/data_structures/action_links.rb +185 -0
  156. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  157. data/lib/active_scaffold/data_structures/column.rb +355 -0
  158. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  159. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  160. data/lib/active_scaffold/data_structures/nested_info.rb +123 -0
  161. data/lib/active_scaffold/data_structures/set.rb +62 -0
  162. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  163. data/lib/active_scaffold/extensions/action_controller_rendering.rb +20 -0
  164. data/lib/active_scaffold/extensions/action_view_rendering.rb +123 -0
  165. data/lib/active_scaffold/extensions/action_view_resolver.rb +7 -0
  166. data/lib/active_scaffold/extensions/active_association_reflection.rb +13 -0
  167. data/lib/active_scaffold/extensions/active_record_offset.rb +12 -0
  168. data/lib/active_scaffold/extensions/array.rb +7 -0
  169. data/lib/active_scaffold/extensions/localize.rb +10 -0
  170. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +12 -0
  171. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +7 -0
  172. data/lib/active_scaffold/extensions/paginator_extensions.rb +26 -0
  173. data/lib/active_scaffold/extensions/reverse_associations.rb +62 -0
  174. data/lib/active_scaffold/extensions/routing_mapper.rb +34 -0
  175. data/lib/active_scaffold/extensions/to_label.rb +8 -0
  176. data/lib/active_scaffold/extensions/unsaved_associated.rb +61 -0
  177. data/lib/active_scaffold/extensions/unsaved_record.rb +20 -0
  178. data/lib/active_scaffold/extensions/usa_state.rb +46 -0
  179. data/lib/active_scaffold/finder.rb +343 -0
  180. data/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  181. data/lib/active_scaffold/helpers/controller_helpers.rb +87 -0
  182. data/lib/active_scaffold/helpers/country_helpers.rb +352 -0
  183. data/lib/active_scaffold/helpers/form_column_helpers.rb +350 -0
  184. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  185. data/lib/active_scaffold/helpers/id_helpers.rb +127 -0
  186. data/lib/active_scaffold/helpers/list_column_helpers.rb +361 -0
  187. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  188. data/lib/active_scaffold/helpers/search_column_helpers.rb +249 -0
  189. data/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  190. data/lib/active_scaffold/helpers/view_helpers.rb +360 -0
  191. data/lib/active_scaffold/locale/de.rb +120 -0
  192. data/lib/active_scaffold/locale/en.rb +119 -0
  193. data/lib/active_scaffold/locale/es.yml +115 -0
  194. data/lib/active_scaffold/locale/fr.rb +122 -0
  195. data/lib/active_scaffold/locale/hu.yml +63 -0
  196. data/lib/active_scaffold/locale/ja.yml +64 -0
  197. data/lib/active_scaffold/locale/ru.yml +119 -0
  198. data/lib/active_scaffold/marked_model.rb +38 -0
  199. data/lib/active_scaffold/paginator.rb +136 -0
  200. data/lib/active_scaffold/responds_to_parent.rb +70 -0
  201. data/lib/active_scaffold/version.rb +9 -0
  202. data/lib/active_scaffold_assets.rb +45 -0
  203. data/lib/active_scaffold_env.rb +14 -0
  204. data/lib/active_scaffold_vho.rb +2 -0
  205. data/lib/generators/active_scaffold/USAGE +29 -0
  206. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  207. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  208. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +29 -0
  209. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  210. data/lib/generators/active_scaffold_controller/templates/helper.rb +2 -0
  211. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  212. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +61 -0
  213. data/public/blank.html +33 -0
  214. data/shoulda_macros/macros.rb +136 -0
  215. data/test/bridges/bridge_test.rb +47 -0
  216. data/test/config/base_test.rb +15 -0
  217. data/test/config/create_test.rb +55 -0
  218. data/test/config/list_test.rb +74 -0
  219. data/test/config/show_test.rb +43 -0
  220. data/test/config/update_test.rb +17 -0
  221. data/test/const_mocker.rb +36 -0
  222. data/test/data_structures/action_columns_test.rb +113 -0
  223. data/test/data_structures/action_link_test.rb +78 -0
  224. data/test/data_structures/action_links_test.rb +78 -0
  225. data/test/data_structures/actions_test.rb +25 -0
  226. data/test/data_structures/association_column_test.rb +42 -0
  227. data/test/data_structures/column_test.rb +185 -0
  228. data/test/data_structures/columns_test.rb +69 -0
  229. data/test/data_structures/error_message_test.rb +28 -0
  230. data/test/data_structures/set_test.rb +86 -0
  231. data/test/data_structures/sorting_test.rb +126 -0
  232. data/test/data_structures/standard_column_test.rb +24 -0
  233. data/test/data_structures/virtual_column_test.rb +23 -0
  234. data/test/extensions/active_record_test.rb +45 -0
  235. data/test/extensions/array_test.rb +12 -0
  236. data/test/helpers/form_column_helpers_test.rb +31 -0
  237. data/test/helpers/list_column_helpers_test.rb +31 -0
  238. data/test/helpers/pagination_helpers_test.rb +55 -0
  239. data/test/misc/active_record_permissions_test.rb +154 -0
  240. data/test/misc/attribute_params_test.rb +110 -0
  241. data/test/misc/configurable_test.rb +96 -0
  242. data/test/misc/constraints_test.rb +193 -0
  243. data/test/misc/finder_test.rb +93 -0
  244. data/test/misc/lang_test.rb +12 -0
  245. data/test/mock_app/.gitignore +2 -0
  246. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  247. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  248. data/test/mock_app/config/boot.rb +110 -0
  249. data/test/mock_app/config/database.yml +16 -0
  250. data/test/mock_app/config/environment.rb +43 -0
  251. data/test/mock_app/config/environments/development.rb +17 -0
  252. data/test/mock_app/config/environments/production.rb +28 -0
  253. data/test/mock_app/config/environments/test.rb +28 -0
  254. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  255. data/test/mock_app/config/initializers/inflections.rb +10 -0
  256. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  257. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  258. data/test/mock_app/config/initializers/session_store.rb +15 -0
  259. data/test/mock_app/config/locales/en.yml +5 -0
  260. data/test/mock_app/config/routes.rb +43 -0
  261. data/test/mock_app/db/test.sqlite3 +1 -0
  262. data/test/mock_app/public/blank.html +33 -0
  263. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  264. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  265. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  266. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  267. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  268. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  269. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  270. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  271. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  272. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  273. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  274. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  275. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  276. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  277. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  278. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  279. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +842 -0
  280. data/test/model_stub.rb +55 -0
  281. data/test/run_all.rb +8 -0
  282. data/test/test_helper.rb +39 -0
  283. data/uninstall.rb +13 -0
  284. metadata +497 -0
@@ -0,0 +1,343 @@
1
+ module ActiveScaffold
2
+ module Finder
3
+ def self.like_operator
4
+ @@like_operator ||= ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
5
+ end
6
+
7
+ module ClassMethods
8
+ # Takes a collection of search terms (the tokens) and creates SQL that
9
+ # searches all specified ActiveScaffold columns. A row will match if each
10
+ # token is found in at least one of the columns.
11
+ def create_conditions_for_columns(tokens, columns, text_search = :full)
12
+ # if there aren't any columns, then just return a nil condition
13
+ return unless columns.length > 0
14
+ like_pattern = like_pattern(text_search)
15
+
16
+ tokens = [tokens] if tokens.is_a? String
17
+
18
+ where_clauses = []
19
+ columns.each do |column|
20
+ where_clauses << ((column.column.nil? || column.column.text?) ? "#{column.search_sql} #{ActiveScaffold::Finder.like_operator} ?" : "#{column.search_sql} = ?")
21
+ end
22
+ phrase = "(#{where_clauses.join(' OR ')})"
23
+
24
+ sql = ([phrase] * tokens.length).join(' AND ')
25
+ tokens = tokens.collect do |value|
26
+ columns.collect {|column| (column.column.nil? || column.column.text?) ? like_pattern.sub('?', value) : column.column.type_cast(value)}
27
+ end.flatten
28
+
29
+ [sql, *tokens]
30
+ end
31
+
32
+ # Generates an SQL condition for the given ActiveScaffold column based on
33
+ # that column's database type (or form_ui ... for virtual columns?).
34
+ # TODO: this should reside on the column, not the controller
35
+ def condition_for_column(column, value, text_search = :full)
36
+ like_pattern = like_pattern(text_search)
37
+ return unless column and column.search_sql and not value.blank?
38
+ search_ui = column.search_ui || column.column.type
39
+ begin
40
+ if self.respond_to?("condition_for_#{column.name}_column")
41
+ self.send("condition_for_#{column.name}_column", column, value, like_pattern)
42
+ elsif self.respond_to?("condition_for_#{search_ui}_type")
43
+ self.send("condition_for_#{search_ui}_type", column, value, like_pattern)
44
+ else
45
+ unless column.search_sql.instance_of? Proc
46
+ case search_ui
47
+ when :boolean, :checkbox
48
+ ["#{column.search_sql} = ?", column.column.type_cast(value)]
49
+ when :integer, :decimal, :float
50
+ condition_for_numeric(column, value)
51
+ when :string, :range
52
+ condition_for_range(column, value, like_pattern)
53
+ when :date, :time, :datetime, :timestamp
54
+ condition_for_datetime(column, value)
55
+ when :select, :multi_select, :country, :usa_state
56
+ ["#{column.search_sql} in (?)", Array(value)]
57
+ else
58
+ if column.column.nil? || column.column.text?
59
+ ["#{column.search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
60
+ else
61
+ ["#{column.search_sql} = ?", column.column.type_cast(value)]
62
+ end
63
+ end
64
+ else
65
+ column.search_sql.call(value)
66
+ end
67
+ end
68
+ rescue Exception => e
69
+ logger.error Time.now.to_s + "#{e.inspect} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{@controller.class}"
70
+ raise e
71
+ end
72
+ end
73
+
74
+ def condition_for_numeric(column, value)
75
+ if !value.is_a?(Hash)
76
+ ["#{column.search_sql} = ?", condition_value_for_numeric(column, value)]
77
+ elsif value[:from].blank? or not ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
78
+ nil
79
+ elsif value[:opt] == 'BETWEEN'
80
+ ["#{column.search_sql} BETWEEN ? AND ?", condition_value_for_numeric(column, value[:from]), condition_value_for_numeric(column, value[:to])]
81
+ else
82
+ ["#{column.search_sql} #{value[:opt]} ?", condition_value_for_numeric(column, value[:from])]
83
+ end
84
+ end
85
+
86
+ def condition_for_range(column, value, like_pattern = nil)
87
+ if !value.is_a?(Hash)
88
+ if column.column.nil? || column.column.text?
89
+ ["#{column.search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
90
+ else
91
+ ["#{column.search_sql} = ?", column.column.type_cast(value)]
92
+ end
93
+ elsif value[:from].blank?
94
+ nil
95
+ elsif ActiveScaffold::Finder::StringComparators.values.include?(value[:opt])
96
+ ["#{column.search_sql} LIKE ?", value[:opt].sub('?', value[:from])]
97
+ elsif value[:opt] == 'BETWEEN'
98
+ ["#{column.search_sql} BETWEEN ? AND ?", value[:from], value[:to]]
99
+ elsif ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
100
+ ["#{column.search_sql} #{value[:opt]} ?", value[:from]]
101
+ else
102
+ nil
103
+ end
104
+ end
105
+
106
+ def condition_value_for_datetime(value, conversion = :to_time)
107
+ if value.is_a? Hash
108
+ Time.zone.local(*[:year, :month, :day, :hour, :minute, :second].collect {|part| value[field][part].to_i}) rescue nil
109
+ elsif value.respond_to?(:strftime)
110
+ value.send(conversion)
111
+ else
112
+ Time.zone.parse(value).in_time_zone.send(conversion) rescue nil
113
+ end unless value.nil? || value.blank?
114
+ end
115
+
116
+ def condition_value_for_numeric(column, value)
117
+ return value if value.nil?
118
+ value = i18n_number_to_native_format(value) if [:i18n_number, :currency].include?(column.options[:format])
119
+ case (column.search_ui || column.column.type)
120
+ when :integer then value.to_i rescue value ? 1 : 0
121
+ when :float then value.to_f
122
+ when :decimal then ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
123
+ else
124
+ value
125
+ end
126
+ end
127
+
128
+ def i18n_number_to_native_format(value)
129
+ native = '.'
130
+ delimiter = I18n.t('number.format.delimiter')
131
+ separator = I18n.t('number.format.separator')
132
+ return value if value.blank? || !value.is_a?(String)
133
+ unless delimiter == native && !value.include?(separator) && value !~ /\.\d{3}$/
134
+ value.gsub(/[^0-9\-#{I18n.t('number.format.separator')}]/, '').gsub(I18n.t('number.format.separator'), native)
135
+ else
136
+ value
137
+ end
138
+ end
139
+
140
+ def condition_for_datetime(column, value, like_pattern = nil)
141
+ conversion = column.column.type == :date ? :to_date : :to_time
142
+ from_value = condition_value_for_datetime(value[:from], conversion)
143
+ to_value = condition_value_for_datetime(value[:to], conversion)
144
+
145
+ if from_value.nil? and to_value.nil?
146
+ nil
147
+ elsif !from_value
148
+ ["#{column.search_sql} <= ?", to_value.to_s(:db)]
149
+ elsif !to_value
150
+ ["#{column.search_sql} >= ?", from_value.to_s(:db)]
151
+ else
152
+ ["#{column.search_sql} BETWEEN ? AND ?", from_value.to_s(:db), to_value.to_s(:db)]
153
+ end
154
+ end
155
+
156
+ def condition_for_record_select_type(column, value, like_pattern = nil)
157
+ if value.is_a?(Array)
158
+ ["#{column.search_sql} IN (?)", value]
159
+ else
160
+ ["#{column.search_sql} = ?", value]
161
+ end
162
+ end
163
+
164
+ def condition_for_null_type(column, value, like_pattern = nil)
165
+ case value.to_sym
166
+ when :null
167
+ ["#{column.search_sql} is null"]
168
+ when :not_null
169
+ ["#{column.search_sql} is not null"]
170
+ else
171
+ nil
172
+ end
173
+ end
174
+
175
+ def like_pattern(text_search)
176
+ case text_search
177
+ when :full then '%?%'
178
+ when :start then '?%'
179
+ when :end then '%?'
180
+ else '?'
181
+ end
182
+ end
183
+ end
184
+
185
+ NumericComparators = [
186
+ '=',
187
+ '>=',
188
+ '<=',
189
+ '>',
190
+ '<',
191
+ '!=',
192
+ 'BETWEEN'
193
+ ]
194
+ StringComparators = {
195
+ :contains => '%?%',
196
+ :begins_with => '?%',
197
+ :ends_with => '%?'
198
+ }
199
+ NullComparators = [
200
+ :null,
201
+ :not_null
202
+ ]
203
+
204
+
205
+
206
+ def self.included(klass)
207
+ klass.extend ClassMethods
208
+ end
209
+
210
+ protected
211
+
212
+ attr_writer :active_scaffold_conditions
213
+ def active_scaffold_conditions
214
+ @active_scaffold_conditions ||= []
215
+ end
216
+
217
+ attr_writer :active_scaffold_includes
218
+ def active_scaffold_includes
219
+ @active_scaffold_includes ||= []
220
+ end
221
+
222
+ attr_writer :active_scaffold_habtm_joins
223
+ def active_scaffold_habtm_joins
224
+ @active_scaffold_habtm_joins ||= []
225
+ end
226
+
227
+ def all_conditions
228
+ merge_conditions(
229
+ active_scaffold_conditions, # from the search modules
230
+ conditions_for_collection, # from the dev
231
+ conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
232
+ conditions_from_constraints, # from any constraints (embedded scaffolds)
233
+ active_scaffold_session_storage[:conditions] # embedding conditions (weaker constraints)
234
+ )
235
+ end
236
+
237
+ # returns a single record (the given id) but only if it's allowed for the specified action.
238
+ # accomplishes this by checking model.#{action}_authorized?
239
+ # TODO: this should reside on the model, not the controller
240
+ def find_if_allowed(id, crud_type, klass = beginning_of_chain)
241
+ record = klass.find(id)
242
+ raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for?(:crud_type => crud_type.to_sym)
243
+ return record
244
+ end
245
+
246
+ # returns a Paginator::Page (not from ActiveRecord::Paginator) for the given parameters
247
+ # options may include:
248
+ # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]). please note that multi-column sorting has some limitations: if any column in a multi-field sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
249
+ # * :per_page
250
+ # * :page
251
+ # TODO: this should reside on the model, not the controller
252
+ def find_page(options = {})
253
+ options.assert_valid_keys :sorting, :per_page, :page, :count_includes, :pagination
254
+
255
+ search_conditions = all_conditions
256
+ full_includes = (active_scaffold_includes.blank? ? nil : active_scaffold_includes)
257
+ options[:per_page] ||= 999999999
258
+ options[:page] ||= 1
259
+ options[:count_includes] ||= full_includes unless search_conditions.nil?
260
+
261
+ klass = beginning_of_chain
262
+
263
+ # create a general-use options array that's compatible with Rails finders
264
+ finder_options = { :order => options[:sorting].try(:clause),
265
+ :where => search_conditions,
266
+ :joins => joins_for_finder,
267
+ :includes => options[:count_includes]}
268
+
269
+ finder_options.merge! custom_finder_options
270
+
271
+ # NOTE: we must use :include in the count query, because some conditions may reference other tables
272
+ count_query = append_to_query(klass, finder_options.reject{|k, v| [:select, :order].include?(k)})
273
+ count = count_query.count unless options[:pagination] == :infinite
274
+
275
+ # Converts count to an integer if ActiveRecord returned an OrderedHash
276
+ # that happens when finder_options contains a :group key
277
+ count = count.length if count.is_a? ActiveSupport::OrderedHash
278
+ finder_options.merge! :includes => full_includes
279
+
280
+ # we build the paginator differently for method- and sql-based sorting
281
+ if options[:sorting] and options[:sorting].sorts_by_method?
282
+ pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
283
+ sorted_collection = sort_collection_by_column(append_to_query(klass, finder_options).all, *options[:sorting].first)
284
+ sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
285
+ sorted_collection
286
+ end
287
+ else
288
+ pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
289
+ finder_options.merge!(:offset => offset, :limit => per_page) if options[:pagination]
290
+ append_to_query(klass, finder_options).all
291
+ end
292
+ end
293
+ pager.page(options[:page])
294
+ end
295
+
296
+ def append_to_query(query, options)
297
+ options.assert_valid_keys :where, :select, :group, :order, :limit, :offset, :joins, :includes, :lock, :readonly, :from
298
+ options.reject{|k, v| v.blank?}.inject(query) do |query, (k, v)|
299
+ # default ordering of model has a higher priority than current queries ordering
300
+ # fix this by removing existing ordering from arel
301
+ if k.to_sym == :order
302
+ query = query.where('1=1') unless query.is_a?(ActiveRecord::Relation)
303
+ query = query.except(:order)
304
+ end
305
+ query.send((k.to_sym), v)
306
+ end
307
+ end
308
+
309
+ def joins_for_finder
310
+ case joins_for_collection
311
+ when String
312
+ [ joins_for_collection ]
313
+ when Array
314
+ joins_for_collection
315
+ else
316
+ []
317
+ end + active_scaffold_habtm_joins
318
+ end
319
+
320
+ def merge_conditions(*conditions)
321
+ segments = []
322
+ conditions.each do |condition|
323
+ unless condition.blank?
324
+ sql = active_scaffold_config.model.send(:sanitize_sql, condition)
325
+ segments << sql unless sql.blank?
326
+ end
327
+ end
328
+ "(#{segments.join(') AND (')})" unless segments.empty?
329
+ end
330
+
331
+ # TODO: this should reside on the column, not the controller
332
+ def sort_collection_by_column(collection, column, order)
333
+ sorter = column.sort[:method]
334
+ collection = collection.sort_by { |record|
335
+ value = (sorter.is_a? Proc) ? record.instance_eval(&sorter) : record.instance_eval(sorter)
336
+ value = '' if value.nil?
337
+ value
338
+ }
339
+ collection.reverse! if order.downcase == 'desc'
340
+ collection
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveScaffold
2
+ module Helpers
3
+ module AssociationHelpers
4
+ # Provides a way to honor the :conditions on an association while searching the association's klass
5
+ def association_options_find(association, conditions = nil)
6
+ association.klass.where(conditions).where(association.options[:conditions]).all
7
+ end
8
+
9
+ def association_options_count(association, conditions = nil)
10
+ association.klass.where(conditions).where(association.options[:conditions]).count
11
+ end
12
+
13
+ # returns options for the given association as a collection of [id, label] pairs intended for the +options_for_select+ helper.
14
+ def options_for_association(association, include_all = false)
15
+ available_records = association_options_find(association, include_all ? nil : options_for_association_conditions(association))
16
+ available_records ||= []
17
+ available_records.sort{|a,b| a.to_label <=> b.to_label}.collect { |model| [ model.to_label, model.id ] }
18
+ end
19
+
20
+ def options_for_association_count(association)
21
+ association_options_count(association, options_for_association_conditions(association))
22
+ end
23
+
24
+ # A useful override for customizing the records present in an association dropdown.
25
+ # Should work in both the subform and form_ui=>:select modes.
26
+ # Check association.name to specialize the conditions per-column.
27
+ def options_for_association_conditions(association)
28
+ return nil if association.options[:through]
29
+ case association.macro
30
+ when :has_one, :has_many
31
+ # Find only orphaned objects
32
+ "#{association.primary_key_name} IS NULL"
33
+ when :belongs_to, :has_and_belongs_to_many
34
+ # Find all
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveScaffold
2
+ module Helpers
3
+ module ControllerHelpers
4
+ def self.included(controller)
5
+ controller.class_eval { helper_method :params_for, :main_path_to_return, :render_parent?, :render_parent_options, :render_parent_action, :nested_singular_association?}
6
+ end
7
+
8
+ include ActiveScaffold::Helpers::IdHelpers
9
+
10
+ def params_for(options = {})
11
+ # :adapter and :position are one-use rendering arguments. they should not propagate.
12
+ # :sort, :sort_direction, and :page are arguments that stored in the session. they need not propagate.
13
+ # and wow. no we don't want to propagate :record.
14
+ # :commit is a special rails variable for form buttons
15
+ blacklist = [:adapter, :position, :sort, :sort_direction, :page, :record, :commit, :_method, :authenticity_token, :iframe]
16
+ unless @params_for
17
+ @params_for = {}
18
+ params.select { |key, value| blacklist.exclude? key.to_sym if key }.each {|key, value| @params_for[key.to_sym] = value.duplicable? ? value.clone : value}
19
+ @params_for[:controller] = '/' + @params_for[:controller].to_s unless @params_for[:controller].to_s.first(1) == '/' # for namespaced controllers
20
+ @params_for.delete(:id) if @params_for[:id].nil?
21
+ end
22
+ @params_for.merge(options)
23
+ end
24
+
25
+ # Parameters to generate url to the main page (override if the ActiveScaffold is used as a component on another controllers page)
26
+ def main_path_to_return
27
+ if params[:return_to]
28
+ params[:return_to]
29
+ else
30
+ parameters = {}
31
+ if params[:parent_controller]
32
+ parameters[:controller] = params[:parent_controller]
33
+ parameters[:eid] = params[:parent_controller]
34
+ end
35
+ if nested?
36
+ parameters[:controller] = nested.parent_scaffold.controller_path
37
+ parameters[:eid] = nil
38
+ end
39
+ if params[:parent_sti]
40
+ parameters[:controller] = params[:parent_sti]
41
+ parameters[:eid] = nil
42
+ end
43
+ parameters[:parent_column] = nil
44
+ parameters[:parent_id] = nil
45
+ parameters[:action] = "index"
46
+ parameters[:id] = nil
47
+ parameters[:associated_id] = nil
48
+ parameters[:utf8] = nil
49
+ params_for(parameters)
50
+ end
51
+ end
52
+
53
+ def nested_singular_association?
54
+ nested? && (nested.belongs_to? || nested.has_one?)
55
+ end
56
+
57
+ def render_parent?
58
+ nested_singular_association? || params[:parent_sti]
59
+ end
60
+
61
+ def render_parent_options
62
+ if nested_singular_association?
63
+ {:controller => nested.parent_scaffold.controller_path, :action => :row, :id => nested.parent_id}
64
+ elsif params[:parent_sti]
65
+ options = {:controller => params[:parent_sti], :action => render_parent_action(params[:parent_sti])}
66
+ if render_parent_action(params[:parent_sti]) == :index
67
+ options.merge(params.slice(:eid))
68
+ else
69
+ options.merge({:id => @record.id})
70
+ end
71
+ end
72
+ end
73
+
74
+ def render_parent_action(controller_path = nil)
75
+ begin
76
+ @parent_action = :row
77
+ parent_controller = "#{controller_path.to_s.camelize}Controller".constantize
78
+ @parent_action = :index if action_name == 'create' && parent_controller.active_scaffold_config.actions.include?(:create) && parent_controller.active_scaffold_config.create.refresh_list == true
79
+ @parent_action = :index if action_name == 'update' && parent_controller.active_scaffold_config.actions.include?(:update) && parent_controller.active_scaffold_config.update.refresh_list == true
80
+ @parent_action = :index if action_name == 'destroy' && parent_controller.active_scaffold_config.actions.include?(:delete) && parent_controller.active_scaffold_config.delete.refresh_list == true
81
+ rescue ActiveScaffold::ControllerNotFound
82
+ end if @parent_action.nil?
83
+ @parent_action
84
+ end
85
+ end
86
+ end
87
+ end