active_scaffold 3.5.5 → 3.6.1

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +75 -0
  3. data/README.md +21 -10
  4. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  5. data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
  6. data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
  7. data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
  8. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
  9. data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
  10. data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
  11. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
  12. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +26 -10
  13. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
  14. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
  15. data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
  16. data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
  17. data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
  18. data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
  19. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
  21. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
  22. data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
  23. data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
  24. data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
  25. data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
  26. data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
  27. data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
  28. data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
  29. data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
  30. data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
  31. data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
  32. data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
  33. data/app/views/active_scaffold_overrides/row.js.erb +1 -1
  34. data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
  35. data/config/locales/de.yml +2 -1
  36. data/config/locales/en.yml +1 -0
  37. data/config/locales/es.yml +1 -0
  38. data/config/locales/fr.yml +2 -1
  39. data/config/locales/hu.yml +1 -0
  40. data/config/locales/ja.yml +1 -0
  41. data/config/locales/ru.yml +1 -0
  42. data/lib/active_scaffold.rb +19 -16
  43. data/lib/active_scaffold/actions/common_search.rb +11 -8
  44. data/lib/active_scaffold/actions/core.rb +91 -70
  45. data/lib/active_scaffold/actions/create.rb +28 -28
  46. data/lib/active_scaffold/actions/delete.rb +3 -3
  47. data/lib/active_scaffold/actions/field_search.rb +53 -43
  48. data/lib/active_scaffold/actions/list.rb +111 -27
  49. data/lib/active_scaffold/actions/nested.rb +65 -48
  50. data/lib/active_scaffold/actions/search.rb +1 -1
  51. data/lib/active_scaffold/actions/show.rb +4 -4
  52. data/lib/active_scaffold/actions/subform.rb +23 -22
  53. data/lib/active_scaffold/actions/update.rb +96 -77
  54. data/lib/active_scaffold/active_record_permissions.rb +2 -11
  55. data/lib/active_scaffold/attribute_params.rb +102 -94
  56. data/lib/active_scaffold/bridges.rb +8 -8
  57. data/lib/active_scaffold/bridges/active_storage.rb +6 -0
  58. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
  59. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
  60. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
  61. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
  62. data/lib/active_scaffold/bridges/bitfields.rb +2 -1
  63. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  64. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
  65. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
  66. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +3 -12
  67. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
  68. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  69. data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
  70. data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
  71. data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
  72. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  73. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
  74. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
  75. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  76. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
  77. data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
  78. data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
  79. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  80. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
  81. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  82. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
  83. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
  84. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  85. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
  86. data/lib/active_scaffold/config/base.rb +133 -41
  87. data/lib/active_scaffold/config/core.rb +146 -18
  88. data/lib/active_scaffold/config/delete.rb +14 -1
  89. data/lib/active_scaffold/config/field_search.rb +7 -1
  90. data/lib/active_scaffold/config/form.rb +10 -1
  91. data/lib/active_scaffold/config/list.rb +39 -13
  92. data/lib/active_scaffold/config/mark.rb +4 -2
  93. data/lib/active_scaffold/config/nested.rb +16 -17
  94. data/lib/active_scaffold/config/search.rb +9 -0
  95. data/lib/active_scaffold/config/show.rb +4 -0
  96. data/lib/active_scaffold/config/update.rb +4 -0
  97. data/lib/active_scaffold/configurable.rb +14 -7
  98. data/lib/active_scaffold/constraints.rb +22 -20
  99. data/lib/active_scaffold/core.rb +67 -28
  100. data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
  101. data/lib/active_scaffold/data_structures/action_link.rb +50 -20
  102. data/lib/active_scaffold/data_structures/action_links.rb +15 -13
  103. data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
  104. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  105. data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
  106. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  107. data/lib/active_scaffold/data_structures/column.rb +75 -66
  108. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  109. data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
  110. data/lib/active_scaffold/data_structures/set.rb +8 -0
  111. data/lib/active_scaffold/data_structures/sorting.rb +10 -2
  112. data/lib/active_scaffold/delayed_setup.rb +16 -5
  113. data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
  114. data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
  115. data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
  116. data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
  117. data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
  118. data/lib/active_scaffold/extensions/localize.rb +3 -1
  119. data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
  120. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  121. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
  122. data/lib/active_scaffold/finder.rb +110 -77
  123. data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
  124. data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
  125. data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
  126. data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
  127. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  128. data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
  129. data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
  130. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  131. data/lib/active_scaffold/helpers/search_column_helpers.rb +43 -41
  132. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  133. data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
  134. data/lib/active_scaffold/marked_model.rb +2 -2
  135. data/lib/active_scaffold/orm_checks.rb +3 -7
  136. data/lib/active_scaffold/paginator.rb +7 -7
  137. data/lib/active_scaffold/registry.rb +33 -0
  138. data/lib/active_scaffold/responds_to_parent.rb +8 -11
  139. data/lib/active_scaffold/tableless.rb +83 -67
  140. data/lib/active_scaffold/version.rb +2 -2
  141. data/lib/generators/active_scaffold/controller_generator.rb +2 -2
  142. data/lib/generators/active_scaffold/install_generator.rb +52 -4
  143. data/lib/generators/active_scaffold/resource_generator.rb +2 -2
  144. data/shoulda_macros/macros.rb +3 -1
  145. data/test/bridges/date_picker_test.rb +1 -2
  146. data/test/bridges/paperclip_test.rb +6 -6
  147. data/test/class_with_finder.rb +2 -2
  148. data/test/company.rb +4 -4
  149. data/test/config/create_test.rb +4 -2
  150. data/test/config/nested_test.rb +1 -1
  151. data/test/config/show_test.rb +1 -1
  152. data/test/config/update_test.rb +7 -6
  153. data/test/data_structures/action_columns_test.rb +2 -2
  154. data/test/data_structures/action_links_test.rb +1 -1
  155. data/test/data_structures/column_test.rb +3 -6
  156. data/test/data_structures/columns_test.rb +2 -2
  157. data/test/data_structures/sorting_test.rb +7 -0
  158. data/test/extensions/action_view_rendering_test.rb +20 -0
  159. data/test/extensions/active_record_test.rb +4 -4
  160. data/test/extensions/routing_mapper_test.rb +2 -2
  161. data/test/helpers/list_column_helpers_test.rb +3 -1
  162. data/test/misc/active_record_permissions_test.rb +3 -11
  163. data/test/misc/attribute_params_test.rb +12 -8
  164. data/test/misc/calculation_test.rb +1 -1
  165. data/test/misc/configurable_test.rb +10 -10
  166. data/test/misc/constraints_test.rb +3 -3
  167. data/test/misc/convert_numbers_format_test.rb +7 -3
  168. data/test/misc/lang_test.rb +1 -1
  169. data/test/misc/parse_datetime_test.rb +3 -4
  170. data/test/misc/tableless_test.rb +14 -0
  171. data/test/mock_app/Rakefile +1 -1
  172. data/test/mock_app/app/assets/config/manifest.js +0 -0
  173. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  174. data/test/mock_app/app/controllers/people_controller.rb +5 -1
  175. data/test/mock_app/app/controllers/roles_controller.rb +4 -0
  176. data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
  177. data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
  178. data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
  179. data/test/mock_app/app/views/people/_form.html.erb +2 -0
  180. data/test/mock_app/app/views/people/list.html.erb +2 -0
  181. data/test/mock_app/config/application.rb +2 -1
  182. data/test/mock_app/config/boot.rb +1 -1
  183. data/test/mock_app/config/environment.rb +2 -2
  184. data/test/mock_app/config/routes.rb +4 -1
  185. data/test/mock_app/db/schema.rb +2 -0
  186. data/test/performance/list_cars_performance_test.rb +34 -0
  187. data/test/performance/list_people_performance_test.rb +31 -0
  188. data/test/performance_test_help.rb +3 -0
  189. data/test/test_helper.rb +12 -4
  190. metadata +71 -15
  191. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  192. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -60,6 +60,11 @@ module ActiveScaffold::Config
60
60
  ActiveScaffold::ActiveRecordPermissions
61
61
  end
62
62
 
63
+ # access to default column configuration.
64
+ def self.column
65
+ ActiveScaffold::DataStructures::Column
66
+ end
67
+
63
68
  # columns that should be ignored for every model. these should be metadata columns like change dates, versions, etc.
64
69
  # values in this array may be symbols or strings.
65
70
  def self.ignore_columns
@@ -83,6 +88,16 @@ module ActiveScaffold::Config
83
88
  cattr_accessor :highlight_messages, instance_accessor: false
84
89
  @@highlight_messages = nil
85
90
 
91
+ # method names or procs to be called after all configure blocks
92
+ cattr_reader :after_config_callbacks, instance_accessor: false
93
+ @@after_config_callbacks = [:_configure_sti]
94
+
95
+ def self.freeze
96
+ super
97
+ security.freeze
98
+ column.freeze
99
+ end
100
+
86
101
  # instance-level configuration
87
102
  # ----------------------------
88
103
 
@@ -149,6 +164,7 @@ module ActiveScaffold::Config
149
164
  def initialize(model_id)
150
165
  # model_id is the only absolutely required configuration value. it is also not publicly accessible.
151
166
  @model_id = model_id
167
+ setup_user_setting_key
152
168
 
153
169
  # inherit the actions list directly from the global level
154
170
  @actions = self.class.actions.clone
@@ -182,17 +198,20 @@ module ActiveScaffold::Config
182
198
  @highlight_messages = self.class.highlight_messages
183
199
  end
184
200
 
185
- # To be called after your finished configuration
186
- def _load_action_columns
187
- # then, register the column objects
201
+ # To be called before freezing
202
+ def _cache_lazy_values
203
+ action_links.each(&:name_to_cache) if cache_action_link_urls
204
+ columns.select(&:sortable?).each(&:sort)
205
+ columns.select(&:searchable?).each(&:search_sql)
188
206
  actions.each do |action_name|
189
207
  action = send(action_name)
190
- action.columns.set_columns(columns) if action.respond_to?(:columns)
208
+ Array(action.class.columns_collections).each { |method| action.send(method) }
191
209
  end
192
210
  end
193
211
 
194
212
  # To be called after your finished configuration
195
213
  def _configure_sti
214
+ return if sti_children.nil?
196
215
  column = model.inheritance_column
197
216
  if sti_create_links
198
217
  columns[column].form_ui ||= :hidden
@@ -205,28 +224,55 @@ module ActiveScaffold::Config
205
224
  end
206
225
  end
207
226
 
227
+ def _setup_action(action)
228
+ define_singleton_method action do
229
+ self[action]
230
+ end
231
+ end
232
+
208
233
  # configuration routing.
209
234
  # we want to route calls named like an activated action to that action's global or local Config class.
210
235
  # ---------------------------
211
236
  def method_missing(name, *args)
212
- @action_configs ||= {}
213
- titled_name = name.to_s.camelcase
214
- underscored_name = name.to_s.underscore.to_sym
215
- klass = "ActiveScaffold::Config::#{titled_name}".constantize rescue nil
216
- if klass
217
- if @actions.include? underscored_name
218
- return @action_configs[underscored_name] ||= klass.new(self)
219
- else
220
- raise "#{titled_name} is not enabled. Please enable it or remove any references in your configuration (e.g. config.#{underscored_name}.columns = [...])."
221
- end
237
+ self[name] || super
238
+ end
239
+
240
+ def respond_to_missing?(name, include_all = false)
241
+ self.class.config_class?(name) && @actions.include?(name.to_sym) || super
242
+ end
243
+
244
+ def [](action_name)
245
+ klass = self.class.config_class(action_name)
246
+ return unless klass
247
+
248
+ underscored_name = action_name.to_s.underscore.to_sym
249
+ unless @actions.include? underscored_name
250
+ raise "#{action_name.to_s.camelcase} is not enabled. Please enable it or remove any references in your configuration (e.g. config.#{underscored_name}.columns = [...])."
222
251
  end
223
- super
252
+ @action_configs ||= {}
253
+ @action_configs[underscored_name] ||= klass.new(self)
254
+ end
255
+
256
+ def []=(action_name, action_config)
257
+ @action_configs ||= {}
258
+ @action_configs[action_name] = action_config
224
259
  end
260
+ private :[]=
225
261
 
226
262
  def self.method_missing(name, *args)
227
- klass = "ActiveScaffold::Config::#{name.to_s.camelcase}".constantize rescue nil
228
- return klass if @@actions.include?(name.to_s.underscore) && klass
229
- super
263
+ config_class(name) || super
264
+ end
265
+
266
+ def self.config_class(name)
267
+ "ActiveScaffold::Config::#{name.to_s.camelcase}".constantize if config_class?(name)
268
+ end
269
+
270
+ def self.config_class?(name)
271
+ ActiveScaffold::Config.const_defined? name.to_s.camelcase
272
+ end
273
+
274
+ def self.respond_to_missing?(name, include_all = false)
275
+ config_class?(name) && @@actions.include?(name.to_s.underscore) || super
230
276
  end
231
277
  # some utility methods
232
278
  # --------------------
@@ -247,6 +293,17 @@ module ActiveScaffold::Config
247
293
  @inherited_view_paths ||= []
248
294
  end
249
295
 
296
+ def build_action_columns(action, columns)
297
+ action_columns =
298
+ if columns.is_a?(ActiveScaffold::DataStructures::ActionColumns)
299
+ columns.dup
300
+ else
301
+ ActiveScaffold::DataStructures::ActionColumns.new(*columns)
302
+ end
303
+ action_columns.action = action.is_a?(Symbol) ? send(action) : action
304
+ action_columns
305
+ end
306
+
250
307
  # must be a class method so the layout doesn't depend on a controller that uses active_scaffold
251
308
  # note that this is unaffected by per-controller frontend configuration.
252
309
  def self.asset_path(filename, frontend = self.frontend)
@@ -264,5 +321,76 @@ module ActiveScaffold::Config
264
321
  frontends_dir = Rails.root.join('vendor', 'plugins', ActiveScaffold::Config::Core.plugin_directory, 'frontends')
265
322
  Dir.entries(frontends_dir).reject { |e| e.match(/^\./) } # Get rid of files that start with .
266
323
  end
324
+
325
+ class UserSettings < UserSettings
326
+ include ActiveScaffold::Configurable
327
+ user_attr :cache_action_link_urls, :cache_association_options, :conditional_get_support,
328
+ :timestamped_messages, :highlight_messages
329
+
330
+ def method_missing(name, *args)
331
+ value = @conf.actions.include?(name) ? @conf.send(name) : super
332
+ value.is_a?(Base) ? action_user_settings(value) : value
333
+ end
334
+
335
+ def respond_to_missing?(name, include_all = false)
336
+ super # avoid rubocop warning
337
+ end
338
+
339
+ def action_user_settings(action_config)
340
+ if action_config.user.nil? && action_config.respond_to?(:new_user_settings)
341
+ action_config.new_user_settings @storage, @params
342
+ end
343
+ action_config.user || action_config
344
+ end
345
+
346
+ def columns
347
+ @columns ||= UserColumns.new(@conf.columns)
348
+ end
349
+
350
+ def action_links
351
+ @action_links ||= CowProxy.wrap(@conf.action_links)
352
+ end
353
+
354
+ def model
355
+ @conf.model # for performance, called many times, so we avoid method_missing
356
+ end
357
+
358
+ def actions
359
+ @conf.actions # for performance, called many times, so we avoid method_missing
360
+ end
361
+ end
362
+
363
+ class UserColumns
364
+ include Enumerable
365
+
366
+ def initialize(columns)
367
+ @global_columns = columns
368
+ @columns = {}
369
+ end
370
+
371
+ def [](name)
372
+ return nil unless @global_columns[name]
373
+ @columns[name.to_sym] ||= CowProxy.wrap @global_columns[name]
374
+ end
375
+
376
+ def each
377
+ return enum_for(:each) unless block_given?
378
+ @global_columns.each do |col|
379
+ yield self[col.name]
380
+ end
381
+ end
382
+
383
+ def method_missing(name, *args, &block)
384
+ if @global_columns.respond_to?(name, true)
385
+ @global_columns.send(name, *args, &block)
386
+ else
387
+ super
388
+ end
389
+ end
390
+
391
+ def respond_to_missing?(name, include_all = false)
392
+ @global_columns.respond_to?(name, include_all) || super
393
+ end
394
+ end
267
395
  end
268
396
  end
@@ -12,7 +12,18 @@ module ActiveScaffold::Config
12
12
 
13
13
  # the ActionLink for this action
14
14
  cattr_accessor :link, instance_accessor: false
15
- @@link = ActiveScaffold::DataStructures::ActionLink.new('destroy', :label => :delete, :type => :member, :confirm => :are_you_sure_to_delete, :method => :delete, :crud_type => :delete, :position => false, :parameters => {:destroy_action => true}, :security_method => :delete_authorized?, :ignore_method => :delete_ignore?)
15
+ @@link = ActiveScaffold::DataStructures::ActionLink.new(
16
+ 'destroy',
17
+ :label => :delete,
18
+ :type => :member,
19
+ :method => :delete,
20
+ :crud_type => :delete,
21
+ :confirm => :are_you_sure_to_delete,
22
+ :position => false,
23
+ :parameters => {:destroy_action => true},
24
+ :security_method => :delete_authorized?,
25
+ :ignore_method => :delete_ignore?
26
+ )
16
27
 
17
28
  # whether we should refresh list after destroy or not
18
29
  cattr_accessor :refresh_list, instance_accessor: false
@@ -26,5 +37,7 @@ module ActiveScaffold::Config
26
37
 
27
38
  # whether we should refresh list after destroy or not
28
39
  attr_accessor :refresh_list
40
+
41
+ undef_method :new_user_settings
29
42
  end
30
43
  end
@@ -1,6 +1,7 @@
1
1
  module ActiveScaffold::Config
2
2
  class FieldSearch < Base
3
3
  self.crud_type = :read
4
+ NO_COLUMNS = [].freeze
4
5
 
5
6
  def initialize(core_config)
6
7
  super
@@ -52,7 +53,8 @@ module ActiveScaffold::Config
52
53
  end
53
54
 
54
55
  def optional_columns
55
- @optional_columns ||= []
56
+ return @optional_columns || NO_COLUMNS if frozen?
57
+ @optional_columns ||= NO_COLUMNS.dup
56
58
  end
57
59
 
58
60
  # add array of columns as options for group by to get aggregated listings
@@ -68,5 +70,9 @@ module ActiveScaffold::Config
68
70
  # human conditions
69
71
  # instead of just filtered you may show the user a humanized search condition statment
70
72
  attr_accessor :human_conditions
73
+
74
+ UserSettings.class_eval do
75
+ user_attr :optional_columns, :group_options, :grouped_columns, :human_conditions
76
+ end
71
77
  end
72
78
  end
@@ -43,7 +43,7 @@ module ActiveScaffold::Config
43
43
 
44
44
  columns_accessor :columns do
45
45
  columns.exclude :created_on, :created_at, :updated_on, :updated_at, :as_marked
46
- columns.exclude(*@core.columns.collect { |c| c.name if c.association.try(:polymorphic?) }.compact)
46
+ columns.exclude(*@core.columns.collect { |c| c.name if c.association&.polymorphic? }.compact)
47
47
  end
48
48
 
49
49
  # whether the form should be multipart
@@ -51,5 +51,14 @@ module ActiveScaffold::Config
51
51
  def multipart?
52
52
  @multipart ? true : false
53
53
  end
54
+
55
+ UserSettings.class_eval do
56
+ user_attr :persistent, :refresh_list, :show_unauthorized_columns
57
+
58
+ attr_writer :multipart
59
+ def multipart?
60
+ defined?(@multipart) ? @multipart : @conf.multipart?
61
+ end
62
+ end
54
63
  end
55
64
  end
@@ -186,8 +186,8 @@ module ActiveScaffold::Config
186
186
  def search_partial
187
187
  if @always_show_search == true
188
188
  auto_search_partial
189
- else
190
- @always_show_search.to_s if @core.actions.include? @always_show_search
189
+ elsif @core.actions.include? @always_show_search
190
+ @always_show_search.to_s
191
191
  end
192
192
  end
193
193
 
@@ -219,10 +219,16 @@ module ActiveScaffold::Config
219
219
  # order clause will be used for ETag when calculate_etag is disabled, so query for records can be avoided
220
220
  attr_accessor :calculate_etag
221
221
 
222
- class UserSettings < UserSettings
222
+ # don't inherit, defined with columns_accessor, class_eval would complain about block too long
223
+ class UserSettings
224
+ user_attr :page_links_inner_window, :page_links_outer_window, :refresh_with_header, :empty_field_text,
225
+ :association_join_text, :messages_above_header, :wrap_tag, :auto_select_columns, :calculate_etag,
226
+ :no_entries_message, :filtered_message, :show_search_reset, :always_show_create, :always_show_search,
227
+ :hide_nested_column, :pagination, :auto_pagination
228
+
223
229
  def initialize(conf, storage, params)
224
230
  super(conf, storage, params, :list)
225
- @sorting = nil
231
+ @_sorting = nil
226
232
  end
227
233
 
228
234
  attr_writer :label
@@ -232,7 +238,7 @@ module ActiveScaffold::Config
232
238
  end
233
239
 
234
240
  def embedded_label
235
- @params[:embedded][:label] if @params[:embedded]
241
+ @params.dig :embedded, :label
236
242
  end
237
243
 
238
244
  def per_page
@@ -240,6 +246,10 @@ module ActiveScaffold::Config
240
246
  self['per_page'] || @conf.per_page
241
247
  end
242
248
 
249
+ def per_page=(value)
250
+ self['per_page'] = value
251
+ end
252
+
243
253
  def page
244
254
  self['page'] = @params['page'] || 1 if @params.key?('page') || @conf.auto_pagination
245
255
  self['page'] || 1
@@ -252,41 +262,57 @@ module ActiveScaffold::Config
252
262
  attr_reader :nested_default_sorting
253
263
 
254
264
  def nested_default_sorting=(options)
255
- @nested_default_sorting ||= @conf.sorting.clone
265
+ @nested_default_sorting ||= sorting_dup
256
266
  @nested_default_sorting.set_nested_sorting(options[:table_name], options[:default_sorting])
257
267
  end
258
268
 
259
269
  def default_sorting
260
- nested_default_sorting.nil? ? @conf.sorting.clone : nested_default_sorting
270
+ nested_default_sorting.nil? || @sorting.present? ? sorting_dup : nested_default_sorting
261
271
  end
262
272
 
273
+ # TODO: programatically set sorting, for per-request configuration, priority @params, then @sort
274
+
263
275
  def user_sorting?
264
276
  @params['sort'] && @params['sort_direction'] != 'reset'
265
277
  end
266
278
 
279
+ # change list sorting for this request, unless sorting is defined
280
+ # {column => direction, column => direction}
281
+ attr_writer :sorting
282
+
267
283
  def sorting
268
- if @sorting.nil?
284
+ if @_sorting.nil?
269
285
  # we want to store as little as possible in the session, but we want to return a Sorting data structure. so we recreate it each page load based on session data.
270
286
  self['sort'] = [@params['sort'], @params['sort_direction']] if @params['sort'] && @params['sort_direction']
271
287
  self['sort'] = nil if @params['sort_direction'] == 'reset'
272
288
 
273
289
  if self['sort'] && @conf.core.columns[self['sort'][0]]
274
- sorting = @conf.sorting.clone
290
+ sorting = sorting_dup
275
291
  sorting.set(*self['sort'])
276
- @sorting = sorting
292
+ @_sorting = sorting
277
293
  else
278
- @sorting = default_sorting
294
+ @_sorting = default_sorting
295
+ @_sorting.set(@sorting) if @sorting
279
296
  if @conf.columns.constraint_columns.present?
280
- @sorting.constraint_columns = @conf.columns.constraint_columns
297
+ @_sorting.constraint_columns = @conf.columns.constraint_columns
281
298
  end
282
299
  end
283
300
  end
284
- @sorting
301
+ @_sorting
285
302
  end
286
303
 
287
304
  def count_includes
288
305
  @conf.count_includes
289
306
  end
307
+
308
+ protected
309
+
310
+ def sorting_dup
311
+ sorting = @conf.sorting.dup
312
+ # access to config instance columns instead of config class columns, in case columns are changed in this request
313
+ sorting.instance_variable_set :@columns, core.columns
314
+ sorting
315
+ end
290
316
  end
291
317
  end
292
318
  end
@@ -11,7 +11,7 @@ module ActiveScaffold::Config
11
11
  attr_accessor :mark_all_mode
12
12
 
13
13
  def initialize(core_config)
14
- @core = core_config
14
+ super
15
15
  @mark_all_mode = self.class.mark_all_mode
16
16
  @core.model.send(:include, ActiveScaffold::MarkedModel) unless @core.model < ActiveScaffold::MarkedModel
17
17
  add_mark_column
@@ -24,7 +24,9 @@ module ActiveScaffold::Config
24
24
  @core.columns[:as_marked].label = 'M'
25
25
  @core.columns[:as_marked].list_ui = :marked
26
26
  @core.columns[:as_marked].sort = false
27
- @core.list.columns = [:as_marked] + @core.list.columns.names_without_auth_check unless @core.list.columns.include? :as_marked
27
+ @core.list.columns = [:as_marked] + @core.list.columns.to_a unless @core.list.columns.include? :as_marked
28
28
  end
29
+
30
+ undef_method :new_user_settings
29
31
  end
30
32
  end
@@ -25,23 +25,20 @@ module ActiveScaffold::Config
25
25
  # Add a nested ActionLink
26
26
  def add_link(attribute, options = {})
27
27
  column = @core.columns[attribute.to_sym]
28
- if column && column.association
29
- label =
30
- if column.association.polymorphic?
31
- column.label
32
- else
33
- column.association.klass.model_name.human(:count => column.association.singular? ? 1 : 2, :default => column.association.klass.name.pluralize)
34
- end
35
- options.reverse_merge! :security_method => :nested_authorized?, :label => label
36
- action_group = options.delete(:action_group) || self.action_group
37
- action_link = @core.link_for_association(column, options)
38
- @core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
39
- action_link
40
- elsif column.nil?
41
- raise ArgumentError, "unknown column #{attribute}"
42
- elsif column.association.nil?
43
- raise ArgumentError, "column #{attribute} is not an association"
44
- end
28
+ raise ArgumentError, "unknown column #{attribute}" if column.nil?
29
+ raise ArgumentError, "column #{attribute} is not an association" if column.association.nil?
30
+
31
+ label =
32
+ if column.association.polymorphic?
33
+ column.label
34
+ else
35
+ column.association.klass.model_name.human(:count => column.association.singular? ? 1 : 2, :default => column.association.klass.name.pluralize)
36
+ end
37
+ options.reverse_merge! :security_method => :nested_authorized?, :label => label
38
+ action_group = options.delete(:action_group) || self.action_group
39
+ action_link = @core.link_for_association(column, options)
40
+ @core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
41
+ action_link
45
42
  end
46
43
 
47
44
  def add_scoped_link(named_scope, options = {})
@@ -51,5 +48,7 @@ module ActiveScaffold::Config
51
48
 
52
49
  # the label for this Nested action. used for the header.
53
50
  attr_writer :label
51
+
52
+ undef_method :new_user_settings
54
53
  end
55
54
  end