active_scaffold 3.5.3 → 3.6.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +73 -0
  3. data/README.md +17 -7
  4. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  5. data/app/assets/javascripts/jquery/active_scaffold.js +97 -6
  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 +9 -7
  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 +1 -0
  63. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  64. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
  65. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -12
  66. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
  67. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  68. data/lib/active_scaffold/bridges/chosen/helpers.rb +11 -9
  69. data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
  70. data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
  71. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  72. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
  73. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
  74. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  75. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
  76. data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
  77. data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
  78. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  79. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
  80. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  81. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
  82. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
  83. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  84. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
  85. data/lib/active_scaffold/config/base.rb +133 -41
  86. data/lib/active_scaffold/config/core.rb +146 -18
  87. data/lib/active_scaffold/config/delete.rb +14 -1
  88. data/lib/active_scaffold/config/field_search.rb +7 -1
  89. data/lib/active_scaffold/config/form.rb +10 -1
  90. data/lib/active_scaffold/config/list.rb +39 -13
  91. data/lib/active_scaffold/config/mark.rb +4 -2
  92. data/lib/active_scaffold/config/nested.rb +16 -17
  93. data/lib/active_scaffold/config/search.rb +9 -0
  94. data/lib/active_scaffold/config/show.rb +4 -0
  95. data/lib/active_scaffold/config/update.rb +4 -0
  96. data/lib/active_scaffold/configurable.rb +14 -7
  97. data/lib/active_scaffold/constraints.rb +22 -20
  98. data/lib/active_scaffold/core.rb +67 -28
  99. data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
  100. data/lib/active_scaffold/data_structures/action_link.rb +50 -20
  101. data/lib/active_scaffold/data_structures/action_links.rb +15 -13
  102. data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
  103. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  104. data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
  105. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  106. data/lib/active_scaffold/data_structures/column.rb +75 -66
  107. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  108. data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
  109. data/lib/active_scaffold/data_structures/set.rb +8 -0
  110. data/lib/active_scaffold/data_structures/sorting.rb +10 -2
  111. data/lib/active_scaffold/delayed_setup.rb +16 -5
  112. data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
  113. data/lib/active_scaffold/extensions/action_view_rendering.rb +34 -14
  114. data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
  115. data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
  116. data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
  117. data/lib/active_scaffold/extensions/localize.rb +3 -1
  118. data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
  119. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  120. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
  121. data/lib/active_scaffold/finder.rb +110 -77
  122. data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
  123. data/lib/active_scaffold/helpers/association_helpers.rb +21 -19
  124. data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
  125. data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
  126. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  127. data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
  128. data/lib/active_scaffold/helpers/list_column_helpers.rb +86 -57
  129. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  130. data/lib/active_scaffold/helpers/search_column_helpers.rb +29 -34
  131. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  132. data/lib/active_scaffold/helpers/view_helpers.rb +38 -35
  133. data/lib/active_scaffold/marked_model.rb +2 -2
  134. data/lib/active_scaffold/orm_checks.rb +3 -7
  135. data/lib/active_scaffold/paginator.rb +7 -7
  136. data/lib/active_scaffold/registry.rb +33 -0
  137. data/lib/active_scaffold/responds_to_parent.rb +8 -11
  138. data/lib/active_scaffold/tableless.rb +67 -65
  139. data/lib/active_scaffold/version.rb +2 -2
  140. data/lib/generators/active_scaffold/controller_generator.rb +2 -2
  141. data/lib/generators/active_scaffold/install_generator.rb +1 -1
  142. data/lib/generators/active_scaffold/resource_generator.rb +2 -2
  143. data/shoulda_macros/macros.rb +3 -1
  144. data/test/bridges/date_picker_test.rb +1 -2
  145. data/test/bridges/paperclip_test.rb +6 -6
  146. data/test/class_with_finder.rb +2 -2
  147. data/test/company.rb +4 -4
  148. data/test/config/create_test.rb +4 -2
  149. data/test/config/nested_test.rb +1 -1
  150. data/test/config/show_test.rb +1 -1
  151. data/test/config/update_test.rb +7 -6
  152. data/test/data_structures/action_columns_test.rb +2 -2
  153. data/test/data_structures/action_links_test.rb +1 -1
  154. data/test/data_structures/column_test.rb +3 -6
  155. data/test/data_structures/columns_test.rb +2 -2
  156. data/test/data_structures/sorting_test.rb +7 -0
  157. data/test/extensions/active_record_test.rb +4 -4
  158. data/test/extensions/routing_mapper_test.rb +2 -2
  159. data/test/helpers/list_column_helpers_test.rb +3 -1
  160. data/test/misc/active_record_permissions_test.rb +3 -11
  161. data/test/misc/attribute_params_test.rb +12 -8
  162. data/test/misc/calculation_test.rb +1 -1
  163. data/test/misc/configurable_test.rb +10 -10
  164. data/test/misc/constraints_test.rb +2 -2
  165. data/test/misc/convert_numbers_format_test.rb +7 -3
  166. data/test/misc/lang_test.rb +1 -1
  167. data/test/misc/parse_datetime_test.rb +3 -4
  168. data/test/misc/tableless_test.rb +6 -0
  169. data/test/mock_app/Rakefile +1 -1
  170. data/test/mock_app/app/assets/config/manifest.js +0 -0
  171. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  172. data/test/mock_app/app/controllers/people_controller.rb +3 -1
  173. data/test/mock_app/config/application.rb +2 -1
  174. data/test/mock_app/config/boot.rb +1 -1
  175. data/test/mock_app/config/environment.rb +2 -2
  176. data/test/mock_app/config/routes.rb +4 -1
  177. data/test/mock_app/db/schema.rb +2 -0
  178. data/test/performance/list_cars_performance_test.rb +34 -0
  179. data/test/performance/list_people_performance_test.rb +31 -0
  180. data/test/performance_test_help.rb +3 -0
  181. data/test/test_helper.rb +10 -2
  182. metadata +55 -20
  183. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  184. 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