active_scaffold 3.6.20 → 3.7.1

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