active_scaffold 4.3.2 → 4.4.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +19 -0
  3. data/README.md +1 -1
  4. data/app/assets/javascripts/active_scaffold/active_scaffold.js +98 -11
  5. data/app/assets/javascripts/active_scaffold/draggable_lists.js +4 -3
  6. data/app/assets/javascripts/active_scaffold/load.js +20 -5
  7. data/app/assets/stylesheets/active_scaffold/_images.scss +53 -1
  8. data/app/assets/stylesheets/active_scaffold/_layout.scss +1 -66
  9. data/app/views/active_scaffold_overrides/_add_existing_form.html.erb +17 -11
  10. data/app/views/active_scaffold_overrides/_base_form.html.erb +29 -19
  11. data/app/views/active_scaffold_overrides/_field_search.html.erb +9 -9
  12. data/app/views/active_scaffold_overrides/_field_search_columns.html.erb +29 -33
  13. data/app/views/active_scaffold_overrides/_form.html.erb +43 -34
  14. data/app/views/active_scaffold_overrides/_form_association.html.erb +3 -3
  15. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +20 -16
  16. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +3 -4
  17. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
  18. data/app/views/active_scaffold_overrides/_list.html.erb +6 -4
  19. data/app/views/active_scaffold_overrides/_list_calculations.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_list_header.html.erb +3 -3
  21. data/app/views/active_scaffold_overrides/_list_messages.html.erb +19 -19
  22. data/app/views/active_scaffold_overrides/_list_messages_content.html.erb +5 -5
  23. data/app/views/active_scaffold_overrides/_list_pagination.html.erb +7 -5
  24. data/app/views/active_scaffold_overrides/_list_pagination_links.html.erb +5 -3
  25. data/app/views/active_scaffold_overrides/_list_record.html.erb +13 -8
  26. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +8 -8
  27. data/app/views/active_scaffold_overrides/_messages.html.erb +3 -3
  28. data/app/views/active_scaffold_overrides/_render_field.js.erb +8 -11
  29. data/app/views/active_scaffold_overrides/_search.html.erb +7 -6
  30. data/app/views/active_scaffold_overrides/_show.html.erb +4 -4
  31. data/app/views/active_scaffold_overrides/_show_actions.html.erb +2 -2
  32. data/app/views/active_scaffold_overrides/_show_columns.html.erb +28 -30
  33. data/app/views/active_scaffold_overrides/add_existing.js.erb +7 -4
  34. data/app/views/active_scaffold_overrides/render_field.js.erb +1 -1
  35. data/config/locales/de.yml +2 -1
  36. data/config/locales/en.yml +33 -32
  37. data/config/locales/es.yml +15 -14
  38. data/config/locales/fr.yml +3 -2
  39. data/config/locales/hu.yml +3 -2
  40. data/config/locales/ja.yml +2 -1
  41. data/config/locales/ro.yml +169 -0
  42. data/config/locales/ru.yml +8 -7
  43. data/config/locales/ta.yml +7 -6
  44. data/config/locales/zh_Hans.yml +14 -13
  45. data/lib/active_scaffold/actions/common_search.rb +1 -14
  46. data/lib/active_scaffold/actions/core.rb +55 -16
  47. data/lib/active_scaffold/actions/field_search.rb +2 -2
  48. data/lib/active_scaffold/actions/list.rb +35 -13
  49. data/lib/active_scaffold/actions/nested.rb +31 -7
  50. data/lib/active_scaffold/actions/show.rb +1 -1
  51. data/lib/active_scaffold/active_record_permissions.rb +1 -1
  52. data/lib/active_scaffold/attribute_params.rb +2 -1
  53. data/lib/active_scaffold/bridges/date_picker/helper.rb +14 -0
  54. data/lib/active_scaffold/bridges/date_picker.rb +10 -0
  55. data/lib/active_scaffold/config/list.rb +10 -1
  56. data/lib/active_scaffold/core.rb +9 -6
  57. data/lib/active_scaffold/data_structures/action_columns.rb +2 -0
  58. data/lib/active_scaffold/data_structures/column.rb +27 -6
  59. data/lib/active_scaffold/data_structures/filter.rb +4 -0
  60. data/lib/active_scaffold/data_structures/nested_info.rb +8 -1
  61. data/lib/active_scaffold/data_structures/sorting.rb +1 -1
  62. data/lib/active_scaffold/extensions/action_view_rendering.rb +1 -1
  63. data/lib/active_scaffold/finder.rb +66 -21
  64. data/lib/active_scaffold/helpers/action_link_helpers.rb +43 -18
  65. data/lib/active_scaffold/helpers/assets_helpers.rb +4 -4
  66. data/lib/active_scaffold/helpers/filter_helpers.rb +3 -3
  67. data/lib/active_scaffold/helpers/form_column_helpers.rb +34 -22
  68. data/lib/active_scaffold/helpers/form_ui_helpers.rb +15 -10
  69. data/lib/active_scaffold/helpers/framework_ui_helpers.rb +22 -0
  70. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -0
  71. data/lib/active_scaffold/helpers/list_column_helpers.rb +19 -0
  72. data/lib/active_scaffold/helpers/pagination_helpers.rb +28 -7
  73. data/lib/active_scaffold/helpers/search_column_helpers.rb +32 -5
  74. data/lib/active_scaffold/helpers/tabs_helpers.rb +3 -5
  75. data/lib/active_scaffold/helpers/view_helpers.rb +12 -0
  76. data/lib/active_scaffold/tableless.rb +11 -8
  77. data/lib/active_scaffold/version.rb +2 -2
  78. data/lib/active_scaffold.rb +169 -0
  79. data/lib/tasks/bundle.rake +2 -1
  80. data/lib/tasks/mock_app.rake +43 -0
  81. metadata +20 -7
  82. data/shoulda_macros/macros.rb +0 -145
@@ -431,6 +431,24 @@ module ActiveScaffold
431
431
  data
432
432
  end
433
433
 
434
+ def local_sorting?
435
+ @page&.pager&.number_of_pages == 1 && active_scaffold_config.list.local_sorting
436
+ end
437
+
438
+ def column_sort_value(record, column)
439
+ value = record.send(column.name)
440
+ return if value.nil?
441
+
442
+ case column.column.type
443
+ when :time, :datetime, Time then value.to_i
444
+ when :boolean then value ? 1 : 0
445
+ when :binary then nil
446
+ when :integer then value
447
+ when :float, :decimal then (value % 1).zero? ? value.to_i : value
448
+ else value.to_s
449
+ end
450
+ end
451
+
434
452
  # MARK
435
453
 
436
454
  def all_marked?
@@ -465,6 +483,7 @@ module ActiveScaffold
465
483
  elsif column.inplace_edit
466
484
  tag_options[:data] = inplace_edit_data(column)
467
485
  end
486
+
468
487
  content_tag(:th, column_heading_value(column, sorting, sort_direction) + inplace_edit_control(column), tag_options)
469
488
  end
470
489
 
@@ -3,8 +3,13 @@
3
3
  module ActiveScaffold
4
4
  module Helpers
5
5
  module PaginationHelpers
6
- def pagination_ajax_link(page_number, url_options, options)
7
- link_to page_number, url_options.merge(page: page_number), options.merge(class: 'as_paginate')
6
+ def pagination_ajax_link(page_number, url_options, options, text: nil)
7
+ attributes = as_element_attributes(
8
+ :pagination_link,
9
+ proc_options: {page_number: page_number, text: text},
10
+ **options.smart_merge(class: 'as_paginate')
11
+ )
12
+ link_to text ? as_(text) : page_number, url_options.merge(page: page_number), attributes
8
13
  end
9
14
 
10
15
  def pagination_url_options(url_options = nil)
@@ -17,6 +22,22 @@ module ActiveScaffold
17
22
  url_options
18
23
  end
19
24
 
25
+ def pagination_active_page(page_number, url_options, options)
26
+ attributes = options.slice(:id, :class).smart_merge(class: 'as_paginate current')
27
+ as_element :pagination_active_page, page_number,
28
+ proc_options: {page_number: page_number}, **attributes
29
+ end
30
+
31
+ def pagination_gap(options)
32
+ '..'
33
+ end
34
+
35
+ # Wraps the complete pagination sequence, including previous and next links.
36
+ # UI adapters may override this method and call +super+ to add nested markup.
37
+ def pagination_html(&)
38
+ capture(&)
39
+ end
40
+
20
41
  def pagination_ajax_links(current_page, url_options, options, inner_window, outer_window)
21
42
  start_number = current_page.number - inner_window
22
43
  end_number = current_page.number + inner_window
@@ -42,16 +63,16 @@ module ActiveScaffold
42
63
  page = current_page.number - offset
43
64
  next unless page < start_number && page > last_page
44
65
 
45
- html << '..' if page > last_page + 1
66
+ html << pagination_gap(options) if page > last_page + 1
46
67
  html << pagination_ajax_link(page, url_options, options)
47
68
  last_page = page
48
69
  end
49
70
  end
50
- html << '..' if start_number > last_page + 1
71
+ html << pagination_gap(options) if start_number > last_page + 1
51
72
 
52
73
  [start_number, last_page + 1].max.upto(end_number) do |num|
53
74
  html << if current_page.number == num
54
- content_tag(:span, num.to_s, class: 'as_paginate current')
75
+ pagination_active_page(num, url_options, options)
55
76
  else
56
77
  pagination_ajax_link(num, url_options, options)
57
78
  end
@@ -59,10 +80,10 @@ module ActiveScaffold
59
80
 
60
81
  if current_page.pager.infinite?
61
82
  offsets.each do |offset|
62
- html << '..' << pagination_ajax_link(current_page.number + offset, url_options, options)
83
+ html << pagination_gap(options) << pagination_ajax_link(current_page.number + offset, url_options, options)
63
84
  end
64
85
  else
65
- html << '..' unless end_number >= current_page.pager.last.number - outer_window - 1
86
+ html << pagination_gap(options) unless end_number >= current_page.pager.last.number - outer_window - 1
66
87
  [end_number + 1, current_page.pager.last.number - outer_window].max.upto(current_page.pager.last.number) do |num|
67
88
  html << pagination_ajax_link(num, url_options, options)
68
89
  end
@@ -50,14 +50,29 @@ module ActiveScaffold
50
50
 
51
51
  # the standard active scaffold options used for class, name and scope
52
52
  def active_scaffold_search_options(column)
53
- {name: "search[#{column.name}]", class: "#{column.name}-input", id: "search_#{column.name}", value: field_search_params[column.name.to_s]}
53
+ attributes = {
54
+ name: "search[#{column.name}]",
55
+ class: "#{column.name}-input",
56
+ id: "search_#{column.name}",
57
+ value: field_search_params[column.name.to_s]
58
+ }
59
+ as_element_attributes(:search_control, proc_options: {column: column, search_ui: column.search_ui}, **attributes)
54
60
  end
55
61
 
56
62
  def search_attribute(column, record)
57
63
  column_options = active_scaffold_search_options(column).merge(object: record)
58
- content_tag :dl do
59
- content_tag(:dt, label_tag(search_label_for(column, column_options), search_column_label(column, record))) <<
60
- content_tag(:dd, active_scaffold_search_for(column, column_options))
64
+ search_attribute_html(
65
+ column,
66
+ label_tag(search_label_for(column, column_options), search_column_label(column, record)),
67
+ active_scaffold_search_for(column, column_options)
68
+ )
69
+ end
70
+
71
+ def search_attribute_html(column, label, field)
72
+ context = {column: column}
73
+ as_element :search_attribute, proc_options: context do
74
+ as_element(:search_attribute_label, label, proc_options: context) <<
75
+ as_element(:search_attribute_field, field, proc_options: context)
61
76
  end
62
77
  end
63
78
 
@@ -156,7 +171,7 @@ module ActiveScaffold
156
171
  select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
157
172
  select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
158
173
 
159
- select_tag(options[:name], options_for_select(select_options, ActiveScaffold::Core.column_type_cast(options[:value], column.column)), id: options[:id])
174
+ select_tag(options[:name], options_for_select(select_options, ActiveScaffold::Core.column_type_cast(options[:value], column.column, column.model)), id: options[:id])
160
175
  end
161
176
  # we can't use checkbox ui because it's not possible to decide whether search for this field or not
162
177
  alias active_scaffold_search_checkbox active_scaffold_search_boolean
@@ -296,6 +311,7 @@ module ActiveScaffold
296
311
  def active_scaffold_search_timestamp(column, options, ui_options: column.options)
297
312
  active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :datetime)
298
313
  end
314
+ alias active_scaffold_search_timestamptz active_scaffold_search_timestamp
299
315
 
300
316
  def active_scaffold_search_time(column, options, ui_options: column.options)
301
317
  active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :time)
@@ -425,6 +441,17 @@ module ActiveScaffold
425
441
  [visibles, hiddens]
426
442
  end
427
443
 
444
+ def render_field_search_column(column, record)
445
+ column_css_class = column.css_class unless column.css_class.is_a?(Proc)
446
+ if column.respond_to? :each_column
447
+ as_element :field_search_subsection, class: column_css_class do
448
+ render_subsection column, record, nil, :field_search, partial: 'field_search_columns'
449
+ end
450
+ else
451
+ as_element :field_search_element, search_attribute(column, record), class: "form-element #{column_css_class}"
452
+ end
453
+ end
454
+
428
455
  def searched_by?(column)
429
456
  value = field_search_params[column.name.to_s]
430
457
  case value
@@ -10,7 +10,7 @@ module ActiveScaffold
10
10
  tab_options = send(override_helper_per_model(:active_scaffold_tab_options, record.class), column, record)
11
11
  used_tabs = send(override_helper_per_model(:active_scaffold_current_tabs, record.class), column, record, tab_options)
12
12
  input_helper = override_helper_per_model(:active_scaffold_input_for_tabbed, record.class)
13
- send(input_helper, column, record, subsection_id, tab_options, used_tabs.map(&:first)) <<
13
+ send(input_helper, column, record, subsection_id, tab_options, used_tabs.map { |v, _| v }) <<
14
14
  active_scaffold_refresh_link(nil, {'data-update_url' => url_for(add_tab_url)}, record, refresh_opts) <<
15
15
  active_scaffold_tabs_for(column, record, subsection_id, tab_options, used_tabs, &)
16
16
  end
@@ -32,8 +32,6 @@ module ActiveScaffold
32
32
  tab_values = record.send(col.name).map(&tabbed_by).compact
33
33
  if tabbed_by_association(col, tabbed_by)
34
34
  tab_values.map! { |value| [value, value.id.to_s] }
35
- else
36
- tab_values.map! { |value| [tab_options.find { |_, tab_value, _| value == tab_value }&.first || value, value] }
37
35
  end
38
36
  used_choices.merge tab_values
39
37
  end
@@ -41,7 +39,7 @@ module ActiveScaffold
41
39
  end
42
40
 
43
41
  def active_scaffold_tab_options(column, record)
44
- subform_column = column.first
42
+ subform_column = column.visible_columns.first
45
43
  if subform_column
46
44
  tabbed_by = subform_column.options[:tabbed_by] || column.tabbed_by
47
45
  if tabbed_by_association(subform_column, tabbed_by)
@@ -51,7 +49,7 @@ module ActiveScaffold
51
49
  end
52
50
  if tab_column
53
51
  label_method = (tab_column.form_ui_options || tab_column.options)[:label_method] || :to_label
54
- helper_method = association_helper_method(column.association, :sorted_association_options_find)
52
+ helper_method = association_helper_method(tab_column.association, :sorted_association_options_find)
55
53
  send(helper_method, tab_column.association, nil, subform_record).map do |opt_record|
56
54
  [opt_record.send(label_method), opt_record.id, opt_record]
57
55
  end
@@ -18,6 +18,7 @@ module ActiveScaffold
18
18
  include ActiveScaffold::Helpers::SearchColumnHelpers
19
19
  include ActiveScaffold::Helpers::HumanConditionHelpers
20
20
  include ActiveScaffold::Helpers::FilterHelpers
21
+ include ActiveScaffold::Helpers::FrameworkUiHelpers
21
22
 
22
23
  ##
23
24
  ## Delegates
@@ -31,6 +32,12 @@ module ActiveScaffold
31
32
  ## Uncategorized
32
33
  ##
33
34
 
35
+ # UI adapters may override this method and call +super+ to wrap the table,
36
+ # for example in a responsive-table container.
37
+ def list_table_html(&)
38
+ capture(&)
39
+ end
40
+
34
41
  def handle_exception_on_column(exception, column, record = nil)
35
42
  raise exception if exception.instance_variable_get(:@_active_scaffold_wrapped)
36
43
 
@@ -126,6 +133,7 @@ module ActiveScaffold
126
133
  def column_heading_class(column, sorting)
127
134
  classes = "#{column.name}-column_heading "
128
135
  classes << "sorted #{sorting.direction_of(column).downcase} " if sorting.sorts_on? column
136
+ classes << 'skip-local-sorting' if column.skip_local_sorting?
129
137
  classes << column.css_class unless column.css_class.nil? || column.css_class.is_a?(Proc)
130
138
  classes
131
139
  end
@@ -140,6 +148,10 @@ module ActiveScaffold
140
148
  params[:eid] ? {eid: id_from_controller(params[:eid])} : {}
141
149
  end
142
150
 
151
+ def server_error_msg(content = nil, **attributes, &)
152
+ as_element :server_error, content, **attributes, &
153
+ end
154
+
143
155
  def column_empty?(column_value)
144
156
  @_empty_values ||= ['&nbsp;', empty_field_text].compact
145
157
  empty = column_value.nil?
@@ -24,16 +24,19 @@ class ActiveScaffold::Tableless < ActiveRecord::Base
24
24
 
25
25
  class Column < ActiveRecord::ConnectionAdapters::Column
26
26
  def initialize(name, default, type, null = true, *, **) # rubocop:disable Style/OptionalBooleanParameter,Metrics/ParameterLists
27
- if Rails.version >= '8.1'
28
- if type.is_a?(Symbol)
29
- cast_type = ActiveRecord::Type.lookup(type)
30
- else
31
- metadata = ActiveRecord::Base.connection.send :fetch_type_metadata, type
32
- cast_type = ActiveRecord::Type.lookup(metadata.type)
33
- end
27
+ connection = ActiveRecord::Base.connection
28
+ sql_type = type.is_a?(Symbol) ? connection.type_to_sql(type) : type.to_s
29
+ cast_type = connection.send(:lookup_cast_type, sql_type)
30
+ metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(
31
+ sql_type: sql_type,
32
+ type: cast_type.type,
33
+ limit: cast_type.limit,
34
+ precision: cast_type.precision,
35
+ scale: cast_type.scale
36
+ )
37
+ if ActiveRecord.version >= Gem::Version.new('8.1')
34
38
  super(name, cast_type, default, metadata, null, *, **)
35
39
  else
36
- metadata = ActiveRecord::Base.connection.send :fetch_type_metadata, type.to_s
37
40
  super(name, default, metadata, null, *, **)
38
41
  end
39
42
  end
@@ -3,8 +3,8 @@
3
3
  module ActiveScaffold
4
4
  module Version
5
5
  MAJOR = 4
6
- MINOR = 3
7
- PATCH = 2
6
+ MINOR = 4
7
+ PATCH = 0
8
8
  FIX = nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, FIX].compact.join('.')
@@ -58,6 +58,174 @@ module ActiveScaffold
58
58
  mattr_accessor :javascripts, instance_writer: false
59
59
  self.javascripts = []
60
60
 
61
+ # A way to define the tags and html attributes used in different places.
62
+ # Changing the class or replacing the attributes may break the default AS stylesheets, but not JS or functionality.
63
+ #
64
+ # The keys in the hash are the names used by ActiveScaffold to identify the
65
+ # elements.
66
+ # The values are a hash of attributes used to create the markup, supporting the following keys:
67
+ # * The :tag key can be used to specify a different tag name for the element.
68
+ # * The :attributes key can be used to specify additional html attributes.
69
+ # * The :proc key can be used to specify a method to dinamically return the tag and attributes hash, as an array.
70
+ # When :proc is present, :tag and :attributes won't be used. The proc will receive an options hash, which usually
71
+ # will be a hash with html attributes, but in some cases may have other options:
72
+ # * For :action_link_group the hash will have keys such as :level, :first_action, and other options supported by
73
+ # +display_action_links+
74
+ # * For :action_link_separator the hash will have keys such as :level, :level_0_tag, and other options supported by
75
+ # +display_action_links+
76
+ # * Form and search elements receive the column and the configured UI in the options hash.
77
+ # * Action link elements receive the link, record, rendering options, and authorized state.
78
+ # * Pagination elements receive the page number and, for links, the optional text key.
79
+ # Some keys don't have a :tag key, because only attributes are used, the tag is hardcoded, usually elements that are
80
+ # rendered as a form or table, or a table's element (thead, tbody, tr, td, th, tfoot, etc.). For those elements,
81
+ # the :tag key will be ignored if it's set, so the tag can't be changed.
82
+ mattr_reader :ui_elements
83
+ @@ui_elements ||= {
84
+ list: {tag: :div},
85
+ list_header: {tag: :div},
86
+ list_title: {tag: :h2},
87
+ list_actions: {tag: :div},
88
+ filters: {tag: :div, attributes: {class: 'filters'}},
89
+ before_header_table: {tag: :table},
90
+ list_content: {tag: :div},
91
+ list_table: {}, # table
92
+ list_table_head: {}, # thead
93
+ list_table_head_row: {}, # tr
94
+ list_records: {}, # tbody
95
+ list_messages: {attributes: {class: 'messages'}}, # tbody
96
+ list_messages_container: {}, # td
97
+ list_action_messages: {tag: :div},
98
+ filtered_message: {tag: :div, attributes: {class: 'filtered-message'}},
99
+ applied_filters_message: {tag: :div, attributes: {class: 'filtered-message applied-filters'}},
100
+ empty_message: {tag: :p},
101
+ message_reset: {tag: :div, attributes: {class: 'reset'}},
102
+ message_close: {tag: :a, attributes: {href: '#', class: 'close'}},
103
+ server_error: {tag: :p, attributes: {class: 'error-message message server-error'}},
104
+ info_message: {tag: :div, attributes: {class: 'info-message message'}},
105
+ warning_message: {tag: :div, attributes: {class: 'warning-message message'}},
106
+ error_message: {tag: :div, attributes: {class: 'error-message message'}},
107
+ list_footer: {tag: :div, attributes: {class: 'active-scaffold-footer'}},
108
+ list_found: {tag: :div},
109
+ list_found_count: {tag: :span},
110
+ list_calculations: {}, # tr
111
+ pagination_links: {tag: :div},
112
+ pagination_link: {}, # a
113
+ pagination_active_page: {tag: :span},
114
+ record_actions_cell: {}, # td
115
+ record_action_links: {}, # table
116
+ action_link_group: {
117
+ proc: lambda do |options|
118
+ if options[:level]&.zero?
119
+ tag = :div
120
+ attributes = {class: 'action_group'}
121
+ else
122
+ tag = :li
123
+ attributes = {class: ('top' if options[:first_action]).to_s}
124
+ end
125
+ attributes[:class] += ' hover_click' if hover_via_click?
126
+ [tag, attributes]
127
+ end
128
+ },
129
+ action_link_separator: {
130
+ proc: lambda do |options|
131
+ tag = options[:level_0_tag] || :a if options[:level]&.zero?
132
+ [tag || :li, {class: 'separator'}]
133
+ end
134
+ },
135
+ action_link_group_title: {tag: :div},
136
+ action_link_group_content: {tag: :ul},
137
+ action_link: {}, # a
138
+ action_link_item: {tag: :li},
139
+ dynamic_action_group: {tag: :ul, attributes: {class: 'dynamic-menu'}},
140
+ dynamic_action_group_element: {tag: :li},
141
+ search_form: {}, # form
142
+ search_field: {attributes: {class: 'text-input', size: 50, autocomplete: :off}}, # input type=search
143
+ search_control: {}, # input, select or textarea
144
+ search_submit: {attributes: {class: 'submit'}}, # input type=submit
145
+ search_reset: {attributes: {class: 'reset'}}, # a
146
+ field_search_form: {}, # form
147
+ field_search_fields_columns: {tag: :div, attributes: {class: 'form-columns'}},
148
+ field_search_fields_container: {tag: :ol},
149
+ field_search_subsection: {tag: :li, attributes: {class: 'sub-section'}},
150
+ field_search_element: {tag: :li},
151
+ field_search_submit: {attributes: {class: 'submit'}}, # input type=submit
152
+ field_search_reset: {attributes: {class: 'reset'}}, # a
153
+ field_search_footer: {tag: :p},
154
+ form: {}, # form
155
+ form_title: {tag: :h4},
156
+ form_messages_container: {tag: :div},
157
+ form_fields_columns: {tag: :div, attributes: {class: 'form-columns'}},
158
+ fields_container: {tag: :ol},
159
+ form_subsection: {tag: :li, attributes: {class: 'sub-section'}},
160
+ form_subsection_header: {tag: :h5},
161
+ subform: {tag: :li},
162
+ subform_header: {tag: :h5},
163
+ subform_footer: {tag: :div, attributes: {class: 'footer'}},
164
+ subform_record_remove: {}, # a
165
+ subform_record_remove_reason: {tag: :span, attributes: {class: 'destroy reason'}},
166
+ subform_create_another: {}, # a
167
+ subform_replace_with_new: {}, # a
168
+ subform_add_existing: {}, # a
169
+ form_element: {tag: :li},
170
+ form_attribute: {tag: :dl},
171
+ form_attribute_label: {tag: :dt},
172
+ form_attribute_field: {tag: :dd},
173
+ search_attribute: {tag: :dl},
174
+ search_attribute_label: {tag: :dt},
175
+ search_attribute_field: {tag: :dd},
176
+ form_control: {}, # input, select or textarea
177
+ form_field_description: {tag: :span, attributes: {class: 'description'}},
178
+ form_field_description_close: {tag: :span, attributes: {class: 'close'}},
179
+ form_footer: {tag: :p},
180
+ form_submit: {attributes: {class: 'submit'}}, # input type=submit
181
+ form_apply: {attributes: {class: 'submit'}}, # input type=submit
182
+ form_cancel: {}, # a
183
+ field_add_new_subform: {},
184
+ field_add_new_popup: {},
185
+ show_title: {tag: :h4},
186
+ show_actions: {tag: :div},
187
+ show_columns_block: {tag: :dl},
188
+ show_label_block: {tag: :dt},
189
+ show_columns_group: {tag: :dd},
190
+ show_value_block: {tag: :dd},
191
+ show_footer: {tag: :p, attributes: {class: 'form-footer'}},
192
+ show_cancel: {} # a
193
+ }
194
+
195
+ def self.set_element_tag(name, tag)
196
+ (ui_elements[name] ||= {})[:tag] = tag
197
+ end
198
+
199
+ # Replace an element's complete definition. Use a block instead of +tag+ and
200
+ # +attributes+ to define a dynamic element.
201
+ def self.set_element(name, tag: nil, attributes: nil, &proc)
202
+ if proc && (tag || attributes)
203
+ raise ArgumentError, 'tag and attributes cannot be used with a proc'
204
+ end
205
+
206
+ ui_elements[name] = if proc
207
+ {proc: proc}
208
+ else
209
+ {}.tap do |element|
210
+ element[:tag] = tag if tag
211
+ element[:attributes] = attributes.as_html_attrs if attributes
212
+ end
213
+ end
214
+ end
215
+
216
+ def self.set_element_proc(name, &block)
217
+ (ui_elements[name] ||= {})[:proc] = block
218
+ end
219
+
220
+ def self.set_element_attributes(name, attributes)
221
+ (ui_elements[name] ||= {})[:attributes] = attributes.as_html_attrs
222
+ end
223
+
224
+ def self.add_element_attributes(name, attributes)
225
+ attrs = (ui_elements[name] ||= {})[:attributes] || {}
226
+ ui_elements[name][:attributes] = attrs.smart_merge(attributes)
227
+ end
228
+
61
229
  def self.threadsafe!; end
62
230
 
63
231
  mattr_writer :jquery_ui_loaded, instance_writer: false
@@ -106,3 +274,4 @@ require 'active_scaffold/engine'
106
274
  require 'ice_nine'
107
275
  require 'ice_nine/core_ext/object'
108
276
  require 'request_store'
277
+ require 'html_attrs'
@@ -16,7 +16,8 @@ namespace :bundle do
16
16
  gemfiles.each do |gemfile|
17
17
  puts "Locking #{File.basename(gemfile)}..."
18
18
  Bundler.with_unbundled_env do
19
- system("bundle lock --gemfile='#{gemfile}'")
19
+ command = ['bundle', 'lock', "--gemfile=#{gemfile}", '--add-platform', 'ruby']
20
+ system(*command, exception: true)
20
21
  end
21
22
  end
22
23
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :mock_app do
4
+ desc 'Build the mock app stylesheets'
5
+ task :assets do
6
+ environment = {
7
+ 'BUNDLE_GEMFILE' => File.expand_path(ENV.fetch('BUNDLE_GEMFILE', 'Gemfile'))
8
+ }
9
+ Dir.chdir('test/mock_app') do
10
+ system(environment, 'rails', 'dartsass:build', exception: true)
11
+ end
12
+ end
13
+
14
+ desc 'Start the mock_app server and Dart Sass watcher (default port 3000)'
15
+ task :server do
16
+ args = ARGV.drop_while { |a| a != '--' }.drop(1)
17
+ Dir.chdir('test/mock_app') do
18
+ environment = {'BUNDLE_GEMFILE' => File.expand_path('../../Gemfile', __dir__)}
19
+ system(environment, 'rails', 'dartsass:build', exception: true)
20
+ watcher = Process.spawn(environment, 'rails', 'dartsass:watch')
21
+ _pid, status = Process.wait2(Process.spawn(environment, 'rails', 'server', *args))
22
+ exit status.exitstatus
23
+ ensure
24
+ begin
25
+ if watcher
26
+ Process.kill('TERM', watcher)
27
+ Process.wait(watcher)
28
+ end
29
+ rescue Errno::ECHILD, Errno::ESRCH
30
+ # The watcher already exited.
31
+ end
32
+ end
33
+ end
34
+
35
+ desc 'Start the mock_app console'
36
+ task :console do
37
+ args = ARGV.drop_while { |a| a != '--' }.drop(1)
38
+ Dir.chdir('test/mock_app') do
39
+ exec({'BUNDLE_GEMFILE' => File.expand_path('../../Gemfile', __dir__)},
40
+ 'rails', 'console', *args)
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_scaffold
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.2
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Many, see README
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-08-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: html_attrs
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: ice_nine
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -190,6 +203,7 @@ files:
190
203
  - config/locales/fr.yml
191
204
  - config/locales/hu.yml
192
205
  - config/locales/ja.yml
206
+ - config/locales/ro.yml
193
207
  - config/locales/ru.yml
194
208
  - config/locales/ta.yml
195
209
  - config/locales/zh_Hans.yml
@@ -328,6 +342,7 @@ files:
328
342
  - lib/active_scaffold/helpers/filter_helpers.rb
329
343
  - lib/active_scaffold/helpers/form_column_helpers.rb
330
344
  - lib/active_scaffold/helpers/form_ui_helpers.rb
345
+ - lib/active_scaffold/helpers/framework_ui_helpers.rb
331
346
  - lib/active_scaffold/helpers/human_condition_helpers.rb
332
347
  - lib/active_scaffold/helpers/id_helpers.rb
333
348
  - lib/active_scaffold/helpers/list_column_helpers.rb
@@ -356,8 +371,8 @@ files:
356
371
  - lib/tasks/active_scaffold/assets.rake
357
372
  - lib/tasks/brakeman.rake
358
373
  - lib/tasks/bundle.rake
374
+ - lib/tasks/mock_app.rake
359
375
  - public/blank.html
360
- - shoulda_macros/macros.rb
361
376
  - vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png
362
377
  - vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png
363
378
  - vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png
@@ -383,7 +398,6 @@ licenses:
383
398
  - MIT
384
399
  metadata:
385
400
  rubygems_mfa_required: 'true'
386
- post_install_message:
387
401
  rdoc_options: []
388
402
  require_paths:
389
403
  - lib
@@ -398,8 +412,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
398
412
  - !ruby/object:Gem::Version
399
413
  version: '0'
400
414
  requirements: []
401
- rubygems_version: 3.5.11
402
- signing_key:
415
+ rubygems_version: 3.6.9
403
416
  specification_version: 4
404
417
  summary: Rails 4.x and 5.x versions of ActiveScaffold supporting prototype and jquery
405
418
  test_files: []