active_scaffold 3.6.0.pre → 3.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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