active_scaffold 3.6.20 → 3.7.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +47 -0
  3. data/README.md +29 -16
  4. data/app/assets/javascripts/jquery/active_scaffold.js +106 -68
  5. data/app/assets/javascripts/jquery/active_scaffold_chosen.js +6 -5
  6. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +18 -4
  7. data/app/assets/stylesheets/active_scaffold_layout.css +13 -2
  8. data/app/views/active_scaffold_overrides/_base_form.html.erb +5 -1
  9. data/app/views/active_scaffold_overrides/_field_search.html.erb +1 -0
  10. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +2 -1
  11. data/app/views/active_scaffold_overrides/_render_field.js.erb +24 -13
  12. data/config/locales/de.yml +6 -3
  13. data/config/locales/en.yml +3 -0
  14. data/config/locales/es.yml +3 -0
  15. data/config/locales/fr.yml +9 -6
  16. data/config/locales/hu.yml +20 -17
  17. data/config/locales/ja.yml +83 -80
  18. data/config/locales/ru.yml +17 -14
  19. data/lib/active_scaffold/actions/common_search.rb +2 -2
  20. data/lib/active_scaffold/actions/core.rb +30 -10
  21. data/lib/active_scaffold/actions/field_search.rb +9 -6
  22. data/lib/active_scaffold/actions/nested.rb +7 -7
  23. data/lib/active_scaffold/actions/update.rb +3 -3
  24. data/lib/active_scaffold/attribute_params.rb +22 -70
  25. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +0 -3
  26. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +6 -6
  27. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +7 -7
  28. data/lib/active_scaffold/bridges/active_storage.rb +3 -0
  29. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -2
  30. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -2
  31. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +12 -14
  32. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +2 -2
  33. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  34. data/lib/active_scaffold/bridges/chosen/helpers.rb +10 -10
  35. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +7 -7
  36. data/lib/active_scaffold/bridges/date_picker/ext.rb +20 -9
  37. data/lib/active_scaffold/bridges/date_picker/helper.rb +9 -9
  38. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  39. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -3
  40. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +5 -5
  41. data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
  42. data/lib/active_scaffold/bridges/file_column/list_ui.rb +3 -3
  43. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +1 -1
  44. data/lib/active_scaffold/bridges/paper_trail/actions.rb +4 -1
  45. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -3
  46. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  47. data/lib/active_scaffold/bridges/record_select/helpers.rb +17 -17
  48. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +6 -6
  49. data/lib/active_scaffold/bridges/tiny_mce.rb +1 -1
  50. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +6 -11
  51. data/lib/active_scaffold/bridges.rb +0 -3
  52. data/lib/active_scaffold/config/core.rb +1 -1
  53. data/lib/active_scaffold/config/field_search.rb +9 -1
  54. data/lib/active_scaffold/config/form.rb +9 -1
  55. data/lib/active_scaffold/constraints.rb +22 -7
  56. data/lib/active_scaffold/core.rb +6 -10
  57. data/lib/active_scaffold/data_structures/action_columns.rb +0 -25
  58. data/lib/active_scaffold/data_structures/action_links.rb +1 -1
  59. data/lib/active_scaffold/data_structures/association/abstract.rb +8 -0
  60. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +8 -0
  61. data/lib/active_scaffold/data_structures/association/active_record.rb +1 -13
  62. data/lib/active_scaffold/data_structures/association/mongoid.rb +21 -8
  63. data/lib/active_scaffold/data_structures/column.rb +139 -28
  64. data/lib/active_scaffold/data_structures/columns.rb +12 -12
  65. data/lib/active_scaffold/data_structures/nested_info.rb +12 -0
  66. data/lib/active_scaffold/data_structures/sorting.rb +1 -1
  67. data/lib/active_scaffold/engine.rb +15 -1
  68. data/lib/active_scaffold/extensions/action_view_rendering.rb +13 -5
  69. data/lib/active_scaffold/extensions/cow_proxy.rb +1 -1
  70. data/lib/active_scaffold/extensions/routing_mapper.rb +1 -0
  71. data/lib/active_scaffold/extensions/unsaved_record.rb +9 -3
  72. data/lib/active_scaffold/finder.rb +147 -28
  73. data/lib/active_scaffold/helpers/action_link_helpers.rb +1 -1
  74. data/lib/active_scaffold/helpers/controller_helpers.rb +9 -4
  75. data/lib/active_scaffold/helpers/form_column_helpers.rb +153 -107
  76. data/lib/active_scaffold/helpers/human_condition_helpers.rb +48 -14
  77. data/lib/active_scaffold/helpers/list_column_helpers.rb +37 -20
  78. data/lib/active_scaffold/helpers/search_column_helpers.rb +137 -55
  79. data/lib/active_scaffold/helpers/show_column_helpers.rb +6 -6
  80. data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
  81. data/lib/active_scaffold/orm_checks.rb +21 -1
  82. data/lib/active_scaffold/registry.rb +10 -15
  83. data/lib/active_scaffold/tableless.rb +10 -79
  84. data/lib/active_scaffold/version.rb +2 -2
  85. data/lib/active_scaffold.rb +3 -9
  86. data/lib/generators/active_scaffold/install_generator.rb +2 -2
  87. data/test/bridges/bridge_test.rb +1 -1
  88. data/test/bridges/date_picker_test.rb +3 -2
  89. data/test/bridges/paperclip_test.rb +18 -14
  90. data/test/bridges/tiny_mce_test.rb +5 -3
  91. data/test/config/base_test.rb +1 -1
  92. data/test/config/core_test.rb +1 -1
  93. data/test/config/create_test.rb +1 -1
  94. data/test/config/delete_test.rb +1 -1
  95. data/test/config/field_search_test.rb +1 -1
  96. data/test/config/list_test.rb +1 -1
  97. data/test/config/nested_test.rb +1 -1
  98. data/test/config/search_test.rb +1 -1
  99. data/test/config/show_test.rb +1 -1
  100. data/test/config/subform_test.rb +1 -1
  101. data/test/config/update_test.rb +1 -1
  102. data/test/data_structures/action_columns_test.rb +1 -1
  103. data/test/data_structures/action_link_test.rb +1 -1
  104. data/test/data_structures/action_links_test.rb +1 -1
  105. data/test/data_structures/actions_test.rb +1 -1
  106. data/test/data_structures/association_column_test.rb +1 -1
  107. data/test/data_structures/column_test.rb +1 -1
  108. data/test/data_structures/columns_test.rb +1 -1
  109. data/test/data_structures/set_test.rb +1 -1
  110. data/test/data_structures/sorting_test.rb +1 -1
  111. data/test/data_structures/standard_column_test.rb +1 -1
  112. data/test/data_structures/validation_reflection_test.rb +1 -1
  113. data/test/data_structures/virtual_column_test.rb +1 -1
  114. data/test/extensions/active_record_test.rb +1 -1
  115. data/test/helpers/form_column_helpers_test.rb +7 -5
  116. data/test/helpers/pagination_helpers_test.rb +1 -1
  117. data/test/helpers/search_column_helpers_test.rb +2 -1
  118. data/test/misc/active_record_permissions_test.rb +1 -1
  119. data/test/misc/attribute_params_test.rb +1 -1
  120. data/test/misc/calculation_test.rb +1 -1
  121. data/test/misc/configurable_test.rb +1 -1
  122. data/test/misc/constraints_test.rb +2 -1
  123. data/test/misc/convert_numbers_format_test.rb +1 -1
  124. data/test/misc/finder_test.rb +39 -1
  125. data/test/misc/lang_test.rb +1 -1
  126. data/test/misc/parse_datetime_test.rb +1 -1
  127. data/test/misc/tableless_test.rb +1 -1
  128. data/test/test_helper.rb +4 -4
  129. metadata +5 -17
  130. data/config/brakeman.ignore +0 -26
  131. data/config/brakeman.yml +0 -3
  132. data/config/i18n-tasks.yml +0 -121
  133. data/lib/active_scaffold/bridges/shared/date_bridge.rb +0 -221
  134. data/lib/active_scaffold/delayed_setup.rb +0 -41
  135. data/lib/active_scaffold/extensions/left_outer_joins.rb +0 -43
@@ -5,8 +5,13 @@ module ActiveScaffold
5
5
  def get_column_value(record, column)
6
6
  record = record.send(column.delegated_association.name) if column.delegated_association
7
7
  if record
8
- method = get_column_method(record, column)
9
- value = send(method, record, column)
8
+ method, list_ui = get_column_method(record, column)
9
+ value =
10
+ if list_ui
11
+ send(method, record, column, ui_options: column.list_ui_options || column.options)
12
+ else
13
+ send(method, record, column)
14
+ end
10
15
  else
11
16
  value = nil
12
17
  end
@@ -27,8 +32,8 @@ module ActiveScaffold
27
32
  method
28
33
  # second, check if the dev has specified a valid list_ui for this column
29
34
  elsif column.list_ui && (method = override_column_ui(column.list_ui))
30
- method
31
- elsif column.column && (method = override_column_ui(column.column.type))
35
+ [method, true]
36
+ elsif column.column && (method = override_column_ui(column.column_type))
32
37
  method
33
38
  else
34
39
  :format_column_value
@@ -76,52 +81,63 @@ module ActiveScaffold
76
81
  ##
77
82
  ## Overrides
78
83
  ##
79
- def active_scaffold_column_text(record, column)
84
+ def active_scaffold_column_text(record, column, ui_options: column.options)
80
85
  # `to_s` is necessary to convert objects in serialized columns to string before truncation.
81
- clean_column_value(truncate(record.send(column.name).to_s, :length => column.options[:truncate] || 50))
86
+ clean_column_value(truncate(record.send(column.name).to_s, length: ui_options[:truncate] || 50))
82
87
  end
83
88
 
84
- def active_scaffold_column_fulltext(record, column)
89
+ def active_scaffold_column_fulltext(record, column, ui_options: column.options)
85
90
  clean_column_value(record.send(column.name))
86
91
  end
87
92
 
88
- def active_scaffold_column_marked(record, column)
93
+ def active_scaffold_column_marked(record, column, ui_options: column.options)
89
94
  options = {:id => nil, :object => record}
90
95
  content_tag(:span, check_box(:record, column.name, options), :class => 'in_place_editor_field', :data => {:ie_id => record.to_param})
91
96
  end
92
97
 
93
- def active_scaffold_column_checkbox(record, column)
98
+ def active_scaffold_column_checkbox(record, column, ui_options: column.options)
94
99
  options = {:disabled => true, :id => nil, :object => record}
95
100
  options.delete(:disabled) if inplace_edit?(record, column)
96
101
  check_box(:record, column.name, options)
97
102
  end
98
103
 
99
- def active_scaffold_column_percentage(record, column)
100
- options = column.options[:slider] || {}
104
+ def active_scaffold_column_boolean(record, column, ui_options: column.options)
105
+ value = record.send(column.name)
106
+ if value.nil? && ui_options[:include_blank]
107
+ value = ui_options[:include_blank]
108
+ value.is_a?(Symbol) ? as_(value) : value
109
+ else
110
+ format_column_value(record, column, value)
111
+ end
112
+ end
113
+
114
+ def active_scaffold_column_percentage(record, column, ui_options: column.options)
115
+ options = ui_options[:slider] || {}
101
116
  options = options.merge(min: record.send(options[:min_method])) if options[:min_method]
102
117
  options = options.merge(max: record.send(options[:max_method])) if options[:max_method]
103
118
  value = record.send(options[:value_method]) if options[:value_method]
104
119
  as_slider options.merge(value: value || record.send(column.name))
105
120
  end
106
121
 
107
- def active_scaffold_column_month(record, column)
122
+ def active_scaffold_column_month(record, column, ui_options: column.options)
108
123
  l record.send(column.name), format: :year_month
109
124
  end
110
125
 
111
- def active_scaffold_column_week(record, column)
126
+ def active_scaffold_column_week(record, column, ui_options: column.options)
112
127
  l record.send(column.name), format: :week
113
128
  end
114
129
 
115
130
  def tel_to(text)
116
- groups = text.to_s.scan(/(?:^\+)?\d+/)
117
- extension = groups.pop if text.to_s =~ /\s*[^\d\s]+\s*\d+$/
131
+ text = text.to_s
132
+ groups = text.scan(/(?:^\+)?\d+/)
133
+ extension = groups.pop if text.match?(/\s*[^\d\s]+\s*\d+$/)
118
134
  link_to text, "tel:#{[groups.join('-'), extension].compact.join(',')}"
119
135
  end
120
136
 
121
- def active_scaffold_column_telephone(record, column)
137
+ def active_scaffold_column_telephone(record, column, ui_options: column.options)
122
138
  phone = record.send column.name
123
139
  return if phone.blank?
124
- phone = number_to_phone(phone) unless column.options[:format] == false
140
+ phone = number_to_phone(phone) unless ui_options[:format] == false
125
141
  tel_to phone
126
142
  end
127
143
 
@@ -146,8 +162,9 @@ module ActiveScaffold
146
162
  def format_column_value(record, column, value = nil)
147
163
  value ||= record.send(column.name) unless record.nil?
148
164
  if column.association.nil?
149
- if FORM_UI_WITH_OPTIONS.include?(column.form_ui) && column.options[:options]
150
- text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
165
+ form_ui_options = column.form_ui_options || column.options if FORM_UI_WITH_OPTIONS.include?(column.form_ui)
166
+ if form_ui_options&.dig(:options)
167
+ text, val = form_ui_options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
151
168
  value = active_scaffold_translated_option(column, text, val).first if text
152
169
  end
153
170
  if grouped_search? && column == search_group_column && search_group_function
@@ -302,7 +319,7 @@ module ActiveScaffold
302
319
  end
303
320
 
304
321
  def inplace_edit_cloning?(column)
305
- column.inplace_edit != :ajax && (override_form_field?(column) || column.form_ui || (column.column && override_input?(column.column.type)))
322
+ column.inplace_edit != :ajax && (override_form_field?(column) || column.form_ui || (column.column && override_input?(column.column_type)))
306
323
  end
307
324
 
308
325
  def active_scaffold_inplace_edit_tag_options(record, column)
@@ -6,6 +6,8 @@ module ActiveScaffold
6
6
  # It does not do any rendering. It only decides which method is responsible for rendering.
7
7
  def active_scaffold_search_for(column, options = nil)
8
8
  options ||= active_scaffold_search_options(column)
9
+ search_columns = active_scaffold_config.field_search.columns.visible_columns_names
10
+ options = update_columns_options(column, nil, options, form_columns: search_columns, url_params: {form_action: :field_search})
9
11
  record = options[:object]
10
12
  if column.delegated_association
11
13
  record = record.send(column.delegated_association.name) || column.active_record_class.new
@@ -18,11 +20,11 @@ module ActiveScaffold
18
20
 
19
21
  # second, check if the dev has specified a valid search_ui for this column, using specific ui for searches
20
22
  elsif column.search_ui && (method = override_search(column.search_ui))
21
- send(method, column, options)
23
+ send(method, column, options, ui_options: column.search_ui_options || column.options)
22
24
 
23
25
  # third, check if the dev has specified a valid search_ui for this column, using generic ui for forms
24
26
  elsif column.search_ui && (method = override_input(column.search_ui))
25
- send(method, column, options)
27
+ send(method, column, options, ui_options: column.search_ui_options || column.options)
26
28
 
27
29
  # fourth, check if the dev has created an override for this specific field
28
30
  elsif (method = override_form_field(column))
@@ -32,18 +34,17 @@ module ActiveScaffold
32
34
  elsif column.association || column.virtual?
33
35
  active_scaffold_search_text(column, options)
34
36
 
35
- elsif (method = override_search(column.column.type))
37
+ elsif (method = override_search(column.column_type))
36
38
  # if we (or someone else) have created a custom render option for the column type, use that
37
39
  send(method, column, options)
38
40
 
39
- elsif (method = override_input(column.column.type))
41
+ elsif (method = override_input(column.column_type))
40
42
  # if we (or someone else) have created a custom render option for the column type, use that
41
43
  send(method, column, options)
42
44
 
43
45
  else # final ultimate fallback: use rails' generic input method
44
46
  # for textual fields we pass different options
45
- text_types = %i[text string integer float decimal]
46
- options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
47
+ options = active_scaffold_input_text_options(options) if column.text? || column.number?
47
48
  text_field(:record, column.name, options.merge(column.options))
48
49
  end
49
50
  rescue StandardError => e
@@ -72,28 +73,28 @@ module ActiveScaffold
72
73
  ## Search input methods
73
74
  ##
74
75
 
75
- def active_scaffold_search_multi_select(column, options)
76
+ def active_scaffold_search_multi_select(column, options, ui_options: column.options)
76
77
  record = options.delete(:object)
77
78
  associated = options.delete :value
78
79
  associated = [associated].compact unless associated.is_a? Array
79
80
 
80
81
  if column.association
81
82
  associated.collect!(&:to_i)
82
- method = column.options[:label_method] || :to_label
83
+ method = ui_options[:label_method] || :to_label
83
84
  select_options = sorted_association_options_find(column.association, nil, record).collect do |r|
84
85
  [r.send(method), r.id]
85
86
  end
86
87
  else
87
- select_options = column.options[:options].collect do |text, value|
88
+ select_options = active_scaffold_enum_options(column, record, ui_options: ui_options).collect do |text, value|
88
89
  active_scaffold_translated_option(column, text, value)
89
90
  end
90
91
  end
91
92
  return as_(:no_options) if select_options.empty?
92
93
 
93
- active_scaffold_checkbox_list(column, select_options, associated, options)
94
+ active_scaffold_checkbox_list(column, select_options, associated, options, ui_options: ui_options)
94
95
  end
95
96
 
96
- def active_scaffold_search_select(column, html_options, options = {})
97
+ def active_scaffold_search_select(column, html_options, options = {}, ui_options: column.options)
97
98
  record = html_options.delete(:object)
98
99
  associated = html_options.delete :value
99
100
  if column.association
@@ -102,13 +103,13 @@ module ActiveScaffold
102
103
  select_options = sorted_association_options_find(column.association, false, record)
103
104
  else
104
105
  method = column.name
105
- select_options = active_scaffold_enum_options(column, record).collect do |text, value|
106
+ select_options = active_scaffold_enum_options(column, record, ui_options: ui_options).collect do |text, value|
106
107
  active_scaffold_translated_option(column, text, value)
107
108
  end
108
109
  end
109
110
 
110
- options = options.merge(:selected => associated).merge column.options
111
- html_options.merge! column.options[:html_options] || {}
111
+ options = options.merge(selected: associated).merge ui_options
112
+ html_options.merge! ui_options[:html_options] || {}
112
113
  if html_options[:multiple]
113
114
  active_scaffold_select_name_with_multiple html_options
114
115
  else
@@ -119,21 +120,34 @@ module ActiveScaffold
119
120
  if (optgroup = options.delete(:optgroup))
120
121
  select(:record, method, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
121
122
  elsif column.association
122
- collection_select(:record, method, select_options, :id, column.options[:label_method] || :to_label, options, html_options)
123
+ collection_select(:record, method, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
123
124
  else
124
125
  select(:record, method, select_options, options, html_options)
125
126
  end
126
127
  end
127
128
 
128
- def active_scaffold_search_text(column, options)
129
+ def active_scaffold_search_select_multiple(column, options, ui_options: column.options)
130
+ active_scaffold_search_select(column, options.merge(multiple: true), ui_options: ui_options)
131
+ end
132
+
133
+ def active_scaffold_search_draggable(column, options, ui_options: column.options)
134
+ active_scaffold_search_multi_select(column, options.merge(draggable_lists: true), ui_options: ui_options)
135
+ end
136
+
137
+ def active_scaffold_search_text(column, options, ui_options: column.options)
129
138
  text_field :record, column.name, active_scaffold_input_text_options(options)
130
139
  end
131
140
 
132
141
  # we can't use active_scaffold_input_boolean because we need to have a nil value even when column can't be null
133
142
  # to decide whether search for this field or not
134
- def active_scaffold_search_boolean(column, options)
143
+ def active_scaffold_search_boolean(column, options, ui_options: column.options)
135
144
  select_options = []
136
145
  select_options << [as_(:_select_), nil]
146
+ if column.null?
147
+ null_label = ui_options[:include_blank] || :null
148
+ null_label = as_(null_label) if null_label.is_a?(Symbol)
149
+ select_options << [null_label, 'null']
150
+ end
137
151
  select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
138
152
  select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
139
153
 
@@ -162,7 +176,7 @@ module ActiveScaffold
162
176
  end
163
177
  end
164
178
 
165
- def active_scaffold_search_null(column, options)
179
+ def active_scaffold_search_null(column, options, ui_options: column.options)
166
180
  select_options = []
167
181
  select_options << [as_(:_select_), nil]
168
182
  select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
@@ -179,38 +193,38 @@ module ActiveScaffold
179
193
  column.text? || column.search_ui == :string
180
194
  end
181
195
 
182
- def active_scaffold_search_range_comparator_options(column)
196
+ def active_scaffold_search_range_comparator_options(column, ui_options: column.options)
183
197
  select_options = ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
184
198
  if active_scaffold_search_range_string?(column)
185
199
  comparators = ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
186
200
  select_options.unshift(*comparators)
187
201
  end
188
- if include_null_comparators? column
202
+ if include_null_comparators? column, ui_options: ui_options
189
203
  select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
190
204
  end
191
205
  select_options
192
206
  end
193
207
 
194
- def include_null_comparators?(column)
195
- return column.options[:null_comparators] if column.options.key? :null_comparators
208
+ def include_null_comparators?(column, ui_options: column.options)
209
+ return ui_options[:null_comparators] if ui_options.key? :null_comparators
196
210
  if column.association
197
- !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].column&.null
211
+ !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].null?
198
212
  else
199
- column.column&.null
213
+ column.null?
200
214
  end
201
215
  end
202
216
 
203
- def active_scaffold_search_range(column, options, input_method = :text_field_tag, input_options = {})
217
+ def active_scaffold_search_range(column, options, input_method = :text_field_tag, input_options = {}, ui_options: column.options)
204
218
  opt_value, from_value, to_value = field_search_params_range_values(column)
205
219
 
206
- select_options = active_scaffold_search_range_comparator_options(column)
220
+ select_options = active_scaffold_search_range_comparator_options(column, ui_options: ui_options)
207
221
  text_field_size = active_scaffold_search_range_string?(column) ? 15 : 10
208
222
  opt_value ||= select_options[0][1]
209
223
 
210
224
  from_value = controller.class.condition_value_for_numeric(column, from_value)
211
225
  to_value = controller.class.condition_value_for_numeric(column, to_value)
212
- from_value = format_number_value(from_value, column.options) if from_value.is_a?(Numeric)
213
- to_value = format_number_value(to_value, column.options) if to_value.is_a?(Numeric)
226
+ from_value = format_number_value(from_value, ui_options) if from_value.is_a?(Numeric)
227
+ to_value = format_number_value(to_value, ui_options) if to_value.is_a?(Numeric)
214
228
  html = select_tag("#{options[:name]}[opt]", options_for_select(select_options, opt_value),
215
229
  :id => "#{options[:id]}_opt", :class => 'as_search_range_option')
216
230
  from_options = active_scaffold_input_text_options(input_options.merge(:id => options[:id], :size => text_field_size))
@@ -227,51 +241,119 @@ module ActiveScaffold
227
241
  end
228
242
  alias active_scaffold_search_string active_scaffold_search_range
229
243
 
230
- def active_scaffold_search_integer(column, options)
231
- active_scaffold_search_range(column, options, :number_field_tag, step: '1')
244
+ def active_scaffold_search_integer(column, options, ui_options: column.options)
245
+ active_scaffold_search_range(column, options, :number_field_tag, {step: '1'}, ui_options: ui_options) # rubocop:disable Style/BracesAroundHashParameters
232
246
  end
233
247
 
234
- def active_scaffold_search_decimal(column, options)
235
- active_scaffold_search_range(column, options, :number_field_tag, step: :any)
248
+ def active_scaffold_search_decimal(column, options, ui_options: column.options)
249
+ active_scaffold_search_range(column, options, :number_field_tag, {step: :any}, ui_options: ui_options) # rubocop:disable Style/BracesAroundHashParameters
236
250
  end
237
251
  alias active_scaffold_search_float active_scaffold_search_decimal
238
252
 
253
+ def active_scaffold_search_datetime(column, options, ui_options: column.options, field_ui: column.search_ui || :datetime)
254
+ current_search = {'from' => nil, 'to' => nil, 'opt' => 'BETWEEN',
255
+ 'number' => 1, 'unit' => 'DAYS', 'range' => nil}
256
+ current_search.merge!(options[:value]) unless options[:value].nil?
257
+ tags = [
258
+ active_scaffold_search_datetime_comparator_tag(column, options, current_search),
259
+ active_scaffold_search_datetime_trend_tag(column, options, current_search),
260
+ active_scaffold_search_datetime_numeric_tag(column, options, current_search, ui_options: ui_options, field_ui: field_ui),
261
+ active_scaffold_search_datetime_range_tag(column, options, current_search)
262
+ ]
263
+ safe_join tags, '&nbsp;'.html_safe # rubocop:disable Rails/OutputSafety
264
+ end
265
+
266
+ def active_scaffold_search_timestamp(column, options, ui_options: column.options)
267
+ active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :datetime)
268
+ end
269
+
270
+ def active_scaffold_search_time(column, options, ui_options: column.options)
271
+ active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :time)
272
+ end
273
+
274
+ def active_scaffold_search_date(column, options, ui_options: column.options)
275
+ active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :date)
276
+ end
277
+
278
+ def active_scaffold_search_datetime_comparator_options(column)
279
+ select_options = ActiveScaffold::Finder::DATE_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
280
+ select_options + ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
281
+ end
282
+
283
+ def active_scaffold_search_datetime_comparator_tag(column, options, current_search)
284
+ choices = options_for_select(active_scaffold_search_datetime_comparator_options(column), current_search['opt'])
285
+ select_tag("#{options[:name]}[opt]", choices, id: "#{options[:id]}_opt", class: 'as_search_range_option as_search_date_time_option')
286
+ end
287
+
288
+ def active_scaffold_search_datetime_numeric_tag(column, options, current_search, ui_options: column.options, field_ui: column.search_ui)
289
+ helper = "active_scaffold_search_#{field_ui}_field"
290
+ numeric_controls = [
291
+ send(helper, column, options, current_search, 'from', ui_options: ui_options),
292
+ content_tag(:span, id: "#{options[:id]}_between", class: 'as_search_range_between',
293
+ style: ('display: none' unless current_search['opt'] == 'BETWEEN')) do
294
+ safe_join([' - ', send(helper, column, options, current_search, 'to', ui_options: ui_options)])
295
+ end
296
+ ]
297
+ content_tag('span', safe_join(numeric_controls),
298
+ :id => "#{options[:id]}_numeric", :class => 'search-date-numeric',
299
+ :style => ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(current_search['opt']) ? nil : 'display: none')
300
+ end
301
+
302
+ def active_scaffold_search_datetime_trend_tag(column, options, current_search)
303
+ trend_controls = [
304
+ text_field_tag("#{options[:name]}[number]", current_search['number'], class: 'text-input', size: 10, autocomplete: 'off'),
305
+ select_tag("#{options[:name]}[unit]",
306
+ options_for_select(active_scaffold_search_datetime_trend_units(column), current_search['unit']),
307
+ class: 'text-input')
308
+ ]
309
+ content_tag('span', safe_join(trend_controls, ' '),
310
+ id: "#{options[:id]}_trend", class: 'search-date-trend',
311
+ style: ('display: none' unless current_search['opt'] == 'PAST' || current_search['opt'] == 'FUTURE'))
312
+ end
313
+
314
+ def active_scaffold_search_datetime_trend_units(column)
315
+ options = ActiveScaffold::Finder::DATE_UNITS.collect { |unit| [as_(unit.downcase.to_sym), unit] }
316
+ options = ActiveScaffold::Finder::TIME_UNITS.collect { |unit| [as_(unit.downcase.to_sym), unit] } + options if column_datetime?(column)
317
+ options
318
+ end
319
+
320
+ def active_scaffold_search_datetime_range_tag(column, options, current_search)
321
+ values = ActiveScaffold::Finder::DATE_RANGES.collect { |range| [as_(range.downcase.to_sym), range] }
322
+ range_controls = select_tag("#{options[:name]}[range]",
323
+ options_for_select(values, current_search['range']),
324
+ class: 'text-input', id: nil)
325
+ content_tag('span', range_controls,
326
+ id: "#{options[:id]}_range", class: 'search-date-range',
327
+ style: ('display: none' unless current_search['opt'] == 'RANGE'))
328
+ end
329
+
330
+ def column_datetime?(column)
331
+ (!column.column.nil? && %i[datetime time].include?(column.column_type))
332
+ end
333
+
239
334
  def field_search_datetime_value(value)
240
335
  Time.zone.local(value[:year].to_i, value[:month].to_i, value[:day].to_i, value[:hour].to_i, value[:minute].to_i, value[:second].to_i) unless value.nil? || value[:year].blank?
241
336
  end
242
337
 
243
- def active_scaffold_search_datetime(column, options)
244
- _, from_value, to_value = field_search_params_range_values(column)
245
- options = column.options.merge(options)
338
+ def active_scaffold_search_datetime_field(column, options, current_search, name, ui_options: column.options)
339
+ options = ui_options.merge(options)
246
340
  type = "#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
247
- use_select = options.delete(:use_select)
248
- from_name = "#{options[:name]}[from]"
249
- to_name = "#{options[:name]}[to]"
250
- if use_select
251
- helper = "select_#{type}"
252
- fields = [
253
- send(helper, field_search_datetime_value(from_value), options.reverse_merge(include_blank: true, prefix: from_name)),
254
- send(helper, field_search_datetime_value(to_value), options.reverse_merge(include_blank: true, prefix: to_name))
255
- ]
341
+ field_name = "#{options[:name]}[#{name}]"
342
+ if options[:use_select]
343
+ send("select_#{type}", current_search[name], options.reverse_merge(include_blank: true, prefix: field_name))
256
344
  else
257
345
  helper = "#{type}#{'_local' if type == 'datetime'}_field_tag"
258
- fields = [
259
- send(helper, from_name, field_search_datetime_value(from_value), options.except(:name, :object).merge(id: "#{options[:id]}_from")),
260
- send(helper, to_name, field_search_datetime_value(to_value), options.except(:name, :object).merge(id: "#{options[:id]}_to"))
261
- ]
346
+ send(helper, field_name, current_search[name], options.except(:name, :object, :use_select).merge(id: "#{options[:id]}_#{name}"))
262
347
  end
263
-
264
- safe_join fields, ' - '
265
348
  end
266
349
 
267
- def active_scaffold_search_date(column, options)
268
- active_scaffold_search_datetime(column, options.merge!(:discard_time => true))
350
+ def active_scaffold_search_date_field(column, options, current_search, name, ui_options: column.options)
351
+ active_scaffold_search_datetime_field(column, options.merge!(:discard_time => true), current_search, name, ui_options: ui_options)
269
352
  end
270
353
 
271
- def active_scaffold_search_time(column, options)
272
- active_scaffold_search_datetime(column, options.merge!(:discard_date => true))
354
+ def active_scaffold_search_time_field(column, options, current_search, name, ui_options: column.options)
355
+ active_scaffold_search_datetime_field(column, options.merge!(:discard_date => true), current_search, name, ui_options: ui_options)
273
356
  end
274
- alias active_scaffold_search_timestamp active_scaffold_search_datetime
275
357
 
276
358
  ##
277
359
  ## Search column override signatures
@@ -13,24 +13,24 @@ module ActiveScaffold
13
13
  send(method, value_record, column)
14
14
  # second, check if the dev has specified a valid list_ui for this column
15
15
  elsif column.show_ui && (method = override_show_column_ui(column.show_ui))
16
- send(method, value_record, column)
17
- elsif column.column && (method = override_show_column_ui(column.column.type))
16
+ send(method, value_record, column, ui_options: column.show_ui_options || column.options)
17
+ elsif column.column && (method = override_show_column_ui(column.column_type))
18
18
  send(method, value_record, column)
19
19
  else
20
20
  get_column_value(record, column)
21
21
  end
22
22
  end
23
23
 
24
- def active_scaffold_show_text(record, column)
25
- simple_format(clean_column_value(record.send(column.name)))
24
+ def active_scaffold_show_text(record, column, ui_options: column.options)
25
+ simple_format(clean_column_value(record.send(column.name)), ui_options[:html_options], ui_options)
26
26
  end
27
27
 
28
- def active_scaffold_show_horizontal(record, column)
28
+ def active_scaffold_show_horizontal(record, column, ui_options: column.options)
29
29
  raise ':horizontal show_ui must be used on association column' unless column.association
30
30
  render :partial => 'show_association', :locals => {:column => column, :parent_record => record, :show_partial => :horizontal}
31
31
  end
32
32
 
33
- def active_scaffold_show_vertical(record, column)
33
+ def active_scaffold_show_vertical(record, column, ui_options: column.options)
34
34
  raise ':vertical show_ui must be used on association column' unless column.association
35
35
  render :partial => 'show_association', :locals => {:column => column, :parent_record => record, :show_partial => :vertical}
36
36
  end
@@ -247,7 +247,7 @@ module ActiveScaffold
247
247
 
248
248
  message = options.include?(:message) ? options[:message] : as_('errors.template.body')
249
249
 
250
- error_messages = objects.sum do |object|
250
+ error_messages = objects.sum([]) do |object|
251
251
  object.errors.full_messages.map do |msg|
252
252
  options[:list_type] != :br ? content_tag(:li, msg) : msg
253
253
  end
@@ -82,6 +82,22 @@ module ActiveScaffold
82
82
  type_for_attribute(klass, column_name)
83
83
  end
84
84
  end
85
+
86
+ def default_value(klass, column_name)
87
+ if ActiveScaffold::OrmChecks.mongoid? klass
88
+ columns_hash(klass)[column_name]&.default_val
89
+ elsif ActiveScaffold::OrmChecks.active_record? klass
90
+ klass._default_attributes[column_name]&.value
91
+ end
92
+ end
93
+
94
+ def cast(klass, column_name, value)
95
+ if active_record? klass
96
+ type_for_attribute(klass, column_name).cast value
97
+ elsif mongoid? klass
98
+ type_for_attribute(klass, column_name)&.evolve value
99
+ end
100
+ end
85
101
  end
86
102
 
87
103
  %i[active_record? mongoid? tableless?].each do |method|
@@ -96,10 +112,14 @@ module ActiveScaffold
96
112
  end
97
113
  end
98
114
 
99
- %i[type_for_attribute column_type].each do |method|
115
+ %i[type_for_attribute column_type default_value].each do |method|
100
116
  define_method method do |column_name|
101
117
  ActiveScaffold::OrmChecks.send method, active_record_class, column_name
102
118
  end
103
119
  end
120
+
121
+ def cast(column_name, value)
122
+ ActiveScaffold::OrmChecks.cast active_record_class, column_name, value
123
+ end
104
124
  end
105
125
  end
@@ -1,33 +1,28 @@
1
1
  module ActiveScaffold
2
2
  class Registry
3
- extend ActiveSupport::PerThreadRegistry
4
- attr_accessor :current_user_proc, :current_ability_proc, :marked_records
3
+ thread_mattr_accessor :current_user_proc, :current_ability_proc, :marked_records
5
4
 
6
- def user_settings
7
- @user_settings ||= {}
5
+ def self.user_settings
6
+ RequestStore.store[:attr_Registry_user_settings] ||= {}
8
7
  end
9
8
 
10
- def constraint_columns
11
- @constraint_columns ||= Hash.new { |h, k| h[k] = [] }
9
+ def self.constraint_columns
10
+ RequestStore.store[:attr_Registry_constraint_columns] ||= Hash.new { |h, k| h[k] = [] }
12
11
  end
13
12
 
14
- def unauthorized_columns
15
- @unauthorized_columns ||= Hash.new { |h, k| h[k] = [] }
13
+ def self.unauthorized_columns
14
+ RequestStore.store[:attr_Registry_unauthorized_columns] ||= Hash.new { |h, k| h[k] = [] }
16
15
  end
17
16
 
18
- def cache(kind, key = nil, &block)
17
+ def self.cache(kind, key = nil, &block)
19
18
  unless key
20
19
  key = kind
21
20
  kind = :cache
22
21
  end
23
- @cache ||= {}
24
- cache = @cache[kind] ||= {}
22
+ RequestStore.store[:attr_Registry_cache] ||= {}
23
+ cache = RequestStore.store[:attr_Registry_cache][kind] ||= {}
25
24
  return cache[key] if cache.include? key
26
25
  cache[key] ||= yield
27
26
  end
28
-
29
- def self.instance
30
- RequestStore.store[@per_thread_registry_key] ||= new
31
- end
32
27
  end
33
28
  end