active_scaffold 3.7.0 → 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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +20 -0
  3. data/README.md +2 -0
  4. data/app/assets/javascripts/jquery/active_scaffold.js +68 -62
  5. data/app/assets/stylesheets/active_scaffold_layout.css +1 -1
  6. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +2 -1
  7. data/app/views/active_scaffold_overrides/_render_field.js.erb +9 -6
  8. data/config/locales/de.yml +6 -3
  9. data/config/locales/en.yml +3 -0
  10. data/config/locales/es.yml +3 -0
  11. data/config/locales/fr.yml +9 -6
  12. data/config/locales/hu.yml +20 -17
  13. data/config/locales/ja.yml +25 -22
  14. data/config/locales/ru.yml +17 -14
  15. data/lib/active_scaffold/actions/update.rb +3 -3
  16. data/lib/active_scaffold/attribute_params.rb +7 -17
  17. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +6 -6
  18. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +7 -7
  19. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -2
  20. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +12 -14
  21. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +2 -2
  22. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  23. data/lib/active_scaffold/bridges/chosen/helpers.rb +10 -10
  24. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +7 -7
  25. data/lib/active_scaffold/bridges/date_picker/ext.rb +20 -9
  26. data/lib/active_scaffold/bridges/date_picker/helper.rb +5 -5
  27. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  28. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -3
  29. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +5 -5
  30. data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
  31. data/lib/active_scaffold/bridges/file_column/list_ui.rb +3 -3
  32. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +1 -1
  33. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -3
  34. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  35. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -15
  36. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +6 -6
  37. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +5 -5
  38. data/lib/active_scaffold/bridges.rb +0 -3
  39. data/lib/active_scaffold/constraints.rb +22 -7
  40. data/lib/active_scaffold/core.rb +5 -3
  41. data/lib/active_scaffold/data_structures/column.rb +108 -23
  42. data/lib/active_scaffold/engine.rb +15 -0
  43. data/lib/active_scaffold/extensions/routing_mapper.rb +1 -0
  44. data/lib/active_scaffold/finder.rb +142 -27
  45. data/lib/active_scaffold/helpers/controller_helpers.rb +9 -4
  46. data/lib/active_scaffold/helpers/form_column_helpers.rb +114 -94
  47. data/lib/active_scaffold/helpers/human_condition_helpers.rb +48 -14
  48. data/lib/active_scaffold/helpers/list_column_helpers.rb +34 -18
  49. data/lib/active_scaffold/helpers/search_column_helpers.rb +131 -55
  50. data/lib/active_scaffold/helpers/show_column_helpers.rb +6 -6
  51. data/lib/active_scaffold/orm_checks.rb +21 -1
  52. data/lib/active_scaffold/version.rb +1 -1
  53. data/lib/active_scaffold.rb +3 -2
  54. data/test/bridges/date_picker_test.rb +3 -2
  55. data/test/bridges/paperclip_test.rb +3 -2
  56. data/test/bridges/tiny_mce_test.rb +4 -2
  57. data/test/helpers/form_column_helpers_test.rb +7 -5
  58. data/test/helpers/search_column_helpers_test.rb +2 -1
  59. data/test/misc/constraints_test.rb +1 -0
  60. data/test/misc/finder_test.rb +38 -0
  61. metadata +2 -6
  62. data/config/brakeman.ignore +0 -26
  63. data/config/brakeman.yml +0 -3
  64. data/config/i18n-tasks.yml +0 -121
  65. data/lib/active_scaffold/bridges/shared/date_bridge.rb +0 -221
@@ -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,39 +81,49 @@ 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
 
@@ -119,10 +134,10 @@ module ActiveScaffold
119
134
  link_to text, "tel:#{[groups.join('-'), extension].compact.join(',')}"
120
135
  end
121
136
 
122
- def active_scaffold_column_telephone(record, column)
137
+ def active_scaffold_column_telephone(record, column, ui_options: column.options)
123
138
  phone = record.send column.name
124
139
  return if phone.blank?
125
- phone = number_to_phone(phone) unless column.options[:format] == false
140
+ phone = number_to_phone(phone) unless ui_options[:format] == false
126
141
  tel_to phone
127
142
  end
128
143
 
@@ -147,8 +162,9 @@ module ActiveScaffold
147
162
  def format_column_value(record, column, value = nil)
148
163
  value ||= record.send(column.name) unless record.nil?
149
164
  if column.association.nil?
150
- if FORM_UI_WITH_OPTIONS.include?(column.form_ui) && column.options[:options]
151
- 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 }
152
168
  value = active_scaffold_translated_option(column, text, val).first if text
153
169
  end
154
170
  if grouped_search? && column == search_group_column && search_group_function
@@ -303,7 +319,7 @@ module ActiveScaffold
303
319
  end
304
320
 
305
321
  def inplace_edit_cloning?(column)
306
- 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)))
307
323
  end
308
324
 
309
325
  def active_scaffold_inplace_edit_tag_options(record, column)
@@ -20,11 +20,11 @@ module ActiveScaffold
20
20
 
21
21
  # second, check if the dev has specified a valid search_ui for this column, using specific ui for searches
22
22
  elsif column.search_ui && (method = override_search(column.search_ui))
23
- send(method, column, options)
23
+ send(method, column, options, ui_options: column.search_ui_options || column.options)
24
24
 
25
25
  # third, check if the dev has specified a valid search_ui for this column, using generic ui for forms
26
26
  elsif column.search_ui && (method = override_input(column.search_ui))
27
- send(method, column, options)
27
+ send(method, column, options, ui_options: column.search_ui_options || column.options)
28
28
 
29
29
  # fourth, check if the dev has created an override for this specific field
30
30
  elsif (method = override_form_field(column))
@@ -34,11 +34,11 @@ module ActiveScaffold
34
34
  elsif column.association || column.virtual?
35
35
  active_scaffold_search_text(column, options)
36
36
 
37
- elsif (method = override_search(column.column.type))
37
+ elsif (method = override_search(column.column_type))
38
38
  # if we (or someone else) have created a custom render option for the column type, use that
39
39
  send(method, column, options)
40
40
 
41
- elsif (method = override_input(column.column.type))
41
+ elsif (method = override_input(column.column_type))
42
42
  # if we (or someone else) have created a custom render option for the column type, use that
43
43
  send(method, column, options)
44
44
 
@@ -73,28 +73,28 @@ module ActiveScaffold
73
73
  ## Search input methods
74
74
  ##
75
75
 
76
- def active_scaffold_search_multi_select(column, options)
76
+ def active_scaffold_search_multi_select(column, options, ui_options: column.options)
77
77
  record = options.delete(:object)
78
78
  associated = options.delete :value
79
79
  associated = [associated].compact unless associated.is_a? Array
80
80
 
81
81
  if column.association
82
82
  associated.collect!(&:to_i)
83
- method = column.options[:label_method] || :to_label
83
+ method = ui_options[:label_method] || :to_label
84
84
  select_options = sorted_association_options_find(column.association, nil, record).collect do |r|
85
85
  [r.send(method), r.id]
86
86
  end
87
87
  else
88
- 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|
89
89
  active_scaffold_translated_option(column, text, value)
90
90
  end
91
91
  end
92
92
  return as_(:no_options) if select_options.empty?
93
93
 
94
- active_scaffold_checkbox_list(column, select_options, associated, options)
94
+ active_scaffold_checkbox_list(column, select_options, associated, options, ui_options: ui_options)
95
95
  end
96
96
 
97
- def active_scaffold_search_select(column, html_options, options = {})
97
+ def active_scaffold_search_select(column, html_options, options = {}, ui_options: column.options)
98
98
  record = html_options.delete(:object)
99
99
  associated = html_options.delete :value
100
100
  if column.association
@@ -103,13 +103,13 @@ module ActiveScaffold
103
103
  select_options = sorted_association_options_find(column.association, false, record)
104
104
  else
105
105
  method = column.name
106
- 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|
107
107
  active_scaffold_translated_option(column, text, value)
108
108
  end
109
109
  end
110
110
 
111
- options = options.merge(:selected => associated).merge column.options
112
- html_options.merge! column.options[:html_options] || {}
111
+ options = options.merge(selected: associated).merge ui_options
112
+ html_options.merge! ui_options[:html_options] || {}
113
113
  if html_options[:multiple]
114
114
  active_scaffold_select_name_with_multiple html_options
115
115
  else
@@ -120,23 +120,31 @@ module ActiveScaffold
120
120
  if (optgroup = options.delete(:optgroup))
121
121
  select(:record, method, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
122
122
  elsif column.association
123
- 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)
124
124
  else
125
125
  select(:record, method, select_options, options, html_options)
126
126
  end
127
127
  end
128
128
 
129
- 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)
130
138
  text_field :record, column.name, active_scaffold_input_text_options(options)
131
139
  end
132
140
 
133
141
  # we can't use active_scaffold_input_boolean because we need to have a nil value even when column can't be null
134
142
  # to decide whether search for this field or not
135
- def active_scaffold_search_boolean(column, options)
143
+ def active_scaffold_search_boolean(column, options, ui_options: column.options)
136
144
  select_options = []
137
145
  select_options << [as_(:_select_), nil]
138
- if column.column&.null
139
- null_label = column.options[:include_blank] || :null
146
+ if column.null?
147
+ null_label = ui_options[:include_blank] || :null
140
148
  null_label = as_(null_label) if null_label.is_a?(Symbol)
141
149
  select_options << [null_label, 'null']
142
150
  end
@@ -168,7 +176,7 @@ module ActiveScaffold
168
176
  end
169
177
  end
170
178
 
171
- def active_scaffold_search_null(column, options)
179
+ def active_scaffold_search_null(column, options, ui_options: column.options)
172
180
  select_options = []
173
181
  select_options << [as_(:_select_), nil]
174
182
  select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
@@ -185,38 +193,38 @@ module ActiveScaffold
185
193
  column.text? || column.search_ui == :string
186
194
  end
187
195
 
188
- def active_scaffold_search_range_comparator_options(column)
196
+ def active_scaffold_search_range_comparator_options(column, ui_options: column.options)
189
197
  select_options = ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
190
198
  if active_scaffold_search_range_string?(column)
191
199
  comparators = ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
192
200
  select_options.unshift(*comparators)
193
201
  end
194
- if include_null_comparators? column
202
+ if include_null_comparators? column, ui_options: ui_options
195
203
  select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
196
204
  end
197
205
  select_options
198
206
  end
199
207
 
200
- def include_null_comparators?(column)
201
- 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
202
210
  if column.association
203
- !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?
204
212
  else
205
- column.column&.null
213
+ column.null?
206
214
  end
207
215
  end
208
216
 
209
- 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)
210
218
  opt_value, from_value, to_value = field_search_params_range_values(column)
211
219
 
212
- select_options = active_scaffold_search_range_comparator_options(column)
220
+ select_options = active_scaffold_search_range_comparator_options(column, ui_options: ui_options)
213
221
  text_field_size = active_scaffold_search_range_string?(column) ? 15 : 10
214
222
  opt_value ||= select_options[0][1]
215
223
 
216
224
  from_value = controller.class.condition_value_for_numeric(column, from_value)
217
225
  to_value = controller.class.condition_value_for_numeric(column, to_value)
218
- from_value = format_number_value(from_value, column.options) if from_value.is_a?(Numeric)
219
- 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)
220
228
  html = select_tag("#{options[:name]}[opt]", options_for_select(select_options, opt_value),
221
229
  :id => "#{options[:id]}_opt", :class => 'as_search_range_option')
222
230
  from_options = active_scaffold_input_text_options(input_options.merge(:id => options[:id], :size => text_field_size))
@@ -233,51 +241,119 @@ module ActiveScaffold
233
241
  end
234
242
  alias active_scaffold_search_string active_scaffold_search_range
235
243
 
236
- def active_scaffold_search_integer(column, options)
237
- 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
238
246
  end
239
247
 
240
- def active_scaffold_search_decimal(column, options)
241
- 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
242
250
  end
243
251
  alias active_scaffold_search_float active_scaffold_search_decimal
244
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
+
245
334
  def field_search_datetime_value(value)
246
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?
247
336
  end
248
337
 
249
- def active_scaffold_search_datetime(column, options)
250
- _, from_value, to_value = field_search_params_range_values(column)
251
- 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)
252
340
  type = "#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
253
- use_select = options.delete(:use_select)
254
- from_name = "#{options[:name]}[from]"
255
- to_name = "#{options[:name]}[to]"
256
- if use_select
257
- helper = "select_#{type}"
258
- fields = [
259
- send(helper, field_search_datetime_value(from_value), options.reverse_merge(include_blank: true, prefix: from_name)),
260
- send(helper, field_search_datetime_value(to_value), options.reverse_merge(include_blank: true, prefix: to_name))
261
- ]
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))
262
344
  else
263
345
  helper = "#{type}#{'_local' if type == 'datetime'}_field_tag"
264
- fields = [
265
- send(helper, from_name, field_search_datetime_value(from_value), options.except(:name, :object).merge(id: "#{options[:id]}_from")),
266
- send(helper, to_name, field_search_datetime_value(to_value), options.except(:name, :object).merge(id: "#{options[:id]}_to"))
267
- ]
346
+ send(helper, field_name, current_search[name], options.except(:name, :object, :use_select).merge(id: "#{options[:id]}_#{name}"))
268
347
  end
269
-
270
- safe_join fields, ' - '
271
348
  end
272
349
 
273
- def active_scaffold_search_date(column, options)
274
- 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)
275
352
  end
276
353
 
277
- def active_scaffold_search_time(column, options)
278
- 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)
279
356
  end
280
- alias active_scaffold_search_timestamp active_scaffold_search_datetime
281
357
 
282
358
  ##
283
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
@@ -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
@@ -2,7 +2,7 @@ module ActiveScaffold
2
2
  module Version
3
3
  MAJOR = 3
4
4
  MINOR = 7
5
- PATCH = 0
5
+ PATCH = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
@@ -3,6 +3,7 @@ module ActiveScaffold
3
3
  autoload :AttributeParams, 'active_scaffold/attribute_params'
4
4
  autoload :Bridges, 'active_scaffold/bridges'
5
5
  autoload :Configurable, 'active_scaffold/configurable'
6
+ autoload :ConnectionAdapters, 'active_scaffold/extensions/connection_adapter.rb'
6
7
  autoload :Constraints, 'active_scaffold/constraints'
7
8
  autoload :Core, 'active_scaffold/core'
8
9
  autoload :Finder, 'active_scaffold/finder'
@@ -78,6 +79,8 @@ module ActiveScaffold
78
79
  @@exclude_bridges ||= []
79
80
  end
80
81
 
82
+ mattr_accessor :nested_subforms, instance_writer: false
83
+
81
84
  def self.root
82
85
  File.dirname(__FILE__) + '/..'
83
86
  end
@@ -90,5 +93,3 @@ require 'active_scaffold/engine'
90
93
  require 'ice_nine'
91
94
  require 'ice_nine/core_ext/object'
92
95
  require 'request_store'
93
- # TODO: clean up extensions. some could be organized for autoloading, and others could be removed entirely.
94
- Dir["#{File.dirname __FILE__}/active_scaffold/extensions/*.rb"].each { |file| require file }
@@ -24,7 +24,8 @@ class DatePickerTest < ActionView::TestCase
24
24
  def test_form_ui
25
25
  config = ActiveScaffold::Config::Core.new(:company)
26
26
  @record = Company.new
27
- assert active_scaffold_input_date_picker(config.columns[:date], :name => 'record[date]', :id => 'record_date', :object => @record)
28
- assert active_scaffold_input_date_picker(config.columns[:datetime], :name => 'record[datetime]', :id => 'record_datetime', :object => @record)
27
+ opts = {object: @record}
28
+ assert active_scaffold_input_date_picker(config.columns[:date], opts.merge(id: 'record_date', name: 'record[date]'))
29
+ assert active_scaffold_input_date_picker(config.columns[:datetime], opts.merge(id: 'record_date', name: 'record[datetime]'))
29
30
  end
30
31
  end
@@ -58,11 +58,12 @@ class PaperclipTest < ActionView::TestCase
58
58
  with_js_framework :jquery do
59
59
  config = PaperclipCore.new(:company)
60
60
  @record = Company.new
61
+ opts = {name: 'record[logo]', id: 'record_logo', object: @record}
61
62
 
62
63
  @record.stubs(:logo).returns(stub(:file? => true, :original_filename => 'file', :url => '/system/file', :styles => Company.attachment_definitions[:logo]))
63
64
  click_js = "jQuery(this).prev().val('true'); jQuery(this).parent().hide().next().show(); return false;"
64
65
  change_js = "jQuery(this).parents('div.paperclip_controls').find('input.remove_file').val('false'); return false;"
65
- @document = Nokogiri::HTML::Document.parse(active_scaffold_input_paperclip(config.columns[:logo], :name => 'record[logo]', :id => 'record_logo', :object => @record))
66
+ @document = Nokogiri::HTML::Document.parse(active_scaffold_input_paperclip(config.columns[:logo], opts))
66
67
  assert_select 'div.paperclip_controls input[type=file]' do |match|
67
68
  assert_equal match[0]['onchange'], change_js
68
69
  end
@@ -72,7 +73,7 @@ class PaperclipTest < ActionView::TestCase
72
73
  assert_select 'div.paperclip_controls input.remove_file[type=hidden][value=false]'
73
74
 
74
75
  @record.stubs(:logo).returns(stub(:file? => false))
75
- assert_dom_equal '<input name="record[logo]" class="text-input" autocomplete="off" type="file" id="record_logo" />', active_scaffold_input_paperclip(config.columns[:logo], :name => 'record[logo]', :id => 'record_logo', :object => @record)
76
+ assert_dom_equal '<input name="record[logo]" class="text-input" autocomplete="off" type="file" id="record_logo" />', active_scaffold_input_paperclip(config.columns[:logo], opts)
76
77
  end
77
78
  end
78
79
 
@@ -35,16 +35,18 @@ class TinyMceTest < ActionView::TestCase
35
35
  def test_form_ui
36
36
  config = ActiveScaffold::Config::Core.new(:company)
37
37
  record = Company.new
38
+ opts = {name: 'record[name]', id: 'record_name', class: 'name-input', object: record}
38
39
 
39
- assert_dom_equal %(<textarea name=\"record[name]\" class=\"name-input mceEditor\" id=\"record_name\" data-tinymce=\"{&quot;theme&quot;:&quot;modern&quot;}\">\n</textarea>), active_scaffold_input_text_editor(config.columns[:name], :name => 'record[name]', :id => 'record_name', :class => 'name-input', :object => record)
40
+ assert_dom_equal %(<textarea name=\"record[name]\" class=\"name-input mceEditor\" id=\"record_name\" data-tinymce=\"{&quot;theme&quot;:&quot;modern&quot;}\">\n</textarea>), active_scaffold_input_text_editor(config.columns[:name], opts)
40
41
  end
41
42
 
42
43
  def test_form_ui_alternate
43
44
  config = ActiveScaffold::Config::Core.new(:company)
44
45
  record = Company.new
45
46
  config.columns[:name].options[:tinymce_config] = :alternate
47
+ opts = {name: 'record[name]', id: 'record_name', class: 'name-input', object: record}
46
48
 
47
- assert_dom_equal %(<textarea name=\"record[name]\" class=\"name-input mceEditor\" id=\"record_name\" data-tinymce=\"{&quot;theme&quot;:&quot;alternate&quot;,&quot;toolbar&quot;:&quot;undo redo | format&quot;}\">\n</textarea>), active_scaffold_input_tinymce(config.columns[:name], :name => 'record[name]', :id => 'record_name', :class => 'name-input', :object => record)
49
+ assert_dom_equal %(<textarea name=\"record[name]\" class=\"name-input mceEditor\" id=\"record_name\" data-tinymce=\"{&quot;theme&quot;:&quot;alternate&quot;,&quot;toolbar&quot;:&quot;undo redo | format&quot;}\">\n</textarea>), active_scaffold_input_tinymce(config.columns[:name], opts)
48
50
  end
49
51
 
50
52
  protected
@@ -9,23 +9,25 @@ class FormColumnHelpersTest < ActionView::TestCase
9
9
  end
10
10
 
11
11
  def test_choices_for_select_form_ui_for_simple_column
12
+ opts = {object: @record}
12
13
  @column.options[:options] = %i[value_1 value_2 value_3]
13
- assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">Value 1</option>\n<option value=\"value_2\">Value 2</option>\n<option value=\"value_3\">Value 3</option></select>", active_scaffold_input_select(@column, :object => @record)
14
+ assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">Value 1</option>\n<option value=\"value_2\">Value 2</option>\n<option value=\"value_3\">Value 3</option></select>", active_scaffold_input_select(@column, opts.dup)
14
15
 
15
16
  @column.options[:options] = %w[value_1 value_2 value_3]
16
- assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">value_1</option>\n<option value=\"value_2\">value_2</option>\n<option value=\"value_3\">value_3</option></select>", active_scaffold_input_select(@column, :object => @record)
17
+ assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">value_1</option>\n<option value=\"value_2\">value_2</option>\n<option value=\"value_3\">value_3</option></select>", active_scaffold_input_select(@column, opts.dup)
17
18
 
18
19
  @column.options[:options] = [%w[text_1 value_1], %w[text_2 value_2], %w[text_3 value_3]]
19
- assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">text_1</option>\n<option value=\"value_2\">text_2</option>\n<option value=\"value_3\">text_3</option></select>", active_scaffold_input_select(@column, :object => @record)
20
+ assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">text_1</option>\n<option value=\"value_2\">text_2</option>\n<option value=\"value_3\">text_3</option></select>", active_scaffold_input_select(@column, opts.dup)
20
21
 
21
22
  @column.options[:options] = [%i[text_1 value_1], %i[text_2 value_2], %i[text_3 value_3]]
22
- assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">Text 1</option>\n<option value=\"value_2\">Text 2</option>\n<option value=\"value_3\">Text 3</option></select>", active_scaffold_input_select(@column, :object => @record)
23
+ assert_dom_equal "<select name=\"record[a]\" id=\"record_a\"><option value=\"value_1\">Text 1</option>\n<option value=\"value_2\">Text 2</option>\n<option value=\"value_3\">Text 3</option></select>", active_scaffold_input_select(@column, opts.dup)
23
24
  end
24
25
 
25
26
  def test_options_for_select_form_ui_for_simple_column
27
+ opts = {object: @record}
26
28
  @column.options = {:include_blank => 'None', :selected => 'value_2', :disabled => %w[value_1 value_3]}
27
29
  @column.options[:options] = %w[value_1 value_2 value_3]
28
30
  @column.options[:html_options] = {:class => 'big'}
29
- assert_dom_equal "<select name=\"record[a]\" class=\"big\" id=\"record_a\"><option value=\"\">None</option>\n<option disabled=\"disabled\" value=\"value_1\">value_1</option>\n<option selected=\"selected\" value=\"value_2\">value_2</option>\n<option disabled=\"disabled\" value=\"value_3\">value_3</option></select>", active_scaffold_input_select(@column, :object => @record)
31
+ assert_dom_equal "<select name=\"record[a]\" class=\"big\" id=\"record_a\"><option value=\"\">None</option>\n<option disabled=\"disabled\" value=\"value_1\">value_1</option>\n<option selected=\"selected\" value=\"value_2\">value_2</option>\n<option disabled=\"disabled\" value=\"value_3\">value_3</option></select>", active_scaffold_input_select(@column, opts)
30
32
  end
31
33
  end