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,185 @@
1
+ module ActiveScaffold::DataStructures
2
+ class ActionLinks
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @set = []
7
+ @name = :root
8
+ end
9
+
10
+ # adds an ActionLink, creating one from the arguments if need be
11
+ def add(action, options = {})
12
+ link = if action.is_a?(ActiveScaffold::DataStructures::ActionLink) || action.is_a?(ActiveScaffold::DataStructures::ActionLinks)
13
+ action
14
+ else
15
+ ActiveScaffold::DataStructures::ActionLink.new(action, options)
16
+ end
17
+ # NOTE: this duplicate check should be done by defining the comparison operator for an Action data structure
18
+ existing = find_duplicate(link)
19
+ unless existing
20
+ # That s for backwards compatibility if we are in root of action_links
21
+ # we have to move actionlink into members or collection subgroup
22
+ group = (name == :root ? subgroup(link.type, link.type) : self)
23
+ group.add_to_set(link)
24
+ link
25
+ else
26
+ existing
27
+ end
28
+ end
29
+ alias_method :<<, :add
30
+
31
+ def add_to_set(link)
32
+ @set << link
33
+ end
34
+
35
+ # adds a link to a specific group
36
+ # groups are represented as a string separated by a dot
37
+ # eg member.crud
38
+ def add_to_group(link, group = nil)
39
+ add_to = root
40
+ add_to = group.split('.').inject(root){|group, group_name| group.send(group_name)} if group
41
+ add_to << link unless link.nil?
42
+ end
43
+
44
+ # finds an ActionLink by matching the action
45
+ def [](val)
46
+ links = []
47
+ @set.each do |item|
48
+ if item.is_a?(ActiveScaffold::DataStructures::ActionLinks)
49
+ collected = item[val]
50
+ links << collected unless collected.nil?
51
+ else
52
+ links << item if item.action == val.to_s
53
+ end
54
+ end
55
+ links.first
56
+ end
57
+
58
+ def find_duplicate(link)
59
+ links = []
60
+ @set.each do |item|
61
+ if item.is_a?(ActiveScaffold::DataStructures::ActionLinks)
62
+ collected = item.find_duplicate(link)
63
+ links << collected unless collected.nil?
64
+ else
65
+ links << item if item.action == link.action and item.static_controller? && item.controller == link.controller and item.parameters == link.parameters
66
+ end
67
+ end
68
+ links.first
69
+ end
70
+
71
+ def delete(val)
72
+ self.each({:include_set => true}) do |link, set|
73
+ if link.action == val.to_s
74
+ set.delete_if {|item| item.is_a?(ActiveScaffold::DataStructures::ActionLink) && item.action == val.to_s}
75
+ end
76
+ end
77
+ end
78
+
79
+ def delete_group(name)
80
+ @set.each do |group|
81
+ if group.name == name
82
+ @set.delete_if {|item| item.is_a?(ActiveScaffold::DataStructures::ActionLinks) && item.name == name}
83
+ else
84
+ group.delete_group(name)
85
+ end if group.is_a?(ActiveScaffold::DataStructures::ActionLinks)
86
+ end
87
+ end
88
+
89
+ # iterates over the links, possibly by type
90
+ def each(options = {}, &block)
91
+ @set.each {|item|
92
+ if item.is_a?(ActiveScaffold::DataStructures::ActionLinks)
93
+ item.each(options, &block)
94
+ else
95
+ if options[:include_set]
96
+ yield item, @set
97
+ else
98
+ yield item
99
+ end
100
+ end
101
+ }
102
+ end
103
+
104
+ def collect_by_type(type = nil)
105
+ links = []
106
+ subgroup(type).each(type) {|link| links << link}
107
+ links
108
+ end
109
+
110
+ def traverse(controller, options = {}, &block)
111
+ traverse_method = options.delete(:reverse).nil? ? :each : :reverse_each
112
+ options[:level] ||= -1
113
+ options[:level] += 1
114
+ first_action = true
115
+ @set.send(traverse_method) do |link|
116
+ if link.is_a?(ActiveScaffold::DataStructures::ActionLinks)
117
+ unless link.empty?
118
+ yield(link, nil, {:node => :start_traversing, :first_action => first_action, :level => options[:level]})
119
+ link.traverse(controller,options, &block)
120
+ yield(link, nil, {:node => :finished_traversing, :first_action => first_action, :level => options[:level]})
121
+ first_action = false
122
+ end
123
+ elsif controller.nil? || !skip_action_link(controller, link, *(Array(options[:for])))
124
+ authorized = options[:for].nil? ? true : options[:for].authorized_for?(:crud_type => link.crud_type, :action => link.action)
125
+ yield(self, link, {:authorized => authorized, :first_action => first_action, :level => options[:level]})
126
+ first_action = false
127
+ end
128
+ end
129
+ options[:level] -= 1
130
+ end
131
+
132
+ def collect
133
+ @set
134
+ end
135
+
136
+ def empty?
137
+ @set.size == 0
138
+ end
139
+
140
+ def subgroup(name, label = nil)
141
+ group = self if name == self.name
142
+ group ||= @set.find do |item|
143
+ name == item.name if item.is_a?(ActiveScaffold::DataStructures::ActionLinks)
144
+ end
145
+
146
+ if group.nil?
147
+ group = ActiveScaffold::DataStructures::ActionLinks.new
148
+ group.label = label || name
149
+ group.name = name
150
+ add_to_set group
151
+ end
152
+ group
153
+ end
154
+
155
+ attr_writer :label
156
+ def label
157
+ as_(@label) if @label
158
+ end
159
+
160
+ def method_missing(name, *args, &block)
161
+ class_eval %{
162
+ def #{name}
163
+ @#{name} ||= subgroup('#{name}'.to_sym)
164
+ yield @#{name} if block_given?
165
+ @#{name}
166
+ end
167
+ }
168
+ send(name, &block)
169
+ end
170
+
171
+ attr_accessor :name
172
+
173
+ protected
174
+
175
+ def skip_action_link(controller, link, *args)
176
+ (!link.ignore_method.nil? and controller.try(link.ignore_method, *args)) || ((link.security_method_set? or controller.respond_to? link.security_method) and !controller.send(link.security_method, *args))
177
+ end
178
+
179
+ # called during clone or dup. makes the clone/dup deeper.
180
+ def initialize_copy(from)
181
+ @set = []
182
+ from.instance_variable_get('@set').each { |link| @set << link.clone }
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,45 @@
1
+ class ActiveScaffold::DataStructures::Actions
2
+ include Enumerable
3
+
4
+ def initialize(*args)
5
+ @set = []
6
+ self.add *args
7
+ end
8
+
9
+ def exclude(*args)
10
+ args.collect! { |a| a.to_sym } # symbolize the args
11
+ @set.reject! { |m| args.include? m } # reject all actions specified
12
+ end
13
+
14
+ def add(*args)
15
+ args.each { |arg| @set << arg.to_sym unless @set.include? arg.to_sym }
16
+ end
17
+ alias_method :<<, :add
18
+
19
+ def each
20
+ @set.each {|item| yield item}
21
+ end
22
+
23
+ def include?(val)
24
+ super val.to_sym
25
+ end
26
+
27
+ # swaps one element in the list with the other.
28
+ # accepts arguments in any order. it just figures out which one is in the list and which one is not.
29
+ def swap(one, two)
30
+ if include? one
31
+ exclude one
32
+ add two
33
+ else
34
+ exclude two
35
+ add one
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ # called during clone or dup. makes the clone/dup deeper.
42
+ def initialize_copy(from)
43
+ @set = from.instance_variable_get('@set').clone
44
+ end
45
+ end
@@ -0,0 +1,355 @@
1
+ module ActiveScaffold::DataStructures
2
+ class Column
3
+ include ActiveScaffold::Configurable
4
+
5
+ attr_reader :active_record_class
6
+
7
+ # this is the name of the getter on the ActiveRecord model. it is the only absolutely required attribute ... all others will be inferred from this name.
8
+ attr_accessor :name
9
+
10
+ # Whether to enable inplace editing for this column. Currently works for text columns, in the List.
11
+ attr_reader :inplace_edit
12
+ def inplace_edit=(value)
13
+ self.clear_link if value
14
+ @inplace_edit = value
15
+ end
16
+
17
+ # Whether this column set is collapsed by default in contexts where collapsing is supported
18
+ attr_accessor :collapsed
19
+
20
+ # Whether to enable add_existing for this column
21
+ attr_accessor :allow_add_existing
22
+
23
+ # Any extra parameters this particular column uses. This is for create/update purposes.
24
+ def params
25
+ # lazy initialize
26
+ @params ||= Set.new
27
+ end
28
+
29
+ # the display-name of the column. this will be used, for instance, as the column title in the table and as the field name in the form.
30
+ # if left alone it will utilize human_attribute_name which includes localization
31
+ attr_writer :label
32
+ def label
33
+ as_(@label) || active_record_class.human_attribute_name(name.to_s)
34
+ end
35
+
36
+ # a textual description of the column and its contents. this will be displayed with any associated form input widget, so you may want to consider adding a content example.
37
+ attr_writer :description
38
+ def description
39
+ if @description
40
+ @description
41
+ else
42
+ I18n.t name, :scope => [:activerecord, :description, active_record_class.to_s.underscore.to_sym], :default => ''
43
+ end
44
+ end
45
+
46
+ # this will be /joined/ to the :name for the td's class attribute. useful if you want to style columns on different ActiveScaffolds the same way, but the columns have different names.
47
+ attr_accessor :css_class
48
+
49
+ # whether the field is required or not. used on the form for visually indicating the fact to the user.
50
+ # TODO: move into predicate
51
+ attr_writer :required
52
+ def required?
53
+ @required
54
+ end
55
+
56
+ attr_reader :update_columns
57
+
58
+ # update dependent columns after value change in form
59
+ # update_columns = :name
60
+ # update_columns = [:name, :age]
61
+ def update_columns=(column_names)
62
+ @update_columns = Array(column_names)
63
+ end
64
+
65
+ # sorting on a column can be configured four ways:
66
+ # sort = true default, uses intelligent sorting sql default
67
+ # sort = false sometimes sorting doesn't make sense
68
+ # sort = {:sql => ""} define your own sql for sorting. this should be result in a sortable value in SQL. ActiveScaffold will handle the ascending/descending.
69
+ # sort = {:method => ""} define ruby-side code for sorting. this is SLOW with large recordsets!
70
+ def sort=(value)
71
+ if value.is_a? Hash
72
+ value.assert_valid_keys(:sql, :method)
73
+ @sort = value
74
+ else
75
+ @sort = value ? true : false # force true or false
76
+ end
77
+ end
78
+
79
+ def sort
80
+ self.initialize_sort if @sort === true
81
+ @sort
82
+ end
83
+
84
+ def sortable?
85
+ sort != false && !sort.nil?
86
+ end
87
+
88
+ # a configuration helper for the self.sort property. simply provides a method syntax instead of setter syntax.
89
+ def sort_by(options)
90
+ self.sort = options
91
+ end
92
+
93
+ # supported options:
94
+ # * for association columns
95
+ # * :select - displays a simple <select> or a collection of checkboxes to (dis)associate records
96
+ attr_writer :form_ui
97
+ def form_ui
98
+ @form_ui
99
+ end
100
+
101
+ attr_writer :list_ui
102
+ def list_ui
103
+ @list_ui || @form_ui
104
+ end
105
+
106
+ attr_writer :search_ui
107
+ def search_ui
108
+ @search_ui || @form_ui || (@association && !polymorphic_association? ? :select : nil)
109
+ end
110
+
111
+ # a place to store dev's column specific options
112
+ attr_accessor :options
113
+ def options
114
+ @options ||= {}
115
+ end
116
+
117
+ def link
118
+ @link = @link.call(self) if @link.is_a? Proc
119
+ @link
120
+ end
121
+
122
+ # associate an action_link with this column
123
+ def set_link(action, options = {})
124
+ if action.is_a?(ActiveScaffold::DataStructures::ActionLink) || (action.is_a? Proc)
125
+ @link = action
126
+ else
127
+ options[:label] ||= self.label
128
+ options[:position] ||= :after unless options.has_key?(:position)
129
+ options[:type] ||= :member
130
+ @link = ActiveScaffold::DataStructures::ActionLink.new(action, options)
131
+ end
132
+ end
133
+
134
+ # set an action_link to nested list or inline form in this column
135
+ def autolink?
136
+ @autolink
137
+ end
138
+
139
+ # this should not only delete any existing link but also prevent column links from being automatically added by later routines
140
+ def clear_link
141
+ @link = nil
142
+ @autolink = false
143
+ end
144
+
145
+ # define a calculation for the column. anything that ActiveRecord::Calculations::ClassMethods#calculate accepts will do.
146
+ attr_accessor :calculate
147
+
148
+ # get whether to run a calculation on this column
149
+ def calculation?
150
+ !(@calculate == false or @calculate.nil?)
151
+ end
152
+
153
+ # a collection of associations to pre-load when finding the records on a page
154
+ attr_reader :includes
155
+ def includes=(value)
156
+ @includes = case value
157
+ when Array, Hash then value
158
+ else [value] # automatically convert to an array
159
+ end
160
+ end
161
+
162
+ # a collection of columns to load when eager loading is disabled, if it's nil all columns will be loaded
163
+ attr_accessor :select_columns
164
+
165
+ # describes how to search on a column
166
+ # search = true default, uses intelligent search sql
167
+ # search = "CONCAT(a, b)" define your own sql for searching. this should be the "left-side" of a WHERE condition. the operator and value will be supplied by ActiveScaffold.
168
+ attr_writer :search_sql
169
+ def search_sql
170
+ self.initialize_search_sql if @search_sql === true
171
+ @search_sql
172
+ end
173
+ def searchable?
174
+ search_sql != false && search_sql != nil
175
+ end
176
+
177
+ # to modify the default order of columns
178
+ attr_accessor :weight
179
+
180
+ # to set how many associated records a column with plural association must show in list
181
+ cattr_accessor :associated_limit
182
+ @@associated_limit = 3
183
+ attr_accessor :associated_limit
184
+
185
+ # whether the number of associated records must be shown or not
186
+ cattr_accessor :associated_number
187
+ @@associated_number = true
188
+ attr_writer :associated_number
189
+ def associated_number?
190
+ @associated_number
191
+ end
192
+
193
+ # whether a blank row must be shown in the subform
194
+ cattr_accessor :show_blank_record
195
+ @@show_blank_record = true
196
+ attr_writer :show_blank_record
197
+ def show_blank_record?(associated)
198
+ if @show_blank_record
199
+ return false unless self.association.klass.authorized_for?(:crud_type => :create)
200
+ self.plural_association? or (self.singular_association? and associated.empty?)
201
+ end
202
+ end
203
+
204
+ # methods for automatic links in singular association columns
205
+ cattr_accessor :actions_for_association_links
206
+ @@actions_for_association_links = [:new, :edit, :show]
207
+ attr_accessor :actions_for_association_links
208
+
209
+ cattr_accessor :association_form_ui
210
+ @@association_form_ui = nil
211
+
212
+ # ----------------------------------------------------------------- #
213
+ # the below functionality is intended for internal consumption only #
214
+ # ----------------------------------------------------------------- #
215
+
216
+ # the ConnectionAdapter::*Column object from the ActiveRecord class
217
+ attr_reader :column
218
+
219
+ # the association from the ActiveRecord class
220
+ attr_reader :association
221
+ def singular_association?
222
+ self.association and [:has_one, :belongs_to].include? self.association.macro
223
+ end
224
+ def plural_association?
225
+ self.association and [:has_many, :has_and_belongs_to_many].include? self.association.macro
226
+ end
227
+ def through_association?
228
+ self.association and self.association.options[:through]
229
+ end
230
+ def polymorphic_association?
231
+ self.association and self.association.options.has_key? :polymorphic and self.association.options[:polymorphic]
232
+ end
233
+ def readonly_association?
234
+ if self.association
235
+ if self.association.options.has_key? :readonly
236
+ self.association.options[:readonly]
237
+ else
238
+ self.through_association?
239
+ end
240
+ end
241
+ end
242
+
243
+ # an interpreted property. the column is virtual if it isn't from the active record model or any associated models
244
+ def virtual?
245
+ column.nil? && association.nil?
246
+ end
247
+
248
+ # this is so that array.delete and array.include?, etc., will work by column name
249
+ def ==(other) #:nodoc:
250
+ # another column
251
+ if other.respond_to? :name and other.class == self.class
252
+ self.name == other.name.to_sym
253
+ # a string or symbol
254
+ elsif other.respond_to? :to_sym
255
+ self.name == other.to_sym rescue false # catch "interning empty string"
256
+ # unknown
257
+ else
258
+ self.eql? other
259
+ end
260
+ end
261
+
262
+ # instantiation is handled internally through the DataStructures::Columns object
263
+ def initialize(name, active_record_class) #:nodoc:
264
+ self.name = name.to_sym
265
+ @column = active_record_class.columns_hash[self.name.to_s]
266
+ @association = active_record_class.reflect_on_association(self.name)
267
+ @autolink = !@association.nil?
268
+ @active_record_class = active_record_class
269
+ @table = active_record_class.table_name
270
+ @associated_limit = self.class.associated_limit
271
+ @associated_number = self.class.associated_number
272
+ @show_blank_record = self.class.show_blank_record
273
+ @actions_for_association_links = self.class.actions_for_association_links.clone if @association
274
+ @options = {:format => :i18n_number} if @column.try(:number?)
275
+ @form_ui = :checkbox if @column and @column.type == :boolean
276
+ @form_ui = :textarea if @column and @column.type == :text
277
+ @allow_add_existing = true
278
+ @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui
279
+
280
+ # default all the configurable variables
281
+ self.css_class = ''
282
+ self.required = active_record_class.validators_on(self.name).map(&:class).include? ActiveModel::Validations::PresenceValidator
283
+ self.sort = true
284
+ self.search_sql = true
285
+
286
+ @weight = estimate_weight
287
+
288
+ self.includes = (association and not polymorphic_association?) ? [association.name] : []
289
+ end
290
+
291
+ # just the field (not table.field)
292
+ def field_name
293
+ return nil if virtual?
294
+ column ? @active_record_class.connection.quote_column_name(column.name) : association.primary_key_name
295
+ end
296
+
297
+ def <=>(other_column)
298
+ order_weight = self.weight <=> other_column.weight
299
+ order_weight != 0 ? order_weight : self.name.to_s <=> other_column.name.to_s
300
+ end
301
+
302
+ protected
303
+
304
+ def initialize_sort
305
+ if self.virtual?
306
+ # we don't automatically enable method sorting for virtual columns because it's slow, and we expect fewer complaints this way.
307
+ self.sort = false
308
+ else
309
+ if self.singular_association?
310
+ self.sort = {:method => "#{self.name}.to_s"}
311
+ elsif self.plural_association?
312
+ self.sort = {:method => "#{self.name}.join(',')"}
313
+ else
314
+ self.sort = {:sql => self.field}
315
+ end
316
+ end
317
+ end
318
+
319
+ def initialize_search_sql
320
+ self.search_sql = unless self.virtual?
321
+ if association.nil?
322
+ self.field.to_s
323
+ elsif !self.polymorphic_association?
324
+ [association.klass.table_name, association.klass.primary_key].collect! do |str|
325
+ association.klass.connection.quote_column_name str
326
+ end.join('.')
327
+ end
328
+ end
329
+ end
330
+
331
+ # the table name from the ActiveRecord class
332
+ attr_reader :table
333
+
334
+ # the table.field name for this column, if applicable
335
+ def field
336
+ @field ||= [@active_record_class.connection.quote_column_name(@table), field_name].join('.')
337
+ end
338
+
339
+ def estimate_weight
340
+ if singular_association?
341
+ 400
342
+ elsif plural_association?
343
+ 500
344
+ elsif [:created_at, :updated_at].include?(self.name)
345
+ 600
346
+ elsif [:name, :label, :title].include?(self.name)
347
+ 100
348
+ elsif required?
349
+ 200
350
+ else
351
+ 300
352
+ end
353
+ end
354
+ end
355
+ end