active_scaffold 3.6.0.pre → 3.6.0.rc1

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +39 -0
  3. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  4. data/app/assets/javascripts/jquery/active_scaffold.js +35 -4
  5. data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
  6. data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
  7. data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
  8. data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
  9. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
  10. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
  11. data/config/locales/de.yml +2 -1
  12. data/config/locales/en.yml +1 -0
  13. data/config/locales/es.yml +1 -0
  14. data/config/locales/fr.yml +2 -1
  15. data/config/locales/hu.yml +1 -0
  16. data/config/locales/ja.yml +1 -0
  17. data/config/locales/ru.yml +1 -0
  18. data/lib/active_scaffold.rb +8 -3
  19. data/lib/active_scaffold/actions/common_search.rb +11 -8
  20. data/lib/active_scaffold/actions/core.rb +79 -51
  21. data/lib/active_scaffold/actions/create.rb +27 -27
  22. data/lib/active_scaffold/actions/delete.rb +1 -1
  23. data/lib/active_scaffold/actions/field_search.rb +52 -42
  24. data/lib/active_scaffold/actions/list.rb +106 -23
  25. data/lib/active_scaffold/actions/nested.rb +59 -42
  26. data/lib/active_scaffold/actions/show.rb +3 -3
  27. data/lib/active_scaffold/actions/subform.rb +9 -16
  28. data/lib/active_scaffold/actions/update.rb +95 -77
  29. data/lib/active_scaffold/attribute_params.rb +93 -68
  30. data/lib/active_scaffold/bridges/active_storage.rb +6 -0
  31. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +33 -0
  32. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
  33. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
  34. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
  35. data/lib/active_scaffold/bridges/bitfields.rb +1 -0
  36. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  37. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +6 -0
  38. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  39. data/lib/active_scaffold/bridges/date_picker/helper.rb +46 -41
  40. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  41. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +2 -2
  42. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  43. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +3 -1
  44. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  45. data/lib/active_scaffold/bridges/record_select/helpers.rb +3 -7
  46. data/lib/active_scaffold/bridges/shared/date_bridge.rb +19 -18
  47. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  48. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +20 -3
  49. data/lib/active_scaffold/config/base.rb +58 -34
  50. data/lib/active_scaffold/config/core.rb +31 -12
  51. data/lib/active_scaffold/config/delete.rb +12 -1
  52. data/lib/active_scaffold/config/list.rb +17 -7
  53. data/lib/active_scaffold/config/mark.rb +1 -1
  54. data/lib/active_scaffold/configurable.rb +5 -3
  55. data/lib/active_scaffold/constraints.rb +21 -19
  56. data/lib/active_scaffold/core.rb +35 -26
  57. data/lib/active_scaffold/data_structures/action_columns.rb +1 -1
  58. data/lib/active_scaffold/data_structures/action_link.rb +34 -16
  59. data/lib/active_scaffold/data_structures/action_links.rb +9 -11
  60. data/lib/active_scaffold/data_structures/association/abstract.rb +35 -13
  61. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  62. data/lib/active_scaffold/data_structures/association/active_record.rb +5 -1
  63. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  64. data/lib/active_scaffold/data_structures/column.rb +49 -58
  65. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  66. data/lib/active_scaffold/data_structures/nested_info.rb +20 -18
  67. data/lib/active_scaffold/data_structures/sorting.rb +5 -0
  68. data/lib/active_scaffold/delayed_setup.rb +16 -6
  69. data/lib/active_scaffold/extensions/action_controller_rendering.rb +1 -1
  70. data/lib/active_scaffold/extensions/action_view_rendering.rb +34 -14
  71. data/lib/active_scaffold/extensions/cow_proxy.rb +50 -2
  72. data/lib/active_scaffold/extensions/localize.rb +3 -1
  73. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -2
  74. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  75. data/lib/active_scaffold/finder.rb +81 -46
  76. data/lib/active_scaffold/helpers/action_link_helpers.rb +47 -21
  77. data/lib/active_scaffold/helpers/association_helpers.rb +13 -11
  78. data/lib/active_scaffold/helpers/controller_helpers.rb +14 -11
  79. data/lib/active_scaffold/helpers/form_column_helpers.rb +133 -99
  80. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  81. data/lib/active_scaffold/helpers/id_helpers.rb +4 -0
  82. data/lib/active_scaffold/helpers/list_column_helpers.rb +76 -49
  83. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  84. data/lib/active_scaffold/helpers/search_column_helpers.rb +25 -30
  85. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  86. data/lib/active_scaffold/helpers/view_helpers.rb +31 -22
  87. data/lib/active_scaffold/orm_checks.rb +2 -2
  88. data/lib/active_scaffold/paginator.rb +1 -3
  89. data/lib/active_scaffold/registry.rb +11 -0
  90. data/lib/active_scaffold/responds_to_parent.rb +6 -5
  91. data/lib/active_scaffold/tableless.rb +6 -8
  92. data/lib/active_scaffold/version.rb +1 -1
  93. data/shoulda_macros/macros.rb +3 -1
  94. data/test/bridges/paperclip_test.rb +1 -1
  95. data/test/company.rb +2 -2
  96. data/test/data_structures/action_columns_test.rb +2 -2
  97. data/test/data_structures/column_test.rb +3 -6
  98. data/test/data_structures/columns_test.rb +2 -2
  99. data/test/extensions/active_record_test.rb +4 -4
  100. data/test/extensions/routing_mapper_test.rb +2 -2
  101. data/test/helpers/list_column_helpers_test.rb +3 -1
  102. data/test/misc/active_record_permissions_test.rb +2 -2
  103. data/test/misc/attribute_params_test.rb +4 -0
  104. data/test/misc/configurable_test.rb +10 -10
  105. data/test/misc/convert_numbers_format_test.rb +4 -0
  106. data/test/mock_app/app/assets/config/manifest.js +0 -0
  107. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  108. data/test/mock_app/app/controllers/people_controller.rb +3 -1
  109. data/test/mock_app/config/application.rb +1 -0
  110. data/test/mock_app/config/routes.rb +4 -1
  111. data/test/mock_app/db/schema.rb +2 -0
  112. data/test/performance/list_cars_performance_test.rb +34 -0
  113. data/test/performance/list_people_performance_test.rb +31 -0
  114. data/test/performance_test_help.rb +3 -0
  115. data/test/test_helper.rb +2 -1
  116. metadata +22 -12
  117. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  118. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -5
@@ -16,11 +16,8 @@ module ActiveScaffold
16
16
  def association_options_find(association, conditions = nil, klass = nil, record = nil)
17
17
  if klass.nil? && association.polymorphic?
18
18
  class_name = record.send(association.foreign_type) if association.belongs_to?
19
- if class_name.present?
20
- klass = class_name.constantize
21
- else
22
- return []
23
- end
19
+ return [] if class_name.blank?
20
+ klass = class_name.constantize
24
21
  cache = !block_given?
25
22
  else
26
23
  cache = !block_given? && klass.nil?
@@ -37,7 +34,9 @@ module ActiveScaffold
37
34
  relation = relation.includes(include_assoc[association.name]) if include_assoc
38
35
  end
39
36
  if column&.sort && column.sort&.dig(:sql)
40
- relation = relation.order(column.sort[:sql])
37
+ # with threasafe enabled, column.sort[:sql] returns proxied strings and
38
+ # regexp capture won't work, which rails uses internally, so to_s is needed
39
+ relation = relation.order(Array(column.sort[:sql]).map(&:to_s))
41
40
  end
42
41
  relation = yield(relation) if block_given?
43
42
  relation.to_a
@@ -45,7 +44,11 @@ module ActiveScaffold
45
44
  end
46
45
 
47
46
  def column_for_association(association, record)
48
- active_scaffold_config_for(record.class).columns[association.name] rescue nil
47
+ active_scaffold_config_for(record.class).columns[association.name]
48
+ rescue StandardError => e
49
+ message = "Error on config for #{record.class.name}:"
50
+ Rails.logger.warn "#{message}\n#{e.message}\n#{e.backtrace.join("\n")}"
51
+ nil
49
52
  end
50
53
 
51
54
  def association_klass_scoped(association, klass, record)
@@ -86,10 +89,9 @@ module ActiveScaffold
86
89
  # Check association.name to specialize the conditions per-column.
87
90
  def options_for_association_conditions(association, record = nil)
88
91
  return nil if association.through?
89
- if association.has_one? || association.has_many?
90
- # Find only orphaned objects
91
- {association.foreign_key => nil}
92
- end
92
+ return nil unless association.has_one? || association.has_many?
93
+ # Find only orphaned objects
94
+ {association.foreign_key => nil}
93
95
  end
94
96
 
95
97
  def record_select_params_for_add_existing(association, edit_associated_url_options, record)
@@ -2,15 +2,14 @@ module ActiveScaffold
2
2
  module Helpers
3
3
  module ControllerHelpers
4
4
  def self.included(controller)
5
- if controller.respond_to? :helper_method
6
- controller.class_eval do
7
- helper_method :params_for, :conditions_from_params, :render_parent?,
8
- :main_path_to_return, :render_parent_options,
9
- :render_parent_action, :nested_singular_association?,
10
- :main_form_controller, :build_associated,
11
- :generate_temporary_id, :generated_id,
12
- :active_scaffold_config_for
13
- end
5
+ return unless controller.respond_to? :helper_method
6
+ controller.class_eval do
7
+ helper_method :params_for, :conditions_from_params, :render_parent?,
8
+ :main_path_to_return, :render_parent_options,
9
+ :render_parent_action, :nested_singular_association?,
10
+ :main_form_controller, :build_associated,
11
+ :generate_temporary_id, :generated_id,
12
+ :active_scaffold_config_for
14
13
  end
15
14
  end
16
15
 
@@ -27,10 +26,14 @@ module ActiveScaffold
27
26
 
28
27
  def generate_temporary_id(record = nil, generated_id = nil)
29
28
  (generated_id || (Time.now.to_f * 1000).to_i.to_s).tap do |id|
30
- (@temporary_ids ||= {})[record.class.name] = id if record
29
+ cache_generated_id record, id
31
30
  end
32
31
  end
33
32
 
33
+ def cache_generated_id(record, generated_id)
34
+ (@temporary_ids ||= {})[record.class.name] = generated_id if record && generated_id
35
+ end
36
+
34
37
  def generated_id(record)
35
38
  @temporary_ids[record.class.name] if record && @temporary_ids
36
39
  end
@@ -101,7 +104,7 @@ module ActiveScaffold
101
104
  else
102
105
  exclude_parameters = %i[utf8 associated_id]
103
106
  parameters = {}
104
- if params[:parent_scaffold] && nested? && nested.singular_association?
107
+ if params[:parent_scaffold] && nested_singular_association?
105
108
  parameters[:controller] = params[:parent_scaffold]
106
109
  exclude_parameters.concat [nested.param_name, :association, :parent_scaffold]
107
110
  # parameters[:eid] = params[:parent_scaffold] # not neeeded anymore?
@@ -16,47 +16,43 @@ module ActiveScaffold
16
16
  # first, check if the dev has created an override for this specific field
17
17
  if (method = override_form_field(column))
18
18
  send(method, record, options)
19
+
19
20
  # second, check if the dev has specified a valid form_ui for this column
20
21
  elsif column.form_ui && (method = override_input(column.form_ui))
21
22
  send(method, column, options)
22
- # fallback: we get to make the decision
23
- else
24
- if column.association
25
- if column.form_ui.nil?
26
- # its an association and nothing is specified, we will assume form_ui :select
27
- active_scaffold_input_select(column, options)
28
- else
29
- # if we get here, it's because the column has a form_ui but not one ActiveScaffold knows about.
30
- raise "Unknown form_ui `#{column.form_ui}' for column `#{column.name}'"
31
- end
32
- elsif column.virtual?
33
- options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
34
- active_scaffold_input_virtual(column, options)
35
-
36
- else # regular model attribute column
37
- # if we (or someone else) have created a custom render option for the column type, use that
38
- if (method = override_input(column.column.type))
39
- send(method, column, options)
40
- # final ultimate fallback: use rails' generic input method
41
- else
42
- # for textual fields we pass different options
43
- text_types = %i[text string integer float decimal]
44
- options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
45
- if column.column.type == :string && options[:maxlength].blank?
46
- options[:maxlength] = column.column.limit
47
- options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
48
- end
49
- options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
50
- text_field(:record, column.name, options.merge(column.options).except(:format))
51
- end
23
+
24
+ elsif column.association
25
+ # if we get here, it's because the column has a form_ui but not one ActiveScaffold knows about.
26
+ raise "Unknown form_ui `#{column.form_ui}' for column `#{column.name}'" if column.form_ui
27
+
28
+ # its an association and nothing is specified, we will assume form_ui :select
29
+ active_scaffold_input_select(column, options)
30
+
31
+ elsif column.virtual?
32
+ options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
33
+ active_scaffold_input_virtual(column, options)
34
+
35
+ elsif (method = override_input(column.column.type)) # regular model attribute column
36
+ # if we (or someone else) have created a custom render option for the column type, use that
37
+ send(method, column, options)
38
+
39
+ else # final ultimate fallback: use rails' generic input method
40
+ # for textual fields we pass different options
41
+ text_types = %i[text string integer float decimal]
42
+ options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
43
+ if column.column.type == :string && options[:maxlength].blank?
44
+ options[:maxlength] = column.column.limit
45
+ options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
52
46
  end
47
+ options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
48
+ text_field(:record, column.name, options.merge(column.options).except(:format))
53
49
  end
54
50
  rescue StandardError => e
55
51
  logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
56
52
  raise e
57
53
  end
58
54
 
59
- def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false, record = nil)
55
+ def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false, record = nil) # rubocop:disable Metrics/ParameterLists
60
56
  if add_class
61
57
  col_class = []
62
58
  col_class << 'required' if column.required?
@@ -134,7 +130,7 @@ module ActiveScaffold
134
130
  url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
135
131
  end
136
132
  if scope
137
- url_params[:parent_controller] ||= url_params[:controller].gsub(%r{/^//}, '')
133
+ url_params[:parent_controller] ||= url_params[:controller].gsub(%r{^/}, '')
138
134
  url_params[:controller] = subform_controller.controller_path
139
135
  url_params[:scope] = scope
140
136
  url_params[:parent_id] = params[:parent_id] || params[:id]
@@ -152,8 +148,8 @@ module ActiveScaffold
152
148
  {}
153
149
  end
154
150
 
155
- def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil)
156
- if partial = override_form_field_partial(column)
151
+ def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil) # rubocop:disable Metrics/ParameterLists
152
+ if (partial = override_form_field_partial(column))
157
153
  render :partial => partial, :locals => {:column => column, :only_value => only_value, :scope => scope, :col_class => col_class, :record => record}
158
154
  elsif renders_as == :field || override_form_field?(column)
159
155
  form_attribute(column, record, scope, only_value, col_class)
@@ -179,12 +175,14 @@ module ActiveScaffold
179
175
  else
180
176
  field = active_scaffold_input_for column, scope, column_options
181
177
  end
178
+ if field
179
+ field << loading_indicator_tag(:action => :render_field, :id => params[:id]) if column.update_columns
180
+ field << content_tag(:span, column.description, :class => 'description') if column.description.present?
181
+ end
182
182
 
183
183
  content_tag :dl, attributes do
184
- %(<dt>#{label_tag label_for(column, column_options), column.label}</dt><dd>#{field}
185
- #{loading_indicator_tag(:action => :render_field, :id => params[:id]) if column.update_columns}
186
- #{content_tag :span, column.description, :class => 'description' if column.description.present?}
187
- </dd>).html_safe
184
+ content_tag(:dt, label_tag(label_for(column, column_options), form_column_label(column))) <<
185
+ content_tag(:dd, field)
188
186
  end
189
187
  end
190
188
 
@@ -192,6 +190,10 @@ module ActiveScaffold
192
190
  options[:id] unless column.form_ui == :select && column.association&.collection?
193
191
  end
194
192
 
193
+ def form_column_label(column)
194
+ column.label
195
+ end
196
+
195
197
  def subform_label(column, hidden)
196
198
  column.label unless hidden
197
199
  end
@@ -276,9 +278,8 @@ module ActiveScaffold
276
278
  end
277
279
 
278
280
  def active_scaffold_select_name_with_multiple(options)
279
- if options[:multiple] && !options[:name].to_s.ends_with?('[]')
280
- options[:name] = "#{options[:name]}[]"
281
- end
281
+ return if !options[:multiple] || options[:name].to_s.ends_with?('[]')
282
+ options[:name] = "#{options[:name]}[]"
282
283
  end
283
284
 
284
285
  def active_scaffold_input_singular_association(column, html_options, options = {})
@@ -303,10 +304,24 @@ module ActiveScaffold
303
304
  collection_select(:record, method, select_options, :id, column.options[:label_method] || :to_label, options, html_options)
304
305
  end
305
306
  html << active_scaffold_refresh_link(column, html_options, record) if column.options[:refresh_link]
307
+ html << active_scaffold_new_record_subform(column, record, html_options) if column.options[:add_new]
306
308
  html
307
309
  end
308
310
 
309
- def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, &block)
311
+ def active_scaffold_new_record_subform(column, record, html_options)
312
+ subform_attrs = active_scaffold_subform_attributes(column).merge(style: 'display: none')
313
+ scope = html_options[:name].scan(/record(.*)\[#{column.name}\]/).dig(0, 0)
314
+ new_record = build_associated(column.association, record)
315
+ subform = render(partial: subform_partial_for_column(column), locals: {column: column, parent_record: record, associated: [], show_blank_record: new_record, scope: scope})
316
+ html = content_tag(:div, subform, subform_attrs)
317
+ html << active_scaffold_show_new_subform_link(column, record, html_options[:id], subform_attrs[:id])
318
+ end
319
+
320
+ def active_scaffold_show_new_subform_link(column, record, select_id, subform_id)
321
+ link_to(as_(:create_new), '#', data: {select_id: select_id, subform_id: subform_id, subform_text: as_(:add_existing)}, class: 'show-new-subform')
322
+ end
323
+
324
+ def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, &block) # rubocop:disable Metrics/ParameterLists
310
325
  options = active_scaffold_input_text_options(options.merge(column.options))
311
326
  if content
312
327
  active_scaffold_file_with_content(column, content, options, remove_file_prefix, controls_class, &block)
@@ -329,12 +344,13 @@ module ActiveScaffold
329
344
  object_name, method = options[:name].split(/\[(#{column.name})\]/)
330
345
  method.sub!(/#{column.name}/, "#{remove_file_prefix}\\0")
331
346
  fields = block_given? ? yield : ''
347
+ link_key = options[:multiple] ? :remove_files : :remove_file
332
348
  input = file_field(:record, column.name, options.merge(:onchange => js_dont_remove_file_code))
333
349
  content_tag(:div, class: controls_class) do
334
350
  content_tag(:div) do
335
351
  safe_join [content, ' | ', fields,
336
352
  hidden_field(object_name, method, :value => 'false', class: 'remove_file'),
337
- content_tag(:a, as_(:remove_file), :href => '#', :onclick => js_remove_file_code)]
353
+ content_tag(:a, as_(link_key), :href => '#', :onclick => js_remove_file_code)]
338
354
  end << content_tag(:div, input, :style => 'display: none')
339
355
  end
340
356
  end
@@ -385,11 +401,11 @@ module ActiveScaffold
385
401
  label_method = column.options[:label_method] || :to_label
386
402
  html = hidden_field_tag("#{options[:name]}[]", '', :id => nil)
387
403
  html << content_tag(:ul, options.merge(:class => "#{options[:class]} checkbox-list#{' draggable-lists' if column.options[:draggable_lists]}")) do
388
- content = ''.html_safe
404
+ content = []
389
405
  select_options.each_with_index do |option, i|
390
406
  content << active_scaffold_checkbox_option(option, label_method, associated_ids, :name => "#{options[:name]}[]", :id => "#{options[:id]}_#{i}_id")
391
407
  end
392
- content
408
+ safe_join content
393
409
  end
394
410
  html
395
411
  end
@@ -454,10 +470,20 @@ module ActiveScaffold
454
470
  end
455
471
 
456
472
  selected = record.send(column.association.name)&.id if column.association
457
- radios = options.map do |option|
458
- active_scaffold_radio_option(option, selected, column, html_options)
473
+ if options.present?
474
+ radios = options.map do |option|
475
+ active_scaffold_radio_option(option, selected, column, html_options)
476
+ end
477
+ if column.options[:include_blank]
478
+ label = column.options[:include_blank]
479
+ label = as_(column.options[:include_blank]) if column.options[:include_blank].is_a?(Symbol)
480
+ radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: nil)) + label)
481
+ end
482
+ safe_join radios
483
+ else
484
+ content_tag(:span, as_(:no_options), :class => "#{html_options[:class]} no-options", :id => html_options[:id]) <<
485
+ hidden_field_tag(html_options[:name], '', :id => nil)
459
486
  end
460
- safe_join radios
461
487
  end
462
488
 
463
489
  def active_scaffold_input_checkbox(column, options)
@@ -519,16 +545,17 @@ module ActiveScaffold
519
545
 
520
546
  # A color picker
521
547
  def active_scaffold_input_color(column, options)
548
+ html = []
522
549
  options = active_scaffold_input_text_options(options)
523
550
  if column.column&.null
524
551
  no_color = options[:object].send(column.name).nil?
525
552
  method = no_color ? :hidden_field : :color_field
526
- html = content_tag(:label, check_box_tag('disable', '1', no_color, id: nil, name: nil, class: 'no-color') << " #{as_ column.options[:no_color] || :no_color}")
553
+ html << content_tag(:label, check_box_tag('disable', '1', no_color, id: nil, name: nil, class: 'no-color') << " #{as_ column.options[:no_color] || :no_color}")
527
554
  else
528
555
  method = :color_field
529
- html = ''.html_safe
530
556
  end
531
557
  html << send(method, :record, column.name, options.merge(column.options).except(:format, :no_color))
558
+ safe_join html
532
559
  end
533
560
 
534
561
  #
@@ -539,8 +566,8 @@ module ActiveScaffold
539
566
  record = options.delete(:object)
540
567
  select_options = []
541
568
  select_options << [as_(:_select_), nil] if !column.virtual? && column.column.null
542
- select_options << [as_(:true), true]
543
- select_options << [as_(:false), false]
569
+ select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
570
+ select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
544
571
 
545
572
  select_tag(options[:name], options_for_select(select_options, record.send(column.name)), options)
546
573
  end
@@ -636,12 +663,12 @@ module ActiveScaffold
636
663
  remote_controller = active_scaffold_controller_for(record_select_config.model).controller_path
637
664
  options[:controller] = remote_controller
638
665
  options.merge!(active_scaffold_input_text_options)
639
- record_select_field(options[:name], record, options)
666
+ record_select_field(options[:name], nil, options)
640
667
  else
641
668
  select_options = sorted_association_options_find(nested.association, nil, record)
642
669
  select_options ||= active_scaffold_config.model.all
643
670
  select_options = options_from_collection_for_select(select_options, :id, :to_label)
644
- select_tag 'associated_id', ('<option value="">' + as_(:_select_) + '</option>' + select_options).html_safe unless select_options.empty?
671
+ select_tag 'associated_id', (content_tag(:option, as_(:_select_), value: '') + select_options) unless select_options.empty?
645
672
  end
646
673
  end
647
674
 
@@ -654,55 +681,62 @@ module ActiveScaffold
654
681
  end
655
682
 
656
683
  # Try to get numerical constraints from model's validators
657
- def numerical_constraints_for_column(column, options)
658
- if column.numerical_constraints.nil?
659
- numerical_constraints = {}
660
- validators = column.active_record_class.validators.select do |v|
661
- v.is_a?(ActiveModel::Validations::NumericalityValidator) && v.attributes.include?(column.name)
684
+ def column_numerical_constraints(column, options)
685
+ validators = column.active_record_class.validators.select do |v|
686
+ v.is_a?(ActiveModel::Validations::NumericalityValidator) && v.attributes.include?(column.name)
687
+ end
688
+
689
+ equal_validator = validators.find { |v| v.options[:equal_to] }
690
+ # If there is equal_to constraint - use it (unless otherwise specified by user)
691
+ if equal_validator && !(options[:min] || options[:max])
692
+ equal_to = equal_validator.options[:equal_to]
693
+ return {min: equal_to, max: equal_to}
694
+ end
695
+
696
+ numerical_constraints = {}
697
+
698
+ # find minimum and maximum from validators
699
+ # we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
700
+ only_integer = column.column.type == :integer if column.column
701
+ only_integer ||= validators.find { |v| v.options[:only_integer] }.present?
702
+ margin = only_integer ? 1 : 0
703
+
704
+ # Minimum
705
+ unless options[:min]
706
+ min = validators.map { |v| v.options[:greater_than_or_equal] }.compact.max
707
+ greater_than = validators.map { |v| v.options[:greater_than] }.compact.max
708
+ numerical_constraints[:min] = [min, (greater_than + margin if greater_than)].compact.max
709
+ end
710
+
711
+ # Maximum
712
+ unless options[:max]
713
+ max = validators.map { |v| v.options[:less_than_or_equal] }.compact.min
714
+ less_than = validators.map { |v| v.options[:less_than] }.compact.min
715
+ numerical_constraints[:max] = [max, (less_than - margin if less_than)].compact.min
716
+ end
717
+
718
+ # Set step = 2 for column values restricted to be odd or even (but only if minimum is set)
719
+ unless options[:step]
720
+ only_odd_valid = validators.any? { |v| v.options[:odd] }
721
+ only_even_valid = validators.any? { |v| v.options[:even] } unless only_odd_valid
722
+ if !only_integer
723
+ numerical_constraints[:step] ||= "0.#{'0' * (column.column.scale - 1)}1" if column.column&.scale.to_i.positive?
724
+ elsif options[:min] && options[:min].respond_to?(:even?) && (only_odd_valid || only_even_valid)
725
+ numerical_constraints[:step] = 2
726
+ numerical_constraints[:min] += 1 if only_odd_valid && options[:min].even?
727
+ numerical_constraints[:min] += 1 if only_even_valid && options[:min].odd?
662
728
  end
663
- equal_to = (val = validators.find { |v| v.options[:equal_to] }) ? val.options[:equal_to] : nil
664
-
665
- # If there is equal_to constraint - use it (unless otherwise specified by user)
666
- if equal_to && !(options[:min] || options[:max])
667
- numerical_constraints[:min] = numerical_constraints[:max] = equal_to
668
- else # find minimum and maximum from validators
669
- # we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
670
- only_integer = column.column.type == :integer if column.column
671
- only_integer ||= validators.find { |v| v.options[:only_integer] }.present?
672
- margin = only_integer ? 1 : 0
673
-
674
- # Minimum
675
- unless options[:min]
676
- min = validators.map { |v| v.options[:greater_than_or_equal] }.compact.max
677
- greater_than = validators.map { |v| v.options[:greater_than] }.compact.max
678
- numerical_constraints[:min] = [min, (greater_than + margin if greater_than)].compact.max
679
- end
680
-
681
- # Maximum
682
- unless options[:max]
683
- max = validators.map { |v| v.options[:less_than_or_equal] }.compact.min
684
- less_than = validators.map { |v| v.options[:less_than] }.compact.min
685
- numerical_constraints[:max] = [max, (less_than - margin if less_than)].compact.min
686
- end
729
+ numerical_constraints[:step] ||= 'any' unless only_integer
730
+ end
687
731
 
688
- # Set step = 2 for column values restricted to be odd or even (but only if minimum is set)
689
- unless options[:step]
690
- only_odd_valid = validators.any? { |v| v.options[:odd] }
691
- only_even_valid = validators.any? { |v| v.options[:even] } unless only_odd_valid
692
- if !only_integer
693
- numerical_constraints[:step] ||= "0.#{'0' * (column.column.scale - 1)}1" if column.column&.scale.to_i.positive?
694
- elsif options[:min] && options[:min].respond_to?(:even?) && (only_odd_valid || only_even_valid)
695
- numerical_constraints[:step] = 2
696
- numerical_constraints[:min] += 1 if only_odd_valid && options[:min].even?
697
- numerical_constraints[:min] += 1 if only_even_valid && options[:min].odd?
698
- end
699
- numerical_constraints[:step] ||= 'any' unless only_integer
700
- end
701
- end
732
+ numerical_constraints
733
+ end
702
734
 
703
- column.numerical_constraints = numerical_constraints
735
+ def numerical_constraints_for_column(column, options)
736
+ constraints = Rails.cache.fetch("#{column.cache_key}#numerical_constarints") do
737
+ column_numerical_constraints(column, options)
704
738
  end
705
- column.numerical_constraints.merge(options)
739
+ constraints.merge(options)
706
740
  end
707
741
  end
708
742
  end