active_scaffold_vho 3.0.6

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 (279) 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 +63 -0
  8. data/Rakefile +53 -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/config.png +0 -0
  14. data/frontends/default/images/cross.png +0 -0
  15. data/frontends/default/images/gears.png +0 -0
  16. data/frontends/default/images/indicator-small.gif +0 -0
  17. data/frontends/default/images/indicator.gif +0 -0
  18. data/frontends/default/images/magnifier.png +0 -0
  19. data/frontends/default/javascripts/jquery/active_scaffold.js +957 -0
  20. data/frontends/default/javascripts/jquery/jquery.editinplace.js +743 -0
  21. data/frontends/default/javascripts/prototype/active_scaffold.js +957 -0
  22. data/frontends/default/javascripts/prototype/dhtml_history.js +867 -0
  23. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  24. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  25. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  26. data/frontends/default/stylesheets/stylesheet.css +964 -0
  27. data/frontends/default/views/_action_group.html.erb +20 -0
  28. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  29. data/frontends/default/views/_base_form.html.erb +45 -0
  30. data/frontends/default/views/_create_form.html.erb +8 -0
  31. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  32. data/frontends/default/views/_field_search.html.erb +32 -0
  33. data/frontends/default/views/_form.html.erb +24 -0
  34. data/frontends/default/views/_form_association.html.erb +14 -0
  35. data/frontends/default/views/_form_association_footer.html.erb +40 -0
  36. data/frontends/default/views/_form_attribute.html.erb +15 -0
  37. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  38. data/frontends/default/views/_form_messages.html.erb +5 -0
  39. data/frontends/default/views/_horizontal_subform.html.erb +19 -0
  40. data/frontends/default/views/_horizontal_subform_header.html.erb +10 -0
  41. data/frontends/default/views/_horizontal_subform_record.html.erb +37 -0
  42. data/frontends/default/views/_human_conditions.html.erb +1 -0
  43. data/frontends/default/views/_list.html.erb +18 -0
  44. data/frontends/default/views/_list_actions.html.erb +15 -0
  45. data/frontends/default/views/_list_calculations.html.erb +16 -0
  46. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  47. data/frontends/default/views/_list_header.html.erb +10 -0
  48. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  49. data/frontends/default/views/_list_messages.html.erb +32 -0
  50. data/frontends/default/views/_list_pagination.html.erb +11 -0
  51. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  52. data/frontends/default/views/_list_record.html.erb +14 -0
  53. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  54. data/frontends/default/views/_list_with_header.html.erb +32 -0
  55. data/frontends/default/views/_messages.html.erb +10 -0
  56. data/frontends/default/views/_render_field.js.rjs +13 -0
  57. data/frontends/default/views/_row.html.erb +12 -0
  58. data/frontends/default/views/_search.html.erb +34 -0
  59. data/frontends/default/views/_search_attribute.html.erb +10 -0
  60. data/frontends/default/views/_show.html.erb +8 -0
  61. data/frontends/default/views/_show_columns.html.erb +15 -0
  62. data/frontends/default/views/_update_actions.html.erb +9 -0
  63. data/frontends/default/views/_update_form.html.erb +6 -0
  64. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  65. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  66. data/frontends/default/views/action_confirmation.html.erb +13 -0
  67. data/frontends/default/views/add_existing.js.rjs +17 -0
  68. data/frontends/default/views/add_existing_form.html.erb +5 -0
  69. data/frontends/default/views/create.html.erb +5 -0
  70. data/frontends/default/views/delete.html.erb +13 -0
  71. data/frontends/default/views/destroy.js.rjs +11 -0
  72. data/frontends/default/views/edit_associated.js.rjs +11 -0
  73. data/frontends/default/views/field_search.html.erb +5 -0
  74. data/frontends/default/views/form_messages.js.rjs +1 -0
  75. data/frontends/default/views/list.html.erb +1 -0
  76. data/frontends/default/views/list.js.rjs +1 -0
  77. data/frontends/default/views/on_action_update.js.rjs +8 -0
  78. data/frontends/default/views/on_create.js.rjs +41 -0
  79. data/frontends/default/views/on_update.js.rjs +28 -0
  80. data/frontends/default/views/search.html.erb +5 -0
  81. data/frontends/default/views/show.html.erb +5 -0
  82. data/frontends/default/views/update.html.erb +8 -0
  83. data/frontends/default/views/update_column.js.rjs +13 -0
  84. data/frontends/default/views/update_row.js.rjs +1 -0
  85. data/init.rb +9 -0
  86. data/lib/active_record_permissions.rb +134 -0
  87. data/lib/active_scaffold/actions/common_search.rb +22 -0
  88. data/lib/active_scaffold/actions/core.rb +170 -0
  89. data/lib/active_scaffold/actions/create.rb +145 -0
  90. data/lib/active_scaffold/actions/delete.rb +75 -0
  91. data/lib/active_scaffold/actions/field_search.rb +82 -0
  92. data/lib/active_scaffold/actions/list.rb +184 -0
  93. data/lib/active_scaffold/actions/mark.rb +50 -0
  94. data/lib/active_scaffold/actions/nested.rb +250 -0
  95. data/lib/active_scaffold/actions/search.rb +47 -0
  96. data/lib/active_scaffold/actions/show.rb +61 -0
  97. data/lib/active_scaffold/actions/subform.rb +17 -0
  98. data/lib/active_scaffold/actions/update.rb +141 -0
  99. data/lib/active_scaffold/attribute_params.rb +207 -0
  100. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  101. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +39 -0
  102. data/lib/active_scaffold/bridges/bridge.rb +52 -0
  103. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  104. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +79 -0
  105. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +7 -0
  106. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +38 -0
  107. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +26 -0
  108. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +35 -0
  109. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  110. data/lib/active_scaffold/bridges/date_picker/bridge.rb +22 -0
  111. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +225 -0
  112. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  113. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  114. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  115. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  116. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  117. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  118. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  119. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  120. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  121. data/lib/active_scaffold/bridges/paperclip/bridge.rb +10 -0
  122. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  123. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  124. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  125. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  126. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  127. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  128. data/lib/active_scaffold/bridges/shared/date_bridge.rb +187 -0
  129. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  130. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +45 -0
  131. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  132. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  133. data/lib/active_scaffold/config/base.rb +62 -0
  134. data/lib/active_scaffold/config/core.rb +220 -0
  135. data/lib/active_scaffold/config/create.rb +51 -0
  136. data/lib/active_scaffold/config/delete.rb +34 -0
  137. data/lib/active_scaffold/config/field_search.rb +75 -0
  138. data/lib/active_scaffold/config/form.rb +47 -0
  139. data/lib/active_scaffold/config/list.rb +174 -0
  140. data/lib/active_scaffold/config/mark.rb +22 -0
  141. data/lib/active_scaffold/config/nested.rb +44 -0
  142. data/lib/active_scaffold/config/search.rb +69 -0
  143. data/lib/active_scaffold/config/show.rb +35 -0
  144. data/lib/active_scaffold/config/subform.rb +35 -0
  145. data/lib/active_scaffold/config/update.rb +46 -0
  146. data/lib/active_scaffold/configurable.rb +29 -0
  147. data/lib/active_scaffold/constraints.rb +184 -0
  148. data/lib/active_scaffold/data_structures/action_columns.rb +133 -0
  149. data/lib/active_scaffold/data_structures/action_link.rb +171 -0
  150. data/lib/active_scaffold/data_structures/action_links.rb +175 -0
  151. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  152. data/lib/active_scaffold/data_structures/column.rb +351 -0
  153. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  154. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  155. data/lib/active_scaffold/data_structures/nested_info.rb +123 -0
  156. data/lib/active_scaffold/data_structures/set.rb +62 -0
  157. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  158. data/lib/active_scaffold/finder.rb +333 -0
  159. data/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  160. data/lib/active_scaffold/helpers/controller_helpers.rb +82 -0
  161. data/lib/active_scaffold/helpers/country_helpers.rb +352 -0
  162. data/lib/active_scaffold/helpers/form_column_helpers.rb +347 -0
  163. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  164. data/lib/active_scaffold/helpers/id_helpers.rb +127 -0
  165. data/lib/active_scaffold/helpers/list_column_helpers.rb +361 -0
  166. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  167. data/lib/active_scaffold/helpers/search_column_helpers.rb +243 -0
  168. data/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  169. data/lib/active_scaffold/helpers/view_helpers.rb +356 -0
  170. data/lib/active_scaffold/locale/de.rb +120 -0
  171. data/lib/active_scaffold/locale/en.rb +119 -0
  172. data/lib/active_scaffold/locale/es.yml +115 -0
  173. data/lib/active_scaffold/locale/fr.rb +116 -0
  174. data/lib/active_scaffold/locale/hu.yml +63 -0
  175. data/lib/active_scaffold/locale/ja.yml +64 -0
  176. data/lib/active_scaffold/locale/ru.yml +119 -0
  177. data/lib/active_scaffold/marked_model.rb +38 -0
  178. data/lib/active_scaffold/version.rb +9 -0
  179. data/lib/active_scaffold.rb +345 -0
  180. data/lib/active_scaffold_assets.rb +45 -0
  181. data/lib/dhtml_confirm.rb +54 -0
  182. data/lib/environment.rb +14 -0
  183. data/lib/extensions/action_controller_rendering.rb +20 -0
  184. data/lib/extensions/action_view_rendering.rb +113 -0
  185. data/lib/extensions/action_view_resolver.rb +7 -0
  186. data/lib/extensions/active_association_reflection.rb +13 -0
  187. data/lib/extensions/active_record_offset.rb +12 -0
  188. data/lib/extensions/array.rb +7 -0
  189. data/lib/extensions/localize.rb +10 -0
  190. data/lib/extensions/name_option_for_datetime.rb +12 -0
  191. data/lib/extensions/nil_id_in_url_params.rb +7 -0
  192. data/lib/extensions/paginator_extensions.rb +26 -0
  193. data/lib/extensions/reverse_associations.rb +62 -0
  194. data/lib/extensions/routing_mapper.rb +34 -0
  195. data/lib/extensions/to_label.rb +8 -0
  196. data/lib/extensions/unsaved_associated.rb +61 -0
  197. data/lib/extensions/unsaved_record.rb +20 -0
  198. data/lib/extensions/usa_state.rb +46 -0
  199. data/lib/generators/active_scaffold/USAGE +29 -0
  200. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  201. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  202. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +28 -0
  203. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  204. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  205. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +53 -0
  206. data/lib/paginator.rb +136 -0
  207. data/lib/responds_to_parent.rb +70 -0
  208. data/public/blank.html +33 -0
  209. data/shoulda_macros/macros.rb +136 -0
  210. data/test/bridges/bridge_test.rb +47 -0
  211. data/test/config/base_test.rb +15 -0
  212. data/test/config/create_test.rb +55 -0
  213. data/test/config/list_test.rb +74 -0
  214. data/test/config/show_test.rb +43 -0
  215. data/test/config/update_test.rb +17 -0
  216. data/test/const_mocker.rb +36 -0
  217. data/test/data_structures/action_columns_test.rb +113 -0
  218. data/test/data_structures/action_link_test.rb +78 -0
  219. data/test/data_structures/action_links_test.rb +78 -0
  220. data/test/data_structures/actions_test.rb +25 -0
  221. data/test/data_structures/association_column_test.rb +42 -0
  222. data/test/data_structures/column_test.rb +185 -0
  223. data/test/data_structures/columns_test.rb +69 -0
  224. data/test/data_structures/error_message_test.rb +28 -0
  225. data/test/data_structures/set_test.rb +86 -0
  226. data/test/data_structures/sorting_test.rb +126 -0
  227. data/test/data_structures/standard_column_test.rb +24 -0
  228. data/test/data_structures/virtual_column_test.rb +23 -0
  229. data/test/extensions/active_record_test.rb +45 -0
  230. data/test/extensions/array_test.rb +12 -0
  231. data/test/helpers/form_column_helpers_test.rb +31 -0
  232. data/test/helpers/list_column_helpers_test.rb +31 -0
  233. data/test/helpers/pagination_helpers_test.rb +55 -0
  234. data/test/misc/active_record_permissions_test.rb +154 -0
  235. data/test/misc/attribute_params_test.rb +110 -0
  236. data/test/misc/configurable_test.rb +96 -0
  237. data/test/misc/constraints_test.rb +193 -0
  238. data/test/misc/finder_test.rb +93 -0
  239. data/test/misc/lang_test.rb +12 -0
  240. data/test/mock_app/.gitignore +2 -0
  241. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  242. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  243. data/test/mock_app/config/boot.rb +110 -0
  244. data/test/mock_app/config/database.yml +16 -0
  245. data/test/mock_app/config/environment.rb +43 -0
  246. data/test/mock_app/config/environments/development.rb +17 -0
  247. data/test/mock_app/config/environments/production.rb +28 -0
  248. data/test/mock_app/config/environments/test.rb +28 -0
  249. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  250. data/test/mock_app/config/initializers/inflections.rb +10 -0
  251. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  252. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  253. data/test/mock_app/config/initializers/session_store.rb +15 -0
  254. data/test/mock_app/config/locales/en.yml +5 -0
  255. data/test/mock_app/config/routes.rb +43 -0
  256. data/test/mock_app/db/test.sqlite3 +1 -0
  257. data/test/mock_app/public/blank.html +33 -0
  258. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  259. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  260. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  261. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  262. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  263. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  264. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  265. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  266. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  267. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  268. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  269. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  270. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  271. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  272. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  273. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  274. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +839 -0
  275. data/test/model_stub.rb +55 -0
  276. data/test/run_all.rb +8 -0
  277. data/test/test_helper.rb +39 -0
  278. data/uninstall.rb +13 -0
  279. metadata +492 -0
@@ -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,351 @@
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 = value.is_a?(Array) ? value : [value] # automatically convert to an array
157
+ end
158
+
159
+ # a collection of columns to load when eager loading is disabled, if it's nil all columns will be loaded
160
+ attr_accessor :select_columns
161
+
162
+ # describes how to search on a column
163
+ # search = true default, uses intelligent search sql
164
+ # 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.
165
+ attr_writer :search_sql
166
+ def search_sql
167
+ self.initialize_search_sql if @search_sql === true
168
+ @search_sql
169
+ end
170
+ def searchable?
171
+ search_sql != false && search_sql != nil
172
+ end
173
+
174
+ # to modify the default order of columns
175
+ attr_accessor :weight
176
+
177
+ # to set how many associated records a column with plural association must show in list
178
+ cattr_accessor :associated_limit
179
+ @@associated_limit = 3
180
+ attr_accessor :associated_limit
181
+
182
+ # whether the number of associated records must be shown or not
183
+ cattr_accessor :associated_number
184
+ @@associated_number = true
185
+ attr_writer :associated_number
186
+ def associated_number?
187
+ @associated_number
188
+ end
189
+
190
+ # whether a blank row must be shown in the subform
191
+ cattr_accessor :show_blank_record
192
+ @@show_blank_record = true
193
+ attr_writer :show_blank_record
194
+ def show_blank_record?(associated)
195
+ if @show_blank_record
196
+ return false unless self.association.klass.authorized_for?(:crud_type => :create)
197
+ self.plural_association? or (self.singular_association? and associated.empty?)
198
+ end
199
+ end
200
+
201
+ # methods for automatic links in singular association columns
202
+ cattr_accessor :actions_for_association_links
203
+ @@actions_for_association_links = [:new, :edit, :show]
204
+ attr_accessor :actions_for_association_links
205
+
206
+ cattr_accessor :association_form_ui
207
+ @@association_form_ui = nil
208
+
209
+ # ----------------------------------------------------------------- #
210
+ # the below functionality is intended for internal consumption only #
211
+ # ----------------------------------------------------------------- #
212
+
213
+ # the ConnectionAdapter::*Column object from the ActiveRecord class
214
+ attr_reader :column
215
+
216
+ # the association from the ActiveRecord class
217
+ attr_reader :association
218
+ def singular_association?
219
+ self.association and [:has_one, :belongs_to].include? self.association.macro
220
+ end
221
+ def plural_association?
222
+ self.association and [:has_many, :has_and_belongs_to_many].include? self.association.macro
223
+ end
224
+ def through_association?
225
+ self.association and self.association.options[:through]
226
+ end
227
+ def polymorphic_association?
228
+ self.association and self.association.options.has_key? :polymorphic and self.association.options[:polymorphic]
229
+ end
230
+ def readonly_association?
231
+ if self.association
232
+ if self.association.options.has_key? :readonly
233
+ self.association.options[:readonly]
234
+ else
235
+ self.through_association?
236
+ end
237
+ end
238
+ end
239
+
240
+ # an interpreted property. the column is virtual if it isn't from the active record model or any associated models
241
+ def virtual?
242
+ column.nil? && association.nil?
243
+ end
244
+
245
+ # this is so that array.delete and array.include?, etc., will work by column name
246
+ def ==(other) #:nodoc:
247
+ # another column
248
+ if other.respond_to? :name and other.class == self.class
249
+ self.name == other.name.to_sym
250
+ # a string or symbol
251
+ elsif other.respond_to? :to_sym
252
+ self.name == other.to_sym rescue false # catch "interning empty string"
253
+ # unknown
254
+ else
255
+ self.eql? other
256
+ end
257
+ end
258
+
259
+ # instantiation is handled internally through the DataStructures::Columns object
260
+ def initialize(name, active_record_class) #:nodoc:
261
+ self.name = name.to_sym
262
+ @column = active_record_class.columns_hash[self.name.to_s]
263
+ @association = active_record_class.reflect_on_association(self.name)
264
+ @autolink = !@association.nil?
265
+ @active_record_class = active_record_class
266
+ @table = active_record_class.table_name
267
+ @associated_limit = self.class.associated_limit
268
+ @associated_number = self.class.associated_number
269
+ @show_blank_record = self.class.show_blank_record
270
+ @actions_for_association_links = self.class.actions_for_association_links.clone if @association
271
+ @options = {:format => :i18n_number} if @column.try(:number?)
272
+ @form_ui = :checkbox if @column and @column.type == :boolean
273
+ @allow_add_existing = true
274
+ @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui
275
+
276
+ # default all the configurable variables
277
+ self.css_class = ''
278
+ self.required = active_record_class.validators_on(self.name).map(&:class).include? ActiveModel::Validations::PresenceValidator
279
+ self.sort = true
280
+ self.search_sql = true
281
+
282
+ @weight = estimate_weight
283
+
284
+ self.includes = (association and not polymorphic_association?) ? [association.name] : []
285
+ end
286
+
287
+ # just the field (not table.field)
288
+ def field_name
289
+ return nil if virtual?
290
+ column ? @active_record_class.connection.quote_column_name(column.name) : association.primary_key_name
291
+ end
292
+
293
+ def <=>(other_column)
294
+ order_weight = self.weight <=> other_column.weight
295
+ order_weight != 0 ? order_weight : self.name.to_s <=> other_column.name.to_s
296
+ end
297
+
298
+ protected
299
+
300
+ def initialize_sort
301
+ if self.virtual?
302
+ # we don't automatically enable method sorting for virtual columns because it's slow, and we expect fewer complaints this way.
303
+ self.sort = false
304
+ else
305
+ if self.singular_association?
306
+ self.sort = {:method => "#{self.name}.to_s"}
307
+ elsif self.plural_association?
308
+ self.sort = {:method => "#{self.name}.join(',')"}
309
+ else
310
+ self.sort = {:sql => self.field}
311
+ end
312
+ end
313
+ end
314
+
315
+ def initialize_search_sql
316
+ self.search_sql = unless self.virtual?
317
+ if association.nil?
318
+ self.field.to_s
319
+ elsif !self.polymorphic_association?
320
+ [association.klass.table_name, association.klass.primary_key].collect! do |str|
321
+ association.klass.connection.quote_column_name str
322
+ end.join('.')
323
+ end
324
+ end
325
+ end
326
+
327
+ # the table name from the ActiveRecord class
328
+ attr_reader :table
329
+
330
+ # the table.field name for this column, if applicable
331
+ def field
332
+ @field ||= [@active_record_class.connection.quote_column_name(@table), field_name].join('.')
333
+ end
334
+
335
+ def estimate_weight
336
+ if singular_association?
337
+ 400
338
+ elsif plural_association?
339
+ 500
340
+ elsif [:created_at, :updated_at].include?(self.name)
341
+ 600
342
+ elsif [:name, :label, :title].include?(self.name)
343
+ 100
344
+ elsif required?
345
+ 200
346
+ else
347
+ 300
348
+ end
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveScaffold::DataStructures
2
+ class Columns
3
+ include Enumerable
4
+ include ActiveScaffold::Configurable
5
+
6
+ # The motivation for this collection is that this Columns data structure fills two roles: it provides
7
+ # the master list of all known columns, and it provides an inheritable list for all other actions (e.g.
8
+ # Create and Update and List). Well we actually want to *know* about as many columns as possible, so
9
+ # we don't want people actually removing columns from @set. But at the same time, we want to be able to
10
+ # manage which columns get inherited. Tada!
11
+ #
12
+ # This collection is referenced by other parts of ActiveScaffold and by methods within this DataStructure.
13
+ # IT IS NOT MEANT FOR PUBLIC USE (but if you know what you're doing, go ahead)
14
+ def _inheritable=(value)
15
+ @sorted = true
16
+ @_inheritable = value
17
+ end
18
+
19
+ # This accessor is used by ActionColumns to create new Column objects without adding them to this set
20
+ attr_reader :active_record_class
21
+
22
+ def initialize(active_record_class, *args)
23
+ @active_record_class = active_record_class
24
+ @_inheritable = []
25
+ @set = []
26
+
27
+ self.add *args
28
+ end
29
+
30
+ # the way to add columns to the set. this is primarily useful for virtual columns.
31
+ # note that this also makes columns inheritable
32
+ def add(*args)
33
+ args.flatten! # allow [] as a param
34
+ args = args.collect{ |a| a.to_sym }
35
+
36
+ # make the columns inheritable
37
+ @_inheritable.concat(args)
38
+ # then add columns to @set (unless they already exist)
39
+ args.each { |a| @set << ActiveScaffold::DataStructures::Column.new(a.to_sym, @active_record_class) unless find_by_name(a) }
40
+ end
41
+ alias_method :<<, :add
42
+
43
+ def exclude(*args)
44
+ # only remove columns from _inheritable. we never want to completely forget about a column.
45
+ args.each { |a| @_inheritable.delete a }
46
+ end
47
+
48
+ # returns an array of columns with the provided names
49
+ def find_by_names(*names)
50
+ @set.find_all { |column| names.include? column.name }
51
+ end
52
+
53
+ # returns the column of the given name.
54
+ def find_by_name(name)
55
+ # this works because of `def column.=='
56
+ column = @set.find { |c| c == name }
57
+ column
58
+ end
59
+ alias_method :[], :find_by_name
60
+
61
+ def each
62
+ @set.each {|i| yield i }
63
+ end
64
+
65
+ def _inheritable
66
+ if @sorted
67
+ @_inheritable
68
+ else
69
+ @_inheritable.sort do |a, b|
70
+ self[a] <=> self[b]
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveScaffold::DataStructures
2
+ # Wrapper for error strings so that they may be exported using to_xxx
3
+ class ErrorMessage
4
+ def initialize(error)
5
+ @error = error
6
+ end
7
+
8
+ def public_attributes
9
+ { :error => @error }
10
+ end
11
+
12
+ def to_xml
13
+ public_attributes.to_xml(:root => "errors")
14
+ end
15
+
16
+ def to_yaml
17
+ public_attributes.to_yaml
18
+ end
19
+
20
+ def to_s
21
+ @error
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,123 @@
1
+ module ActiveScaffold::DataStructures
2
+ class NestedInfo
3
+ def self.get(model, session_storage)
4
+ if session_storage[:nested].nil?
5
+ nil
6
+ else
7
+ session_info = session_storage[:nested].clone
8
+ begin
9
+ session_info[:parent_scaffold] = "#{session_info[:parent_scaffold].to_s.camelize}Controller".constantize
10
+ session_info[:parent_model] = session_info[:parent_scaffold].active_scaffold_config.model
11
+ session_info[:association] = session_info[:parent_model].reflect_on_association(session_info[:name])
12
+ unless session_info[:association].nil?
13
+ ActiveScaffold::DataStructures::NestedInfoAssociation.new(model, session_info)
14
+ else
15
+ ActiveScaffold::DataStructures::NestedInfoScope.new(model, session_info)
16
+ end
17
+ rescue ActiveScaffold::ControllerNotFound
18
+ nil
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_accessor :association, :child_association, :parent_model, :parent_scaffold, :parent_id, :constrained_fields, :scope
24
+
25
+ def initialize(model, session_info)
26
+ @parent_model = session_info[:parent_model]
27
+ @parent_id = session_info[:parent_id]
28
+ @parent_scaffold = session_info[:parent_scaffold]
29
+ end
30
+
31
+ def new_instance?
32
+ result = @new_instance.nil?
33
+ @new_instance = false
34
+ result
35
+ end
36
+
37
+ def parent_scope
38
+ parent_model.find(parent_id)
39
+ end
40
+
41
+ def habtm?
42
+ false
43
+ end
44
+
45
+ def belongs_to?
46
+ false
47
+ end
48
+
49
+ def has_one?
50
+ false
51
+ end
52
+
53
+ def readonly?
54
+ false
55
+ end
56
+
57
+ def sorted?
58
+ false
59
+ end
60
+ end
61
+
62
+ class NestedInfoAssociation < NestedInfo
63
+ def initialize(model, session_info)
64
+ super(model, session_info)
65
+ @association = session_info[:association]
66
+ iterate_model_associations(model)
67
+ end
68
+
69
+ def habtm?
70
+ association.macro == :has_and_belongs_to_many
71
+ end
72
+
73
+ def belongs_to?
74
+ association.belongs_to?
75
+ end
76
+
77
+ def has_one?
78
+ association.macro == :has_one
79
+ end
80
+
81
+ def readonly?
82
+ if association.options.has_key? :readonly
83
+ association.options[:readonly]
84
+ else
85
+ association.options.has_key? :through
86
+ end
87
+ end
88
+
89
+ def sorted?
90
+ association.options.has_key? :order
91
+ end
92
+
93
+ def default_sorting
94
+ association.options[:order]
95
+ end
96
+
97
+ protected
98
+
99
+ def iterate_model_associations(model)
100
+ @constrained_fields = []
101
+ @constrained_fields << association.primary_key_name.to_sym unless association.belongs_to?
102
+ model.reflect_on_all_associations.each do |current|
103
+ if !current.belongs_to? && association.primary_key_name == current.association_foreign_key
104
+ constrained_fields << current.name.to_sym
105
+ @child_association = current
106
+ end
107
+ if association.primary_key_name == current.primary_key_name
108
+ # show columns for has_many and has_one child associationes
109
+ constrained_fields << current.name.to_sym if current.belongs_to?
110
+ @child_association = current
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ class NestedInfoScope < NestedInfo
117
+ def initialize(model, session_info)
118
+ super(model, session_info)
119
+ @scope = session_info[:name]
120
+ @constrained_fields = []
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,62 @@
1
+ module ActiveScaffold::DataStructures
2
+ class Set
3
+ include Enumerable
4
+ include ActiveScaffold::Configurable
5
+
6
+ attr_writer :label
7
+ def label
8
+ as_(@label)
9
+ end
10
+
11
+ def initialize(*args)
12
+ @set = []
13
+ self.add *args
14
+ end
15
+
16
+ # the way to add items to the set.
17
+ def add(*args)
18
+ args.flatten! # allow [] as a param
19
+ args.each { |arg|
20
+ arg = arg.to_sym if arg.is_a? String
21
+ @set << arg unless @set.include? arg # avoid duplicates
22
+ }
23
+ end
24
+ alias_method :<<, :add
25
+
26
+ # the way to remove items from the set.
27
+ def exclude(*args)
28
+ args.flatten! # allow [] as a param
29
+ args.collect! { |a| a.to_sym } # symbolize the args
30
+ # check respond_to? :to_sym, ActionColumns doesn't respond to to_sym
31
+ @set.reject! { |c| c.respond_to? :to_sym and args.include? c.to_sym } # reject all items specified
32
+ end
33
+ alias_method :remove, :exclude
34
+
35
+ # returns an array of items with the provided names
36
+ def find_by_names(*names)
37
+ @set.find_all { |item| names.include? item }
38
+ end
39
+
40
+ # returns the item of the given name.
41
+ def find_by_name(name)
42
+ # this works because of `def item.=='
43
+ item = @set.find { |c| c == name }
44
+ item
45
+ end
46
+ alias_method :[], :find_by_name
47
+
48
+ def each
49
+ @set.each {|i| yield i }
50
+ end
51
+
52
+ # returns the number of items in the set
53
+ def length
54
+ @set.length
55
+ end
56
+
57
+ def empty?
58
+ @set.empty?
59
+ end
60
+
61
+ end
62
+ end