active_scaffold 3.0.0

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 (272) hide show
  1. data/.autotest +27 -0
  2. data/CHANGELOG +152 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README +51 -0
  6. data/Rakefile +24 -0
  7. data/active_scaffold.gemspec +24 -0
  8. data/environment.rb +22 -0
  9. data/frontends/default/images/add.gif +0 -0
  10. data/frontends/default/images/arrow_down.gif +0 -0
  11. data/frontends/default/images/arrow_up.gif +0 -0
  12. data/frontends/default/images/close.gif +0 -0
  13. data/frontends/default/images/cross.png +0 -0
  14. data/frontends/default/images/indicator-small.gif +0 -0
  15. data/frontends/default/images/indicator.gif +0 -0
  16. data/frontends/default/images/magnifier.png +0 -0
  17. data/frontends/default/javascripts/jquery/active_scaffold.js +957 -0
  18. data/frontends/default/javascripts/jquery/jquery.editinplace.js +726 -0
  19. data/frontends/default/javascripts/prototype/active_scaffold.js +954 -0
  20. data/frontends/default/javascripts/prototype/dhtml_history.js +867 -0
  21. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  22. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  23. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  24. data/frontends/default/stylesheets/stylesheet.css +858 -0
  25. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  26. data/frontends/default/views/_base_form.html.erb +41 -0
  27. data/frontends/default/views/_create_form.html.erb +6 -0
  28. data/frontends/default/views/_create_form_on_list.html.erb +5 -0
  29. data/frontends/default/views/_field_search.html.erb +32 -0
  30. data/frontends/default/views/_form.html.erb +24 -0
  31. data/frontends/default/views/_form_association.html.erb +14 -0
  32. data/frontends/default/views/_form_association_footer.html.erb +40 -0
  33. data/frontends/default/views/_form_attribute.html.erb +15 -0
  34. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  35. data/frontends/default/views/_form_messages.html.erb +5 -0
  36. data/frontends/default/views/_horizontal_subform.html.erb +19 -0
  37. data/frontends/default/views/_horizontal_subform_header.html.erb +10 -0
  38. data/frontends/default/views/_horizontal_subform_record.html.erb +37 -0
  39. data/frontends/default/views/_human_conditions.html.erb +1 -0
  40. data/frontends/default/views/_list.html.erb +18 -0
  41. data/frontends/default/views/_list_actions.html.erb +16 -0
  42. data/frontends/default/views/_list_calculations.html.erb +16 -0
  43. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  44. data/frontends/default/views/_list_header.html.erb +12 -0
  45. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  46. data/frontends/default/views/_list_messages.html.erb +32 -0
  47. data/frontends/default/views/_list_pagination.html.erb +11 -0
  48. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  49. data/frontends/default/views/_list_record.html.erb +14 -0
  50. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  51. data/frontends/default/views/_list_with_header.html.erb +32 -0
  52. data/frontends/default/views/_messages.html.erb +10 -0
  53. data/frontends/default/views/_render_field.js.rjs +13 -0
  54. data/frontends/default/views/_row.html.erb +12 -0
  55. data/frontends/default/views/_search.html.erb +34 -0
  56. data/frontends/default/views/_search_attribute.html.erb +10 -0
  57. data/frontends/default/views/_show.html.erb +8 -0
  58. data/frontends/default/views/_show_columns.html.erb +12 -0
  59. data/frontends/default/views/_update_actions.html.erb +9 -0
  60. data/frontends/default/views/_update_form.html.erb +5 -0
  61. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  62. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  63. data/frontends/default/views/add_existing.js.rjs +17 -0
  64. data/frontends/default/views/add_existing_form.html.erb +5 -0
  65. data/frontends/default/views/create.html.erb +5 -0
  66. data/frontends/default/views/delete.html.erb +13 -0
  67. data/frontends/default/views/destroy.js.rjs +5 -0
  68. data/frontends/default/views/edit_associated.js.rjs +11 -0
  69. data/frontends/default/views/field_search.html.erb +5 -0
  70. data/frontends/default/views/form_messages.js.rjs +1 -0
  71. data/frontends/default/views/list.html.erb +1 -0
  72. data/frontends/default/views/list.js.rjs +1 -0
  73. data/frontends/default/views/on_action_update.js.rjs +8 -0
  74. data/frontends/default/views/on_create.js.rjs +24 -0
  75. data/frontends/default/views/on_update.js.rjs +15 -0
  76. data/frontends/default/views/search.html.erb +5 -0
  77. data/frontends/default/views/show.html.erb +5 -0
  78. data/frontends/default/views/update.html.erb +8 -0
  79. data/frontends/default/views/update_column.js.rjs +13 -0
  80. data/frontends/default/views/update_row.js.rjs +1 -0
  81. data/init.rb +1 -0
  82. data/install_assets.rb +44 -0
  83. data/lib/active_record_permissions.rb +134 -0
  84. data/lib/active_scaffold.rb +279 -0
  85. data/lib/active_scaffold/actions/common_search.rb +22 -0
  86. data/lib/active_scaffold/actions/core.rb +150 -0
  87. data/lib/active_scaffold/actions/create.rb +152 -0
  88. data/lib/active_scaffold/actions/delete.rb +72 -0
  89. data/lib/active_scaffold/actions/field_search.rb +82 -0
  90. data/lib/active_scaffold/actions/list.rb +128 -0
  91. data/lib/active_scaffold/actions/mark.rb +50 -0
  92. data/lib/active_scaffold/actions/nested.rb +241 -0
  93. data/lib/active_scaffold/actions/search.rb +47 -0
  94. data/lib/active_scaffold/actions/show.rb +54 -0
  95. data/lib/active_scaffold/actions/subform.rb +17 -0
  96. data/lib/active_scaffold/actions/update.rb +134 -0
  97. data/lib/active_scaffold/attribute_params.rb +207 -0
  98. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  99. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +38 -0
  100. data/lib/active_scaffold/bridges/bridge.rb +52 -0
  101. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  102. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +79 -0
  103. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +7 -0
  104. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +38 -0
  105. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +26 -0
  106. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +35 -0
  107. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  108. data/lib/active_scaffold/bridges/date_picker/bridge.rb +22 -0
  109. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +225 -0
  110. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  111. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  112. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  113. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  114. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  115. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  116. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  117. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  118. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  119. data/lib/active_scaffold/bridges/paperclip/bridge.rb +12 -0
  120. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  121. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  122. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  123. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  124. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  125. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  126. data/lib/active_scaffold/bridges/shared/date_bridge.rb +187 -0
  127. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  128. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +45 -0
  129. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  130. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  131. data/lib/active_scaffold/config/base.rb +54 -0
  132. data/lib/active_scaffold/config/core.rb +229 -0
  133. data/lib/active_scaffold/config/create.rb +43 -0
  134. data/lib/active_scaffold/config/delete.rb +25 -0
  135. data/lib/active_scaffold/config/field_search.rb +74 -0
  136. data/lib/active_scaffold/config/form.rb +46 -0
  137. data/lib/active_scaffold/config/list.rb +174 -0
  138. data/lib/active_scaffold/config/mark.rb +22 -0
  139. data/lib/active_scaffold/config/nested.rb +43 -0
  140. data/lib/active_scaffold/config/search.rb +68 -0
  141. data/lib/active_scaffold/config/show.rb +34 -0
  142. data/lib/active_scaffold/config/subform.rb +35 -0
  143. data/lib/active_scaffold/config/update.rb +38 -0
  144. data/lib/active_scaffold/configurable.rb +29 -0
  145. data/lib/active_scaffold/constraints.rb +179 -0
  146. data/lib/active_scaffold/data_structures/action_columns.rb +133 -0
  147. data/lib/active_scaffold/data_structures/action_link.rb +162 -0
  148. data/lib/active_scaffold/data_structures/action_links.rb +59 -0
  149. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  150. data/lib/active_scaffold/data_structures/column.rb +348 -0
  151. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  152. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  153. data/lib/active_scaffold/data_structures/nested_info.rb +108 -0
  154. data/lib/active_scaffold/data_structures/set.rb +62 -0
  155. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  156. data/lib/active_scaffold/finder.rb +333 -0
  157. data/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  158. data/lib/active_scaffold/helpers/controller_helpers.rb +40 -0
  159. data/lib/active_scaffold/helpers/country_helpers.rb +352 -0
  160. data/lib/active_scaffold/helpers/form_column_helpers.rb +343 -0
  161. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  162. data/lib/active_scaffold/helpers/id_helpers.rb +131 -0
  163. data/lib/active_scaffold/helpers/list_column_helpers.rb +363 -0
  164. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  165. data/lib/active_scaffold/helpers/search_column_helpers.rb +238 -0
  166. data/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  167. data/lib/active_scaffold/helpers/view_helpers.rb +315 -0
  168. data/lib/active_scaffold/locale/de.rb +113 -0
  169. data/lib/active_scaffold/locale/en.rb +118 -0
  170. data/lib/active_scaffold/locale/es.yml +112 -0
  171. data/lib/active_scaffold/locale/fr.rb +113 -0
  172. data/lib/active_scaffold/locale/hu.yml +63 -0
  173. data/lib/active_scaffold/locale/ja.yml +64 -0
  174. data/lib/active_scaffold/locale/ru.yml +62 -0
  175. data/lib/active_scaffold/marked_model.rb +38 -0
  176. data/lib/dhtml_confirm.rb +54 -0
  177. data/lib/extensions/action_controller_rendering.rb +20 -0
  178. data/lib/extensions/action_view_rendering.rb +113 -0
  179. data/lib/extensions/action_view_resolver.rb +7 -0
  180. data/lib/extensions/active_record_offset.rb +12 -0
  181. data/lib/extensions/array.rb +7 -0
  182. data/lib/extensions/localize.rb +10 -0
  183. data/lib/extensions/name_option_for_datetime.rb +12 -0
  184. data/lib/extensions/nil_id_in_url_params.rb +7 -0
  185. data/lib/extensions/paginator_extensions.rb +26 -0
  186. data/lib/extensions/reverse_associations.rb +62 -0
  187. data/lib/extensions/routing_mapper.rb +34 -0
  188. data/lib/extensions/to_label.rb +8 -0
  189. data/lib/extensions/unsaved_associated.rb +61 -0
  190. data/lib/extensions/unsaved_record.rb +20 -0
  191. data/lib/extensions/usa_state.rb +46 -0
  192. data/lib/generators/active_scaffold/USAGE +29 -0
  193. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  194. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  195. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +28 -0
  196. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  197. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  198. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +53 -0
  199. data/lib/paginator.rb +136 -0
  200. data/lib/responds_to_parent.rb +70 -0
  201. data/public/blank.html +33 -0
  202. data/shoulda_macros/macros.rb +136 -0
  203. data/test/bridges/bridge_test.rb +47 -0
  204. data/test/config/base_test.rb +15 -0
  205. data/test/config/create_test.rb +55 -0
  206. data/test/config/list_test.rb +74 -0
  207. data/test/config/show_test.rb +43 -0
  208. data/test/config/update_test.rb +17 -0
  209. data/test/const_mocker.rb +36 -0
  210. data/test/data_structures/action_columns_test.rb +113 -0
  211. data/test/data_structures/action_link_test.rb +78 -0
  212. data/test/data_structures/action_links_test.rb +78 -0
  213. data/test/data_structures/actions_test.rb +25 -0
  214. data/test/data_structures/association_column_test.rb +42 -0
  215. data/test/data_structures/column_test.rb +185 -0
  216. data/test/data_structures/columns_test.rb +69 -0
  217. data/test/data_structures/error_message_test.rb +28 -0
  218. data/test/data_structures/set_test.rb +86 -0
  219. data/test/data_structures/sorting_test.rb +126 -0
  220. data/test/data_structures/standard_column_test.rb +24 -0
  221. data/test/data_structures/virtual_column_test.rb +23 -0
  222. data/test/extensions/active_record_test.rb +45 -0
  223. data/test/extensions/array_test.rb +12 -0
  224. data/test/helpers/form_column_helpers_test.rb +31 -0
  225. data/test/helpers/list_column_helpers_test.rb +31 -0
  226. data/test/helpers/pagination_helpers_test.rb +55 -0
  227. data/test/misc/active_record_permissions_test.rb +154 -0
  228. data/test/misc/attribute_params_test.rb +110 -0
  229. data/test/misc/configurable_test.rb +96 -0
  230. data/test/misc/constraints_test.rb +193 -0
  231. data/test/misc/finder_test.rb +93 -0
  232. data/test/misc/lang_test.rb +12 -0
  233. data/test/mock_app/.gitignore +2 -0
  234. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  235. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  236. data/test/mock_app/config/boot.rb +110 -0
  237. data/test/mock_app/config/database.yml +16 -0
  238. data/test/mock_app/config/environment.rb +43 -0
  239. data/test/mock_app/config/environments/development.rb +17 -0
  240. data/test/mock_app/config/environments/production.rb +28 -0
  241. data/test/mock_app/config/environments/test.rb +28 -0
  242. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  243. data/test/mock_app/config/initializers/inflections.rb +10 -0
  244. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  245. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  246. data/test/mock_app/config/initializers/session_store.rb +15 -0
  247. data/test/mock_app/config/locales/en.yml +5 -0
  248. data/test/mock_app/config/routes.rb +43 -0
  249. data/test/mock_app/db/test.sqlite3 +1 -0
  250. data/test/mock_app/public/blank.html +33 -0
  251. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  252. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  253. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  254. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  255. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  256. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  257. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  258. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  259. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  260. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  261. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  262. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  263. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  264. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  265. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  266. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  267. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +839 -0
  268. data/test/model_stub.rb +55 -0
  269. data/test/run_all.rb +8 -0
  270. data/test/test_helper.rb +39 -0
  271. data/uninstall.rb +13 -0
  272. metadata +478 -0
@@ -0,0 +1,59 @@
1
+ module ActiveScaffold::DataStructures
2
+ class ActionLinks
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @set = []
7
+ end
8
+
9
+ # adds an ActionLink, creating one from the arguments if need be
10
+ def add(action, options = {})
11
+ link = action.is_a?(ActiveScaffold::DataStructures::ActionLink) ? action : ActiveScaffold::DataStructures::ActionLink.new(action, options)
12
+ # NOTE: this duplicate check should be done by defining the comparison operator for an Action data structure
13
+ existing = @set.find {|a| a.action == link.action and a.controller == link.controller and a.parameters == link.parameters}
14
+ unless existing
15
+ @set << link
16
+ link
17
+ else
18
+ existing
19
+ end
20
+ end
21
+ alias_method :<<, :add
22
+
23
+ # finds an ActionLink by matching the action
24
+ def [](val)
25
+ @set.find {|item| item.action == val.to_s}
26
+ end
27
+
28
+ def delete(val)
29
+ @set.delete_if{|item| item.action == val.to_s}
30
+ end
31
+
32
+ # iterates over the links, possibly by type
33
+ def each(type = nil)
34
+ type = type.to_sym if type
35
+ @set.each {|item|
36
+ next if type and item.type != type
37
+ yield item
38
+ }
39
+ end
40
+
41
+ def collect_by_type(type = nil)
42
+ links = []
43
+ each(type) {|link| links << link}
44
+ links
45
+ end
46
+
47
+ def empty?
48
+ @set.size == 0
49
+ end
50
+
51
+ protected
52
+
53
+ # called during clone or dup. makes the clone/dup deeper.
54
+ def initialize_copy(from)
55
+ @set = []
56
+ from.instance_variable_get('@set').each { |link| @set << link.clone }
57
+ end
58
+ end
59
+ 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,348 @@
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
+ # associate an action_link with this column
118
+ attr_reader :link
119
+
120
+ # set an action_link to nested list or inline form in this column
121
+ def autolink?
122
+ @autolink
123
+ end
124
+
125
+ # this should not only delete any existing link but also prevent column links from being automatically added by later routines
126
+ def clear_link
127
+ @link = nil
128
+ @autolink = false
129
+ end
130
+
131
+ def set_link(action, options = {})
132
+ if action.is_a? ActiveScaffold::DataStructures::ActionLink
133
+ @link = action
134
+ else
135
+ options[:label] ||= self.label
136
+ options[:position] ||= :after unless options.has_key?(:position)
137
+ options[:type] ||= :member
138
+ @link = ActiveScaffold::DataStructures::ActionLink.new(action, options)
139
+ end
140
+ end
141
+
142
+ # define a calculation for the column. anything that ActiveRecord::Calculations::ClassMethods#calculate accepts will do.
143
+ attr_accessor :calculate
144
+
145
+ # get whether to run a calculation on this column
146
+ def calculation?
147
+ !(@calculate == false or @calculate.nil?)
148
+ end
149
+
150
+ # a collection of associations to pre-load when finding the records on a page
151
+ attr_reader :includes
152
+ def includes=(value)
153
+ @includes = value.is_a?(Array) ? value : [value] # automatically convert to an array
154
+ end
155
+
156
+ # a collection of columns to load when eager loading is disabled, if it's nil all columns will be loaded
157
+ attr_accessor :select_columns
158
+
159
+ # describes how to search on a column
160
+ # search = true default, uses intelligent search sql
161
+ # 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.
162
+ attr_writer :search_sql
163
+ def search_sql
164
+ self.initialize_search_sql if @search_sql === true
165
+ @search_sql
166
+ end
167
+ def searchable?
168
+ search_sql != false && search_sql != nil
169
+ end
170
+
171
+ # to modify the default order of columns
172
+ attr_accessor :weight
173
+
174
+ # to set how many associated records a column with plural association must show in list
175
+ cattr_accessor :associated_limit
176
+ @@associated_limit = 3
177
+ attr_accessor :associated_limit
178
+
179
+ # whether the number of associated records must be shown or not
180
+ cattr_accessor :associated_number
181
+ @@associated_number = true
182
+ attr_writer :associated_number
183
+ def associated_number?
184
+ @associated_number
185
+ end
186
+
187
+ # whether a blank row must be shown in the subform
188
+ cattr_accessor :show_blank_record
189
+ @@show_blank_record = true
190
+ attr_writer :show_blank_record
191
+ def show_blank_record?(associated)
192
+ if @show_blank_record
193
+ return false unless self.association.klass.authorized_for?(:crud_type => :create)
194
+ self.plural_association? or (self.singular_association? and associated.empty?)
195
+ end
196
+ end
197
+
198
+ # methods for automatic links in singular association columns
199
+ cattr_accessor :actions_for_association_links
200
+ @@actions_for_association_links = [:new, :edit, :show]
201
+ attr_accessor :actions_for_association_links
202
+
203
+ cattr_accessor :association_form_ui
204
+ @@association_form_ui = nil
205
+
206
+ # ----------------------------------------------------------------- #
207
+ # the below functionality is intended for internal consumption only #
208
+ # ----------------------------------------------------------------- #
209
+
210
+ # the ConnectionAdapter::*Column object from the ActiveRecord class
211
+ attr_reader :column
212
+
213
+ # the association from the ActiveRecord class
214
+ attr_reader :association
215
+ def singular_association?
216
+ self.association and [:has_one, :belongs_to].include? self.association.macro
217
+ end
218
+ def plural_association?
219
+ self.association and [:has_many, :has_and_belongs_to_many].include? self.association.macro
220
+ end
221
+ def through_association?
222
+ self.association and self.association.options[:through]
223
+ end
224
+ def polymorphic_association?
225
+ self.association and self.association.options.has_key? :polymorphic and self.association.options[:polymorphic]
226
+ end
227
+ def readonly_association?
228
+ if self.association
229
+ if self.association.options.has_key? :readonly
230
+ self.association.options[:readonly]
231
+ else
232
+ self.through_association?
233
+ end
234
+ end
235
+ end
236
+
237
+ # an interpreted property. the column is virtual if it isn't from the active record model or any associated models
238
+ def virtual?
239
+ column.nil? && association.nil?
240
+ end
241
+
242
+ # this is so that array.delete and array.include?, etc., will work by column name
243
+ def ==(other) #:nodoc:
244
+ # another column
245
+ if other.respond_to? :name and other.class == self.class
246
+ self.name == other.name.to_sym
247
+ # a string or symbol
248
+ elsif other.respond_to? :to_sym
249
+ self.name == other.to_sym rescue false # catch "interning empty string"
250
+ # unknown
251
+ else
252
+ self.eql? other
253
+ end
254
+ end
255
+
256
+ # instantiation is handled internally through the DataStructures::Columns object
257
+ def initialize(name, active_record_class) #:nodoc:
258
+ self.name = name.to_sym
259
+ @column = active_record_class.columns_hash[self.name.to_s]
260
+ @association = active_record_class.reflect_on_association(self.name)
261
+ @autolink = !@association.nil?
262
+ @active_record_class = active_record_class
263
+ @table = active_record_class.table_name
264
+ @associated_limit = self.class.associated_limit
265
+ @associated_number = self.class.associated_number
266
+ @show_blank_record = self.class.show_blank_record
267
+ @actions_for_association_links = self.class.actions_for_association_links.clone if @association
268
+ @options = {:format => :i18n_number} if @column.try(:number?)
269
+ @form_ui = :checkbox if @column and @column.type == :boolean
270
+ @allow_add_existing = true
271
+ @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui
272
+
273
+ # default all the configurable variables
274
+ self.css_class = ''
275
+ self.required = active_record_class.validators_on(self.name).map(&:class).include? ActiveModel::Validations::PresenceValidator
276
+ self.sort = true
277
+ self.search_sql = true
278
+
279
+ @weight = estimate_weight
280
+
281
+ self.includes = (association and not polymorphic_association?) ? [association.name] : []
282
+ end
283
+
284
+ # just the field (not table.field)
285
+ def field_name
286
+ return nil if virtual?
287
+ column ? @active_record_class.connection.quote_column_name(column.name) : association.primary_key_name
288
+ end
289
+
290
+ def <=>(other_column)
291
+ order_weight = self.weight <=> other_column.weight
292
+ order_weight != 0 ? order_weight : self.name.to_s <=> other_column.name.to_s
293
+ end
294
+
295
+ protected
296
+
297
+ def initialize_sort
298
+ if self.virtual?
299
+ # we don't automatically enable method sorting for virtual columns because it's slow, and we expect fewer complaints this way.
300
+ self.sort = false
301
+ else
302
+ if self.singular_association?
303
+ self.sort = {:method => "#{self.name}.to_s"}
304
+ elsif self.plural_association?
305
+ self.sort = {:method => "#{self.name}.join(',')"}
306
+ else
307
+ self.sort = {:sql => self.field}
308
+ end
309
+ end
310
+ end
311
+
312
+ def initialize_search_sql
313
+ self.search_sql = unless self.virtual?
314
+ if association.nil?
315
+ self.field.to_s
316
+ elsif !self.polymorphic_association?
317
+ [association.klass.table_name, association.klass.primary_key].collect! do |str|
318
+ association.klass.connection.quote_column_name str
319
+ end.join('.')
320
+ end
321
+ end
322
+ end
323
+
324
+ # the table name from the ActiveRecord class
325
+ attr_reader :table
326
+
327
+ # the table.field name for this column, if applicable
328
+ def field
329
+ @field ||= [@active_record_class.connection.quote_column_name(@table), field_name].join('.')
330
+ end
331
+
332
+ def estimate_weight
333
+ if singular_association?
334
+ 400
335
+ elsif plural_association?
336
+ 500
337
+ elsif [:created_at, :updated_at].include?(self.name)
338
+ 600
339
+ elsif [:name, :label, :title].include?(self.name)
340
+ 100
341
+ elsif required?
342
+ 200
343
+ else
344
+ 300
345
+ end
346
+ end
347
+ end
348
+ end