active_scaffold 3.2.18 → 3.2.19

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 (246) hide show
  1. data/CHANGELOG +3 -0
  2. data/app/assets/images/active_scaffold/add.png +0 -0
  3. data/app/assets/images/active_scaffold/arrow_down.png +0 -0
  4. data/app/assets/images/active_scaffold/arrow_up.png +0 -0
  5. data/app/assets/images/active_scaffold/close.png +0 -0
  6. data/app/assets/images/active_scaffold/close_touch.png +0 -0
  7. data/app/assets/images/active_scaffold/config.png +0 -0
  8. data/app/assets/images/active_scaffold/cross.png +0 -0
  9. data/app/assets/images/active_scaffold/gears.png +0 -0
  10. data/app/assets/images/active_scaffold/indicator-small.gif +0 -0
  11. data/app/assets/images/active_scaffold/indicator.gif +0 -0
  12. data/app/assets/images/active_scaffold/magnifier.png +0 -0
  13. data/app/assets/javascripts/active_scaffold.js.erb +19 -0
  14. data/app/assets/javascripts/jquery/active_scaffold.js +1113 -0
  15. data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +24 -0
  16. data/app/assets/javascripts/jquery/draggable_lists.js +27 -0
  17. data/app/assets/javascripts/jquery/jquery.editinplace.js +743 -0
  18. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +7 -0
  19. data/app/assets/javascripts/prototype/active_scaffold.js +1107 -0
  20. data/app/assets/javascripts/prototype/dhtml_history.js +870 -0
  21. data/app/assets/javascripts/prototype/form_enhancements.js +117 -0
  22. data/app/assets/javascripts/prototype/rico_corner.js +370 -0
  23. data/app/assets/javascripts/prototype/tiny_mce_bridge.js +7 -0
  24. data/app/assets/stylesheets/active_scaffold-ie.css.scss +54 -0
  25. data/app/assets/stylesheets/active_scaffold.css.scss +14 -0
  26. data/app/assets/stylesheets/active_scaffold_colors.css.scss +395 -0
  27. data/app/assets/stylesheets/active_scaffold_extensions.css.erb +2 -0
  28. data/app/assets/stylesheets/active_scaffold_images.css.scss +40 -0
  29. data/app/assets/stylesheets/active_scaffold_layout.css +936 -0
  30. data/app/assets/stylesheets/blue-theme.css +74 -0
  31. data/config/locales/de.yml +125 -0
  32. data/config/locales/en.yml +127 -0
  33. data/config/locales/es.yml +128 -0
  34. data/config/locales/fr.yml +131 -0
  35. data/config/locales/hu.yml +126 -0
  36. data/config/locales/ja.yml +126 -0
  37. data/config/locales/ru.yml +135 -0
  38. data/frontends/default/views/_action_group.html.erb +24 -0
  39. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  40. data/frontends/default/views/_base_form.html.erb +53 -0
  41. data/frontends/default/views/_create_form.html.erb +8 -0
  42. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  43. data/frontends/default/views/_field_search.html.erb +32 -0
  44. data/frontends/default/views/_form.html.erb +28 -0
  45. data/frontends/default/views/_form_association.html.erb +17 -0
  46. data/frontends/default/views/_form_association_footer.html.erb +47 -0
  47. data/frontends/default/views/_form_attribute.html.erb +23 -0
  48. data/frontends/default/views/_form_hidden_attribute.html.erb +7 -0
  49. data/frontends/default/views/_form_messages.html.erb +5 -0
  50. data/frontends/default/views/_horizontal_subform.html.erb +22 -0
  51. data/frontends/default/views/_horizontal_subform_footer.html.erb +0 -0
  52. data/frontends/default/views/_horizontal_subform_header.html.erb +11 -0
  53. data/frontends/default/views/_horizontal_subform_record.html.erb +43 -0
  54. data/frontends/default/views/_human_conditions.html.erb +1 -0
  55. data/frontends/default/views/_list.html.erb +18 -0
  56. data/frontends/default/views/_list_actions.html.erb +15 -0
  57. data/frontends/default/views/_list_calculations.html.erb +16 -0
  58. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  59. data/frontends/default/views/_list_header.html.erb +10 -0
  60. data/frontends/default/views/_list_inline_adapter.html.erb +21 -0
  61. data/frontends/default/views/_list_messages.html.erb +28 -0
  62. data/frontends/default/views/_list_pagination.html.erb +11 -0
  63. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  64. data/frontends/default/views/_list_record.html.erb +13 -0
  65. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  66. data/frontends/default/views/_list_with_header.html.erb +36 -0
  67. data/frontends/default/views/_messages.html.erb +10 -0
  68. data/frontends/default/views/_refresh_list.js.erb +1 -0
  69. data/frontends/default/views/_render_field.js.erb +20 -0
  70. data/frontends/default/views/_row.html.erb +1 -0
  71. data/frontends/default/views/_search.html.erb +34 -0
  72. data/frontends/default/views/_search_attribute.html.erb +10 -0
  73. data/frontends/default/views/_show.html.erb +8 -0
  74. data/frontends/default/views/_show_columns.html.erb +15 -0
  75. data/frontends/default/views/_update_actions.html.erb +9 -0
  76. data/frontends/default/views/_update_calculations.js.erb +4 -0
  77. data/frontends/default/views/_update_form.html.erb +6 -0
  78. data/frontends/default/views/_update_messages.js.erb +2 -0
  79. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  80. data/frontends/default/views/_vertical_subform_record.html.erb +43 -0
  81. data/frontends/default/views/action_confirmation.html.erb +13 -0
  82. data/frontends/default/views/add_existing.js.erb +14 -0
  83. data/frontends/default/views/add_existing_form.html.erb +5 -0
  84. data/frontends/default/views/create.html.erb +5 -0
  85. data/frontends/default/views/delete.html.erb +13 -0
  86. data/frontends/default/views/destroy.js.erb +26 -0
  87. data/frontends/default/views/edit_associated.js.erb +12 -0
  88. data/frontends/default/views/field_search.html.erb +5 -0
  89. data/frontends/default/views/form_messages.js.erb +1 -0
  90. data/frontends/default/views/list.html.erb +1 -0
  91. data/frontends/default/views/on_action_update.js.erb +22 -0
  92. data/frontends/default/views/on_create.js.erb +38 -0
  93. data/frontends/default/views/on_mark.js.erb +6 -0
  94. data/frontends/default/views/on_update.js.erb +29 -0
  95. data/frontends/default/views/refresh_list.js.erb +2 -0
  96. data/frontends/default/views/render_field.js.erb +1 -0
  97. data/frontends/default/views/row.js.erb +2 -0
  98. data/frontends/default/views/search.html.erb +5 -0
  99. data/frontends/default/views/show.html.erb +5 -0
  100. data/frontends/default/views/update.html.erb +8 -0
  101. data/frontends/default/views/update_column.js.erb +15 -0
  102. data/frontends/default/views/update_row.js.erb +1 -0
  103. data/lib/active_scaffold/actions/common_search.rb +22 -0
  104. data/lib/active_scaffold/actions/core.rb +203 -0
  105. data/lib/active_scaffold/actions/create.rb +139 -0
  106. data/lib/active_scaffold/actions/delete.rb +74 -0
  107. data/lib/active_scaffold/actions/field_search.rb +78 -0
  108. data/lib/active_scaffold/actions/list.rb +208 -0
  109. data/lib/active_scaffold/actions/mark.rb +89 -0
  110. data/lib/active_scaffold/actions/nested.rb +244 -0
  111. data/lib/active_scaffold/actions/search.rb +48 -0
  112. data/lib/active_scaffold/actions/show.rb +61 -0
  113. data/lib/active_scaffold/actions/subform.rb +23 -0
  114. data/lib/active_scaffold/actions/update.rb +156 -0
  115. data/lib/active_scaffold/active_record_permissions.rb +135 -0
  116. data/lib/active_scaffold/attribute_params.rb +200 -0
  117. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +39 -0
  118. data/lib/active_scaffold/bridges/ancestry.rb +5 -0
  119. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +37 -0
  120. data/lib/active_scaffold/bridges/bitfields.rb +6 -0
  121. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +66 -0
  122. data/lib/active_scaffold/bridges/calendar_date_select.rb +24 -0
  123. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +127 -0
  124. data/lib/active_scaffold/bridges/cancan.rb +15 -0
  125. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +31 -0
  126. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +10 -0
  127. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +45 -0
  128. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +17 -0
  129. data/lib/active_scaffold/bridges/carrierwave.rb +12 -0
  130. data/lib/active_scaffold/bridges/country_helper/country_helper_bridge.rb +358 -0
  131. data/lib/active_scaffold/bridges/country_helper.rb +9 -0
  132. data/lib/active_scaffold/bridges/date_picker/ext.rb +63 -0
  133. data/lib/active_scaffold/bridges/date_picker/helper.rb +180 -0
  134. data/lib/active_scaffold/bridges/date_picker.rb +23 -0
  135. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +34 -0
  136. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +10 -0
  137. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +27 -0
  138. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +16 -0
  139. data/lib/active_scaffold/bridges/dragonfly.rb +9 -0
  140. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +46 -0
  141. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +57 -0
  142. data/lib/active_scaffold/bridges/file_column/form_ui.rb +34 -0
  143. data/lib/active_scaffold/bridges/file_column/list_ui.rb +26 -0
  144. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  145. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  146. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  147. data/lib/active_scaffold/bridges/file_column.rb +11 -0
  148. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +27 -0
  149. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +16 -0
  150. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +36 -0
  151. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +24 -0
  152. data/lib/active_scaffold/bridges/paperclip.rb +12 -0
  153. data/lib/active_scaffold/bridges/record_select/helpers.rb +92 -0
  154. data/lib/active_scaffold/bridges/record_select.rb +11 -0
  155. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +20 -0
  156. data/lib/active_scaffold/bridges/semantic_attributes.rb +5 -0
  157. data/lib/active_scaffold/bridges/shared/date_bridge.rb +209 -0
  158. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +46 -0
  159. data/lib/active_scaffold/bridges/tiny_mce.rb +17 -0
  160. data/lib/active_scaffold/bridges.rb +61 -0
  161. data/lib/active_scaffold/config/base.rb +75 -0
  162. data/lib/active_scaffold/config/core.rb +236 -0
  163. data/lib/active_scaffold/config/create.rb +32 -0
  164. data/lib/active_scaffold/config/delete.rb +32 -0
  165. data/lib/active_scaffold/config/field_search.rb +79 -0
  166. data/lib/active_scaffold/config/form.rb +64 -0
  167. data/lib/active_scaffold/config/list.rb +247 -0
  168. data/lib/active_scaffold/config/mark.rb +30 -0
  169. data/lib/active_scaffold/config/nested.rb +42 -0
  170. data/lib/active_scaffold/config/search.rb +73 -0
  171. data/lib/active_scaffold/config/show.rb +31 -0
  172. data/lib/active_scaffold/config/subform.rb +35 -0
  173. data/lib/active_scaffold/config/update.rb +33 -0
  174. data/lib/active_scaffold/configurable.rb +29 -0
  175. data/lib/active_scaffold/constraints.rb +171 -0
  176. data/lib/active_scaffold/data_structures/action_columns.rb +142 -0
  177. data/lib/active_scaffold/data_structures/action_link.rb +185 -0
  178. data/lib/active_scaffold/data_structures/action_links.rb +191 -0
  179. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  180. data/lib/active_scaffold/data_structures/bridge.rb +22 -0
  181. data/lib/active_scaffold/data_structures/column.rb +402 -0
  182. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  183. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  184. data/lib/active_scaffold/data_structures/nested_info.rb +171 -0
  185. data/lib/active_scaffold/data_structures/set.rb +61 -0
  186. data/lib/active_scaffold/data_structures/sorting.rb +167 -0
  187. data/lib/active_scaffold/engine.rb +4 -0
  188. data/lib/active_scaffold/extensions/action_controller_rendering.rb +20 -0
  189. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +7 -0
  190. data/lib/active_scaffold/extensions/action_view_rendering.rb +115 -0
  191. data/lib/active_scaffold/extensions/active_record_offset.rb +12 -0
  192. data/lib/active_scaffold/extensions/array.rb +7 -0
  193. data/lib/active_scaffold/extensions/cache_association.rb +16 -0
  194. data/lib/active_scaffold/extensions/localize.rb +10 -0
  195. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +12 -0
  196. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +7 -0
  197. data/lib/active_scaffold/extensions/paginator_extensions.rb +26 -0
  198. data/lib/active_scaffold/extensions/reverse_associations.rb +64 -0
  199. data/lib/active_scaffold/extensions/routing_mapper.rb +48 -0
  200. data/lib/active_scaffold/extensions/to_label.rb +8 -0
  201. data/lib/active_scaffold/extensions/unsaved_associated.rb +61 -0
  202. data/lib/active_scaffold/extensions/unsaved_record.rb +20 -0
  203. data/lib/active_scaffold/extensions/usa_state.rb +46 -0
  204. data/lib/active_scaffold/finder.rb +399 -0
  205. data/lib/active_scaffold/helpers/association_helpers.rb +42 -0
  206. data/lib/active_scaffold/helpers/controller_helpers.rb +94 -0
  207. data/lib/active_scaffold/helpers/form_column_helpers.rb +322 -0
  208. data/lib/active_scaffold/helpers/human_condition_helpers.rb +64 -0
  209. data/lib/active_scaffold/helpers/id_helpers.rb +131 -0
  210. data/lib/active_scaffold/helpers/list_column_helpers.rb +374 -0
  211. data/lib/active_scaffold/helpers/pagination_helpers.rb +62 -0
  212. data/lib/active_scaffold/helpers/search_column_helpers.rb +257 -0
  213. data/lib/active_scaffold/helpers/show_column_helpers.rb +44 -0
  214. data/lib/active_scaffold/helpers/view_helpers.rb +398 -0
  215. data/lib/active_scaffold/marked_model.rb +38 -0
  216. data/lib/active_scaffold/paginator.rb +136 -0
  217. data/lib/active_scaffold/responds_to_parent.rb +70 -0
  218. data/lib/active_scaffold/tableless.rb +83 -0
  219. data/lib/active_scaffold/version.rb +9 -0
  220. data/lib/active_scaffold.rb +373 -0
  221. data/lib/active_scaffold_env.rb +13 -0
  222. data/lib/generators/active_scaffold/USAGE +29 -0
  223. data/lib/generators/active_scaffold/active_scaffold_generator.rb +21 -0
  224. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  225. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +29 -0
  226. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  227. data/lib/generators/active_scaffold_controller/templates/helper.rb +2 -0
  228. data/public/blank.html +33 -0
  229. data/shoulda_macros/macros.rb +136 -0
  230. data/vendor/assets/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  231. data/vendor/assets/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  232. data/vendor/assets/images/ui-bg_flat_10_000000_40x100.png +0 -0
  233. data/vendor/assets/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  234. data/vendor/assets/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  235. data/vendor/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  236. data/vendor/assets/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  237. data/vendor/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  238. data/vendor/assets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  239. data/vendor/assets/images/ui-icons_222222_256x240.png +0 -0
  240. data/vendor/assets/images/ui-icons_228ef1_256x240.png +0 -0
  241. data/vendor/assets/images/ui-icons_ef8c08_256x240.png +0 -0
  242. data/vendor/assets/images/ui-icons_ffd27a_256x240.png +0 -0
  243. data/vendor/assets/images/ui-icons_ffffff_256x240.png +0 -0
  244. data/vendor/assets/javascripts/jquery-ui-timepicker-addon.js +1276 -0
  245. data/vendor/assets/stylesheets/jquery-ui.css +568 -0
  246. metadata +261 -17
@@ -0,0 +1,191 @@
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
+ security_method = link.security_method_set? || controller.respond_to?(link.security_method)
125
+ authorized = if security_method
126
+ controller.send(link.security_method, *(Array(options[:for])))
127
+ else
128
+ options[:for].nil? ? true : options[:for].authorized_for?(:crud_type => link.crud_type, :action => link.action)
129
+ end
130
+ next unless authorized || link.type == :member
131
+ yield(self, link, {:authorized => authorized, :first_action => first_action, :level => options[:level]})
132
+ first_action = false
133
+ end
134
+ end
135
+ options[:level] -= 1
136
+ end
137
+
138
+ def collect
139
+ @set
140
+ end
141
+
142
+ def empty?
143
+ @set.size == 0
144
+ end
145
+
146
+ def subgroup(name, label = nil)
147
+ group = self if name == self.name
148
+ group ||= @set.find do |item|
149
+ name == item.name if item.is_a?(ActiveScaffold::DataStructures::ActionLinks)
150
+ end
151
+
152
+ if group.nil?
153
+ group = ActiveScaffold::DataStructures::ActionLinks.new
154
+ group.label = label || name
155
+ group.name = name
156
+ add_to_set group
157
+ end
158
+ group
159
+ end
160
+
161
+ attr_writer :label
162
+ def label
163
+ as_(@label) if @label
164
+ end
165
+
166
+ def method_missing(name, *args, &block)
167
+ class_eval %{
168
+ def #{name}
169
+ @#{name} ||= subgroup('#{name}'.to_sym)
170
+ yield @#{name} if block_given?
171
+ @#{name}
172
+ end
173
+ }
174
+ send(name, &block)
175
+ end
176
+
177
+ attr_accessor :name
178
+
179
+ protected
180
+
181
+ def skip_action_link(controller, link, *args)
182
+ !link.ignore_method.nil? && controller.respond_to?(link.ignore_method) && controller.send(link.ignore_method, *args)
183
+ end
184
+
185
+ # called during clone or dup. makes the clone/dup deeper.
186
+ def initialize_copy(from)
187
+ @set = []
188
+ from.instance_variable_get('@set').each { |link| @set << link.clone }
189
+ end
190
+ end
191
+ 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,22 @@
1
+ module ActiveScaffold::DataStructures
2
+ class Bridge
3
+ def self.install
4
+ raise(RunTimeError, "install not defined for bridge #{name}")
5
+ end
6
+
7
+ # by convention and default, use the bridge name as the required constant for installation
8
+ def self.install?
9
+ Object.const_defined? name.demodulize
10
+ end
11
+
12
+ def self.run
13
+ install if install?
14
+ end
15
+
16
+ def self.stylesheets
17
+ end
18
+
19
+ def self.javascripts
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,402 @@
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
+ # send all the form instead of only new value when this column change
66
+ cattr_accessor :send_form_on_update_column
67
+ attr_accessor :send_form_on_update_column
68
+
69
+ # column to be updated in a form when this column changes
70
+ def update_column=(column_name)
71
+ ActiveSupport::Deprecation.warn "Use update_columns= instead of update_column="
72
+ self.update_columns = column_name
73
+ end
74
+
75
+ # send all the form instead of only new value when this column change
76
+ cattr_accessor :send_form_on_update_column
77
+ attr_accessor :send_form_on_update_column
78
+
79
+ # sorting on a column can be configured four ways:
80
+ # sort = true default, uses intelligent sorting sql default
81
+ # sort = false sometimes sorting doesn't make sense
82
+ # sort = {:sql => ""} define your own sql for sorting. this should be result in a sortable value in SQL. ActiveScaffold will handle the ascending/descending.
83
+ # sort = {:method => ""} define ruby-side code for sorting. this is SLOW with large recordsets!
84
+ def sort=(value)
85
+ if value.is_a? Hash
86
+ value.assert_valid_keys(:sql, :method)
87
+ @sort = value
88
+ else
89
+ @sort = value ? true : false # force true or false
90
+ end
91
+ end
92
+
93
+ def sort
94
+ self.initialize_sort if @sort === true
95
+ @sort
96
+ end
97
+
98
+ def sortable?
99
+ sort != false && !sort.nil?
100
+ end
101
+
102
+ # a configuration helper for the self.sort property. simply provides a method syntax instead of setter syntax.
103
+ def sort_by(options)
104
+ self.sort = options
105
+ end
106
+
107
+ # supported options:
108
+ # * for association columns
109
+ # * :select - displays a simple <select> or a collection of checkboxes to (dis)associate records
110
+ attr_writer :form_ui
111
+ def form_ui
112
+ @form_ui
113
+ end
114
+
115
+ attr_writer :list_ui
116
+ def list_ui
117
+ @list_ui || @form_ui
118
+ end
119
+
120
+ attr_writer :search_ui
121
+ def search_ui
122
+ @search_ui || @form_ui || (@association && !polymorphic_association? ? :select : nil)
123
+ end
124
+
125
+ # a place to store dev's column specific options
126
+ attr_accessor :options
127
+ def options
128
+ @options ||= {}
129
+ end
130
+
131
+ def link
132
+ @link = @link.call(self) if @link.is_a? Proc
133
+ @link
134
+ end
135
+
136
+ # associate an action_link with this column
137
+ def set_link(action, options = {})
138
+ if action.is_a?(ActiveScaffold::DataStructures::ActionLink) || (action.is_a? Proc)
139
+ @link = action
140
+ else
141
+ options[:label] ||= self.label
142
+ options[:position] ||= :after unless options.has_key?(:position)
143
+ options[:type] ||= :member
144
+ @link = ActiveScaffold::DataStructures::ActionLink.new(action, options)
145
+ end
146
+ end
147
+
148
+ # set an action_link to nested list or inline form in this column
149
+ def autolink?
150
+ @autolink
151
+ end
152
+
153
+ # this should not only delete any existing link but also prevent column links from being automatically added by later routines
154
+ def clear_link
155
+ @link = nil
156
+ @autolink = false
157
+ end
158
+
159
+ # define a calculation for the column. anything that ActiveRecord::Calculations::ClassMethods#calculate accepts will do.
160
+ attr_accessor :calculate
161
+
162
+ # get whether to run a calculation on this column
163
+ def calculation?
164
+ !(@calculate == false or @calculate.nil?)
165
+ end
166
+
167
+ # a collection of associations to pre-load when finding the records on a page
168
+ attr_reader :includes
169
+ def includes=(value)
170
+ @includes = case value
171
+ when Array then value
172
+ else [value] # automatically convert to an array
173
+ end
174
+ end
175
+
176
+ # a collection of columns to load when eager loading is disabled, if it's nil all columns will be loaded
177
+ attr_accessor :select_columns
178
+
179
+ # describes how to search on a column
180
+ # search = true default, uses intelligent search sql
181
+ # 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.
182
+ # search = [:a, :b] searches in both fields
183
+ def search_sql=(value)
184
+ @search_sql = (value == true || value.is_a?(Proc)) ? value : Array(value)
185
+ end
186
+ def search_sql
187
+ self.initialize_search_sql if @search_sql === true
188
+ @search_sql
189
+ end
190
+ def searchable?
191
+ search_sql != false && search_sql != nil
192
+ end
193
+
194
+ # to modify the default order of columns
195
+ attr_accessor :weight
196
+
197
+ # to set how many associated records a column with plural association must show in list
198
+ cattr_accessor :associated_limit
199
+ @@associated_limit = 3
200
+ attr_accessor :associated_limit
201
+
202
+ # whether the number of associated records must be shown or not
203
+ cattr_accessor :associated_number
204
+ @@associated_number = true
205
+ attr_writer :associated_number
206
+ def associated_number?
207
+ @associated_number
208
+ end
209
+
210
+ # whether a blank row must be shown in the subform
211
+ cattr_accessor :show_blank_record
212
+ @@show_blank_record = true
213
+ attr_writer :show_blank_record
214
+ def show_blank_record?(associated)
215
+ if @show_blank_record
216
+ return false unless self.association.klass.authorized_for?(:crud_type => :create)
217
+ self.plural_association? or (self.singular_association? and associated.blank?)
218
+ end
219
+ end
220
+
221
+ # methods for automatic links in singular association columns
222
+ cattr_accessor :actions_for_association_links
223
+ @@actions_for_association_links = [:new, :edit, :show]
224
+ attr_accessor :actions_for_association_links
225
+
226
+ cattr_accessor :association_form_ui
227
+ @@association_form_ui = nil
228
+
229
+ # ----------------------------------------------------------------- #
230
+ # the below functionality is intended for internal consumption only #
231
+ # ----------------------------------------------------------------- #
232
+
233
+ # the ConnectionAdapter::*Column object from the ActiveRecord class
234
+ attr_reader :column
235
+
236
+ # the association from the ActiveRecord class
237
+ attr_reader :association
238
+ def singular_association?
239
+ self.association and [:has_one, :belongs_to].include? self.association.macro
240
+ end
241
+ def plural_association?
242
+ self.association and [:has_many, :has_and_belongs_to_many].include? self.association.macro
243
+ end
244
+ def through_association?
245
+ self.association and self.association.options[:through]
246
+ end
247
+ def polymorphic_association?
248
+ self.association and self.association.options.has_key? :polymorphic and self.association.options[:polymorphic]
249
+ end
250
+ def readonly_association?
251
+ if self.association
252
+ if self.association.options.has_key? :readonly
253
+ self.association.options[:readonly]
254
+ else
255
+ self.through_association?
256
+ end
257
+ end
258
+ end
259
+
260
+ # an interpreted property. the column is virtual if it isn't from the active record model or any associated models
261
+ def virtual?
262
+ column.nil? && association.nil?
263
+ end
264
+
265
+ attr_writer :number
266
+ def number?
267
+ @number
268
+ end
269
+
270
+ # this is so that array.delete and array.include?, etc., will work by column name
271
+ def ==(other) #:nodoc:
272
+ # another column
273
+ if other.respond_to? :name and other.class == self.class
274
+ self.name == other.name.to_sym
275
+ # a string or symbol
276
+ elsif other.respond_to? :to_sym
277
+ self.name == other.to_sym rescue false # catch "interning empty string"
278
+ # unknown
279
+ else
280
+ self.eql? other
281
+ end
282
+ end
283
+
284
+ # instantiation is handled internally through the DataStructures::Columns object
285
+ def initialize(name, active_record_class) #:nodoc:
286
+ self.name = name.to_sym
287
+ @tableless = active_record_class < ActiveScaffold::Tableless
288
+ @column = active_record_class.columns_hash[self.name.to_s]
289
+ @association = active_record_class.reflect_on_association(self.name)
290
+ @autolink = !@association.nil?
291
+ @active_record_class = active_record_class
292
+ @table = active_record_class.table_name
293
+ @associated_limit = self.class.associated_limit
294
+ @associated_number = self.class.associated_number
295
+ @show_blank_record = self.class.show_blank_record
296
+ @send_form_on_update_column = self.class.send_form_on_update_column
297
+ @actions_for_association_links = self.class.actions_for_association_links.clone if @association
298
+
299
+ self.number = @column.try(:number?)
300
+ @options = {:format => :i18n_number} if self.number?
301
+ @form_ui = :checkbox if @column and @column.type == :boolean
302
+ @form_ui = :textarea if @column and @column.type == :text
303
+ @allow_add_existing = true
304
+ @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui
305
+
306
+ # default all the configurable variables
307
+ self.css_class = ''
308
+ self.required = active_record_class.validators_on(self.name).any? do |val|
309
+ ActiveModel::Validations::PresenceValidator === val or (
310
+ ActiveModel::Validations::InclusionValidator === val and not val.options[:allow_nil] and not val.options[:allow_blank]
311
+ )
312
+ end
313
+ self.sort = true
314
+ self.search_sql = true
315
+
316
+ @weight = estimate_weight
317
+
318
+ self.clear_link if self.polymorphic_association?
319
+ self.includes = (association and not polymorphic_association?) ? [association.name] : []
320
+ end
321
+
322
+ # just the field (not table.field)
323
+ def field_name
324
+ return nil if virtual?
325
+ @field_name ||= column ? @active_record_class.connection.quote_column_name(column.name) : association.foreign_key
326
+ end
327
+
328
+ def <=>(other_column)
329
+ order_weight = self.weight <=> other_column.weight
330
+ order_weight != 0 ? order_weight : self.name.to_s <=> other_column.name.to_s
331
+ end
332
+
333
+ def number_to_native(value)
334
+ return value if value.blank? || !value.is_a?(String)
335
+ native = '.' # native ruby separator
336
+ format = {:separator => '', :delimiter => ''}.merge! I18n.t('number.format', :default => {})
337
+ specific = case self.options[:format]
338
+ when :currency
339
+ I18n.t('number.currency.format', :default => nil)
340
+ when :size
341
+ I18n.t('number.human.format', :default => nil)
342
+ when :percentage
343
+ I18n.t('number.percentage.format', :default => nil)
344
+ end
345
+ format.merge! specific unless specific.nil?
346
+ unless format[:separator].blank? || !value.include?(format[:separator]) && value.include?(native) && (format[:delimiter] != native || value !~ /\.\d{3}$/)
347
+ value.gsub(/[^0-9\-#{format[:separator]}]/, '').gsub(format[:separator], native)
348
+ else
349
+ value
350
+ end
351
+ end
352
+
353
+ protected
354
+
355
+ def initialize_sort
356
+ if self.virtual?
357
+ # we don't automatically enable method sorting for virtual columns because it's slow, and we expect fewer complaints this way.
358
+ self.sort = false
359
+ else
360
+ if column && !@tableless
361
+ self.sort = {:sql => self.field}
362
+ else
363
+ self.sort = false
364
+ end
365
+ end
366
+ end
367
+
368
+ def initialize_search_sql
369
+ self.search_sql = unless self.virtual?
370
+ if association.nil?
371
+ self.field.to_s unless @tableless
372
+ elsif !self.polymorphic_association?
373
+ [association.klass.quoted_table_name, association.klass.quoted_primary_key].join('.') unless association.klass < ActiveScaffold::Tableless
374
+ end
375
+ end
376
+ end
377
+
378
+ # the table name from the ActiveRecord class
379
+ attr_reader :table
380
+
381
+ # the table.field name for this column, if applicable
382
+ def field
383
+ @field ||= [@active_record_class.quoted_table_name, field_name].join('.')
384
+ end
385
+
386
+ def estimate_weight
387
+ if singular_association?
388
+ 400
389
+ elsif plural_association?
390
+ 500
391
+ elsif [:created_at, :updated_at].include?(self.name)
392
+ 600
393
+ elsif [:name, :label, :title].include?(self.name)
394
+ 100
395
+ elsif required?
396
+ 200
397
+ else
398
+ 300
399
+ end
400
+ end
401
+ end
402
+ end