active_scaffold 4.1.6 → 4.2.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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +27 -0
  3. data/README.md +6 -5
  4. data/app/assets/javascripts/jquery/active_scaffold.js +98 -47
  5. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +15 -2
  6. data/app/assets/stylesheets/active_scaffold_images.scss +6 -0
  7. data/app/assets/stylesheets/{active_scaffold_layout.css → active_scaffold_layout.scss} +104 -4
  8. data/app/assets/stylesheets/tiny_mce_bridge.scss +11 -0
  9. data/app/views/active_scaffold_overrides/_base_form.html.erb +3 -2
  10. data/app/views/active_scaffold_overrides/_field_search.html.erb +2 -2
  11. data/app/views/active_scaffold_overrides/_form.html.erb +14 -4
  12. data/app/views/active_scaffold_overrides/_form_association.html.erb +1 -1
  13. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +5 -11
  14. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  15. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -3
  16. data/app/views/active_scaffold_overrides/_new_record.js.erb +3 -1
  17. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_render_field.js.erb +67 -36
  19. data/app/views/active_scaffold_overrides/_update_field_on_create.js.erb +41 -6
  20. data/app/views/active_scaffold_overrides/action_links_menu.js.erb +1 -0
  21. data/config/locales/de.yml +9 -0
  22. data/config/locales/en.yml +11 -0
  23. data/config/locales/es.yml +8 -0
  24. data/config/locales/fr.yml +8 -0
  25. data/config/locales/hu.yml +8 -0
  26. data/config/locales/ja.yml +8 -0
  27. data/config/locales/ru.yml +8 -0
  28. data/lib/active_scaffold/actions/common_search.rb +2 -0
  29. data/lib/active_scaffold/actions/core.rb +47 -23
  30. data/lib/active_scaffold/actions/create.rb +2 -0
  31. data/lib/active_scaffold/actions/delete.rb +6 -0
  32. data/lib/active_scaffold/actions/field_search.rb +36 -11
  33. data/lib/active_scaffold/actions/list.rb +26 -8
  34. data/lib/active_scaffold/actions/mark.rb +6 -0
  35. data/lib/active_scaffold/actions/nested.rb +2 -0
  36. data/lib/active_scaffold/actions/search.rb +7 -0
  37. data/lib/active_scaffold/actions/show.rb +6 -0
  38. data/lib/active_scaffold/actions/subform.rb +2 -0
  39. data/lib/active_scaffold/actions/update.rb +8 -1
  40. data/lib/active_scaffold/active_record_permissions.rb +3 -3
  41. data/lib/active_scaffold/attribute_params.rb +35 -17
  42. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +2 -0
  43. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +10 -9
  44. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +10 -3
  45. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +2 -0
  46. data/lib/active_scaffold/bridges/active_storage.rb +2 -0
  47. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -0
  48. data/lib/active_scaffold/bridges/ancestry.rb +2 -0
  49. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -0
  50. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +2 -0
  51. data/lib/active_scaffold/bridges/bitfields.rb +2 -0
  52. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -6
  53. data/lib/active_scaffold/bridges/cancan.rb +2 -0
  54. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -0
  55. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +2 -0
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +3 -1
  57. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -0
  58. data/lib/active_scaffold/bridges/carrierwave.rb +2 -0
  59. data/lib/active_scaffold/bridges/chosen/helpers.rb +13 -4
  60. data/lib/active_scaffold/bridges/chosen.rb +2 -0
  61. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -0
  62. data/lib/active_scaffold/bridges/country_select.rb +2 -0
  63. data/lib/active_scaffold/bridges/date_picker/ext.rb +6 -0
  64. data/lib/active_scaffold/bridges/date_picker/helper.rb +7 -3
  65. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  66. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +2 -0
  67. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +2 -0
  68. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -1
  69. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -0
  70. data/lib/active_scaffold/bridges/dragonfly.rb +2 -0
  71. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +2 -0
  72. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +10 -9
  73. data/lib/active_scaffold/bridges/file_column/form_ui.rb +2 -0
  74. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -0
  75. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +2 -0
  76. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +2 -0
  77. data/lib/active_scaffold/bridges/file_column.rb +2 -0
  78. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.rb +65 -0
  79. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.treetop +31 -0
  80. data/lib/active_scaffold/bridges/logical_query_parser.rb +9 -0
  81. data/lib/active_scaffold/bridges/paper_trail/actions.rb +2 -0
  82. data/lib/active_scaffold/bridges/paper_trail/config.rb +2 -0
  83. data/lib/active_scaffold/bridges/paper_trail/helper.rb +2 -0
  84. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +2 -0
  85. data/lib/active_scaffold/bridges/paper_trail.rb +2 -0
  86. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -1
  87. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +2 -0
  88. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +2 -0
  89. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +12 -12
  90. data/lib/active_scaffold/bridges/paperclip.rb +2 -0
  91. data/lib/active_scaffold/bridges/record_select/helpers.rb +19 -11
  92. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  93. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +2 -0
  94. data/lib/active_scaffold/bridges/semantic_attributes.rb +2 -0
  95. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  96. data/lib/active_scaffold/bridges/tiny_mce.rb +6 -0
  97. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +2 -0
  98. data/lib/active_scaffold/bridges/usa_state_select.rb +2 -0
  99. data/lib/active_scaffold/bridges.rb +2 -0
  100. data/lib/active_scaffold/config/base.rb +12 -7
  101. data/lib/active_scaffold/config/core.rb +26 -23
  102. data/lib/active_scaffold/config/create.rb +2 -0
  103. data/lib/active_scaffold/config/delete.rb +2 -0
  104. data/lib/active_scaffold/config/field_search.rb +2 -0
  105. data/lib/active_scaffold/config/form.rb +11 -1
  106. data/lib/active_scaffold/config/list.rb +7 -7
  107. data/lib/active_scaffold/config/mark.rb +2 -0
  108. data/lib/active_scaffold/config/nested.rb +28 -0
  109. data/lib/active_scaffold/config/search.rb +2 -0
  110. data/lib/active_scaffold/config/show.rb +2 -0
  111. data/lib/active_scaffold/config/subform.rb +2 -0
  112. data/lib/active_scaffold/config/update.rb +3 -1
  113. data/lib/active_scaffold/configurable.rb +4 -2
  114. data/lib/active_scaffold/constraints.rb +2 -0
  115. data/lib/active_scaffold/core.rb +14 -4
  116. data/lib/active_scaffold/data_structures/action_columns.rb +3 -1
  117. data/lib/active_scaffold/data_structures/action_link.rb +10 -0
  118. data/lib/active_scaffold/data_structures/action_link_separator.rb +2 -0
  119. data/lib/active_scaffold/data_structures/action_links.rb +32 -21
  120. data/lib/active_scaffold/data_structures/actions.rb +4 -2
  121. data/lib/active_scaffold/data_structures/association/abstract.rb +4 -2
  122. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +4 -2
  123. data/lib/active_scaffold/data_structures/association/active_record.rb +3 -9
  124. data/lib/active_scaffold/data_structures/association/mongoid.rb +4 -2
  125. data/lib/active_scaffold/data_structures/association.rb +2 -0
  126. data/lib/active_scaffold/data_structures/bridge.rb +3 -1
  127. data/lib/active_scaffold/data_structures/column.rb +37 -3
  128. data/lib/active_scaffold/data_structures/columns.rb +4 -2
  129. data/lib/active_scaffold/data_structures/filter.rb +3 -3
  130. data/lib/active_scaffold/data_structures/filter_option.rb +2 -0
  131. data/lib/active_scaffold/data_structures/filters.rb +3 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +4 -2
  133. data/lib/active_scaffold/data_structures/set.rb +8 -10
  134. data/lib/active_scaffold/data_structures/sorting.rb +5 -7
  135. data/lib/active_scaffold/engine.rb +3 -4
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +2 -0
  137. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +2 -0
  138. data/lib/active_scaffold/extensions/action_view_rendering.rb +2 -0
  139. data/lib/active_scaffold/extensions/connection_adapter.rb +2 -0
  140. data/lib/active_scaffold/extensions/ice_nine.rb +2 -0
  141. data/lib/active_scaffold/extensions/localize.rb +2 -0
  142. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +2 -0
  143. data/lib/active_scaffold/extensions/paginator_extensions.rb +3 -1
  144. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -0
  145. data/lib/active_scaffold/extensions/to_label.rb +2 -0
  146. data/lib/active_scaffold/extensions/unsaved_associated.rb +10 -8
  147. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -0
  148. data/lib/active_scaffold/finder.rb +57 -18
  149. data/lib/active_scaffold/helpers/action_link_helpers.rb +112 -37
  150. data/lib/active_scaffold/helpers/association_helpers.rb +4 -2
  151. data/lib/active_scaffold/helpers/controller_helpers.rb +2 -0
  152. data/lib/active_scaffold/helpers/filter_helpers.rb +11 -3
  153. data/lib/active_scaffold/helpers/form_column_helpers.rb +98 -71
  154. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -0
  155. data/lib/active_scaffold/helpers/id_helpers.rb +2 -0
  156. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -5
  157. data/lib/active_scaffold/helpers/pagination_helpers.rb +3 -1
  158. data/lib/active_scaffold/helpers/search_column_helpers.rb +19 -9
  159. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
  160. data/lib/active_scaffold/helpers/tabs_helpers.rb +5 -3
  161. data/lib/active_scaffold/helpers/view_helpers.rb +3 -3
  162. data/lib/active_scaffold/marked_model.rb +6 -5
  163. data/lib/active_scaffold/orm_checks.rb +2 -0
  164. data/lib/active_scaffold/paginator.rb +4 -1
  165. data/lib/active_scaffold/registry.rb +2 -0
  166. data/lib/active_scaffold/responds_to_parent.rb +2 -0
  167. data/lib/active_scaffold/tableless.rb +23 -13
  168. data/lib/active_scaffold/version.rb +4 -2
  169. data/lib/active_scaffold.rb +10 -2
  170. data/lib/generators/active_scaffold/controller/USAGE +19 -0
  171. data/lib/generators/active_scaffold/controller/controller_generator.rb +29 -0
  172. data/lib/generators/active_scaffold/install/USAGE +2 -0
  173. data/lib/generators/active_scaffold/{install_generator.rb → install/install_generator.rb} +10 -6
  174. data/lib/generators/active_scaffold/resource/USAGE +29 -0
  175. data/lib/generators/active_scaffold/resource/resource_generator.rb +30 -0
  176. data/lib/tasks/brakeman.rake +2 -0
  177. data/shoulda_macros/macros.rb +2 -0
  178. metadata +19 -11
  179. data/lib/generators/active_scaffold/controller_generator.rb +0 -49
  180. data/lib/generators/active_scaffold/resource_generator.rb +0 -56
  181. /data/lib/generators/{templates → active_scaffold/controller/templates}/controller.rb +0 -0
  182. /data/lib/generators/{templates → active_scaffold/controller/templates}/helper.rb +0 -0
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module FieldSearch
3
5
  def self.included(base)
6
+ conf = base.active_scaffold_config
7
+ if conf.model.primary_key.nil? && conf.field_search.columns.any? { |col| conf.columns[col]&.association }
8
+ raise "#{base.active_scaffold_config.model.name} has no primary key, field_search with association columns won't work"
9
+ end
10
+
4
11
  base.class_eval do
5
12
  helper_method :field_search_params, :grouped_search?, :search_group_column, :search_group_function
6
13
  include ActiveScaffold::Actions::CommonSearch
@@ -124,7 +131,7 @@ module ActiveScaffold::Actions
124
131
  when 'year_quarter'
125
132
  sql_operator(sql_operator(extract_sql_fn('year', group_sql), '*', 10), '+', extract_sql_fn('quarter', group_sql))
126
133
  else
127
- raise "#{group_function} unsupported, override calculation_for_group_by in #{self.class.name}"
134
+ raise ArgumentError, "#{group_function} unsupported, override calculation_for_group_by in #{self.class.name}"
128
135
  end
129
136
  end
130
137
 
@@ -184,27 +191,45 @@ module ActiveScaffold::Actions
184
191
  end
185
192
 
186
193
  def do_field_search
187
- filtered_columns = []
188
194
  text_search = active_scaffold_config.field_search.text_search
189
195
  columns = active_scaffold_config.field_search.columns
190
- search_params.each do |key, value|
196
+
197
+ search_conditions = search_params.filter_map do |key, value|
191
198
  next unless columns.include? key
192
199
 
193
200
  column = active_scaffold_config.columns[key]
194
201
  search_condition = self.class.condition_for_column(column, value, text_search, session)
195
- next if search_condition.blank?
196
-
197
- active_scaffold_conditions << search_condition
198
- filtered_columns << column
199
- end
200
- setup_joins_for_filtered_columns(filtered_columns)
201
- if filtered_columns.present? || grouped_search?
202
- @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
202
+ [column, search_condition] unless search_condition.nil?
203
203
  end
204
204
 
205
+ process_search_conditions(search_conditions)
205
206
  active_scaffold_config.list.user.page = nil
206
207
  end
207
208
 
209
+ def process_search_conditions(search_conditions)
210
+ filtered_columns = []
211
+ filtered_columns_for_joins = []
212
+ search_conditions.each do |column, search_condition|
213
+ if search_condition.is_a? ActiveRecord::Relation
214
+ active_scaffold_relations << search_condition
215
+ filtered_columns << column
216
+ else
217
+ active_scaffold_conditions << search_condition
218
+ filtered_columns << column
219
+ filtered_columns_for_joins << column
220
+ end
221
+ end
222
+
223
+ setup_joins_for_filtered_columns(filtered_columns_for_joins)
224
+ setup_human_conditions(filtered_columns)
225
+ end
226
+
227
+ def setup_human_conditions(filtered_columns)
228
+ return unless filtered_columns.present? || grouped_search?
229
+
230
+ @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
231
+ end
232
+
208
233
  def field_search_ignore?
209
234
  active_scaffold_config.list.always_show_search && active_scaffold_config.list.search_partial == 'field_search'
210
235
  end
@@ -1,12 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module List
3
5
  def self.included(base)
4
6
  base.before_action :list_authorized_filter, only: :index
5
- base.helper_method :list_columns, :count_on_association_class?
7
+ base.helper_method :list_columns, :count_on_association_class?, :filters_enabled?
6
8
  end
7
9
 
8
10
  def index
9
- if params[:id] && !params[:id].is_a?(Array) && request.xhr?
11
+ if params[:action_links] && request.xhr?
12
+ action_links_menu
13
+ elsif params[:id] && !params[:id].is_a?(Array) && request.xhr?
10
14
  row
11
15
  else
12
16
  list
@@ -15,6 +19,18 @@ module ActiveScaffold::Actions
15
19
 
16
20
  protected
17
21
 
22
+ def action_links_menu
23
+ @record = find_if_allowed(params[:id], :read) if params[:id]
24
+ @action_links = params[:action_links].split('.').reduce(active_scaffold_config.action_links) do |links, submenu|
25
+ links.subgroup(submenu)
26
+ end
27
+ respond_to_action(:action_links_menu, action_links_menu_formats)
28
+ end
29
+
30
+ def action_links_menu_formats
31
+ %i[js]
32
+ end
33
+
18
34
  # get just a single row
19
35
  def row
20
36
  get_row
@@ -64,15 +80,17 @@ module ActiveScaffold::Actions
64
80
  render action: 'row'
65
81
  end
66
82
 
83
+ def action_links_menu_respond_to_js
84
+ render action: 'action_links_menu'
85
+ end
86
+
67
87
  # The actual algorithm to prepare for the list view
68
88
  def set_includes_for_columns(action = :list, sorting = active_scaffold_config.list.user.sorting)
69
89
  @cache_associations = true
70
90
  columns = columns_for_action(action)
71
- joins_cols, preload_cols = columns.select { |c| c.includes.present? }.partition do |col|
72
- includes_need_join?(col, sorting) && !grouped_search?
73
- end
74
- active_scaffold_references.concat joins_cols.map(&:includes).flatten.uniq
75
- active_scaffold_preload.concat preload_cols.map(&:includes).flatten.uniq
91
+ joins_cols = columns.select { |col| col.sort_joins.present? && includes_need_join?(col, sorting) && !grouped_search? }
92
+ active_scaffold_references.concat joins_cols.map(&:sort_joins).flatten.uniq
93
+ active_scaffold_preload.concat columns.filter_map { |c| c.includes.presence }.flatten.uniq
76
94
  set_includes_for_sorting(columns, sorting) if sorting.sorts_by_sql?
77
95
  end
78
96
 
@@ -286,7 +304,7 @@ module ActiveScaffold::Actions
286
304
  do_search if respond_to? :do_search, true
287
305
  set_includes_for_columns
288
306
  # where(nil) is needed because we need a relation
289
- append_to_query(beginning_of_chain.where(nil), finder_options)
307
+ append_to_query(filtered_query.where(nil), finder_options)
290
308
  end
291
309
  end
292
310
 
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module Mark
3
5
  def self.included(base)
6
+ if base.active_scaffold_config.model.primary_key.nil?
7
+ raise "#{base.active_scaffold_config.model.name} has no primary key, mark won't work"
8
+ end
9
+
4
10
  base.before_action :mark_authorized_filter, only: :mark
5
11
  base.before_action :assign_marked_records_to_model
6
12
  base.helper_method :marked_records
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  # The Nested module basically handles automatically linking controllers together.
3
5
  # It does this by creating column links with the right parameters, and by providing
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module Search
3
5
  def self.included(base)
6
+ conf = base.active_scaffold_config
7
+ if conf.model.primary_key.nil? && conf.search.columns.any? { |col| conf.columns[col]&.association }
8
+ raise "#{base.active_scaffold_config.model.name} has no primary key, search with association columns won't work"
9
+ end
10
+
4
11
  base.send :include, ActiveScaffold::Actions::CommonSearch
5
12
  base.send :include, InstanceMethods
6
13
  end
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module Show
3
5
  def self.included(base)
6
+ if base.active_scaffold_config.model.primary_key.nil?
7
+ raise "#{base.active_scaffold_config.model.name} has no primary key, show won't work"
8
+ end
9
+
4
10
  base.before_action :show_authorized_filter, only: :show
5
11
  end
6
12
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module Subform
3
5
  def edit_associated
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Actions
2
4
  module Update
3
5
  def self.included(base)
6
+ if base.active_scaffold_config.model.primary_key.nil?
7
+ raise "#{base.active_scaffold_config.model.name} has no primary key, update won't work"
8
+ end
9
+
4
10
  base.before_action :update_authorized_filter, only: %i[edit update]
5
11
  base.helper_method :update_refresh_list?
6
12
  end
@@ -97,7 +103,8 @@ module ActiveScaffold::Actions
97
103
  # A simple method to find and prepare a record for editing
98
104
  # May be overridden to customize the record (set default values, etc.)
99
105
  def do_edit
100
- @record = find_if_allowed(params[:id], :update)
106
+ preload_values = preload_for_form(active_scaffold_config.update.columns)
107
+ @record = find_if_allowed(params[:id], :update, filtered_query.preload(preload_values))
101
108
  end
102
109
 
103
110
  # A complex method to update a record. The complexity comes from the support for subforms,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This module attempts to create permissions conventions for your ActiveRecord models. It supports english-based
2
4
  # methods that let you restrict access per-model, per-record, per-column, per-action, and per-user. All at once.
3
5
  #
@@ -55,9 +57,7 @@ module ActiveScaffold
55
57
  end
56
58
 
57
59
  # Instance-level access to the current user
58
- def current_user
59
- self.class.current_user
60
- end
60
+ delegate :current_user, to: :class
61
61
  end
62
62
  end
63
63
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  # Provides support for param hashes assumed to be model attributes.
3
5
  # Support is primarily needed for creating/editing associated records using a nested hash structure.
@@ -80,8 +82,8 @@ module ActiveScaffold
80
82
  rescue StandardError => e
81
83
  message = "on the ActiveScaffold column = :#{column.name} for #{parent_record.inspect} " \
82
84
  "(value from params #{attributes[column.name].inspect})"
83
- Rails.logger.error "#{e.class.name}: #{e.message} -- #{message}"
84
- raise e.class, "#{e.message} -- #{message}"
85
+ ActiveScaffold.log_exception(e, message)
86
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
85
87
  end
86
88
  end
87
89
 
@@ -156,13 +158,9 @@ module ActiveScaffold
156
158
 
157
159
  def association_value_from_param_simple_value(parent_record, column, value)
158
160
  if column.association.singular?
159
- # value may be Array if using update_columns in field_search with multi-select
160
- klass = column.association.klass(parent_record)
161
- # find_by needed when using update_columns in type foreign type key of polymorphic association,
162
- # and foreign key had value, it will try to find record with id of previous type
163
- klass&.find_by(klass&.primary_key => value) if value.present? && !value.is_a?(Array)
161
+ column_singular_assocation_value_from_value(parent_record, column, value)
164
162
  else # column.association.collection?
165
- column_plural_assocation_value_from_value(column, Array(value))
163
+ column_plural_assocation_value_from_value(parent_record, column, Array(value))
166
164
  end
167
165
  end
168
166
 
@@ -180,7 +178,21 @@ module ActiveScaffold
180
178
  end
181
179
  end
182
180
 
183
- def column_plural_assocation_value_from_value(column, value)
181
+ def column_singular_assocation_value_from_value(parent_record, column, value)
182
+ # value may be Array if using update_columns in field_search with multi-select
183
+ return if value.blank? || value.is_a?(Array)
184
+
185
+ if parent_record.association_cached?(column.name) && parent_record.send(column.name)&.id.to_s == value
186
+ parent_record.send(column.name)
187
+ else
188
+ klass = column.association.klass(parent_record)
189
+ # find_by needed when using update_columns in type foreign type key of polymorphic association,
190
+ # and foreign key had value, it will try to find record with id of previous type
191
+ klass&.find_by(klass.primary_key => value)
192
+ end
193
+ end
194
+
195
+ def column_plural_assocation_value_from_value(parent_record, column, value)
184
196
  # it's an array of ids
185
197
  if value.present?
186
198
  ids = value.compact_blank
@@ -213,9 +225,9 @@ module ActiveScaffold
213
225
  end
214
226
 
215
227
  def manage_nested_record_from_params(parent_record, column, attributes, avoid_changes = false)
216
- return nil unless build_record_from_params?(attributes, column, parent_record)
228
+ return nil unless avoid_changes || build_record_from_params?(attributes, column, parent_record)
217
229
 
218
- record = find_or_create_for_params(attributes, column, parent_record)
230
+ record = find_or_create_for_params(attributes, column, parent_record, avoid_changes)
219
231
  if record
220
232
  record_columns = active_scaffold_config_for(record.class).subform.columns
221
233
  prev_constraints = record_columns.constraint_columns
@@ -238,11 +250,11 @@ module ActiveScaffold
238
250
  # Attempts to create or find an instance of the klass of the association in parent_column from the
239
251
  # request parameters given. If params[primary_key] exists it will attempt to find an existing object
240
252
  # otherwise it will build a new one.
241
- def find_or_create_for_params(params, parent_column, parent_record)
253
+ def find_or_create_for_params(params, parent_column, parent_record, avoid_changes = false)
242
254
  current = parent_record.send(parent_column.name)
243
255
  klass = parent_column.association.klass(parent_record)
244
256
  if params.key? klass.primary_key
245
- record_from_current_or_find(klass, params[klass.primary_key], current)
257
+ record_from_current_or_find(klass, params[klass.primary_key], current, avoid_changes)
246
258
  elsif klass.authorized_for?(crud_type: :create)
247
259
  association = parent_column.association
248
260
  record = klass.new
@@ -255,15 +267,21 @@ module ActiveScaffold
255
267
 
256
268
  # Attempts to find an instance of klass (which must be an ActiveRecord object) with id primary key
257
269
  # Returns record from current if it's included or find from DB
258
- def record_from_current_or_find(klass, id, current)
270
+ def record_from_current_or_find(klass, id, current, avoid_changes = false)
271
+ record = record_from_current(current, id)
272
+ record ||= klass.new(klass.primary_key => id) if avoid_changes
273
+ record ||= klass.find(id)
274
+ record = copy_attributes(record) if avoid_changes && record.persisted?
275
+ record
276
+ end
277
+
278
+ def record_from_current(current, id)
259
279
  if current.is_a?(ActiveRecord::Base) && current.id.to_s == id
260
280
  # modifying the current object of a singular association
261
281
  current
262
- elsif current.respond_to?(:any?) && current.any? { |o| o.id.to_s == id }
282
+ elsif current.respond_to?(:any?)
263
283
  # modifying one of the current objects in a plural association
264
284
  current.detect { |o| o.id.to_s == id }
265
- else # attaching an existing but not-current object
266
- klass.find(id)
267
285
  end
268
286
  end
269
287
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class ActiveStorage
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class ActiveStorage
@@ -23,17 +25,16 @@ module ActiveScaffold
23
25
 
24
26
  def generate_delete_helpers(klass)
25
27
  (active_storage_has_one_fields(klass) | active_storage_has_many_fields(klass)).each do |field|
26
- klass.send :class_eval, <<-CODE, __FILE__, __LINE__ + 1 unless klass.method_defined?(:"#{field}_with_delete=")
27
- attr_reader :delete_#{field}
28
+ next if klass.method_defined?(:"#{field}_with_delete=")
28
29
 
29
- def delete_#{field}=(value)
30
- value = (value=="true") if String===value
31
- return unless value
30
+ klass.attr_reader :"delete_#{field}"
31
+ klass.define_method "delete_#{field}=" do |value|
32
+ value = (value == 'true') if value.is_a?(String)
33
+ return unless value
32
34
 
33
- # passing nil to the file column causes the file to be deleted.
34
- self.#{field}.purge
35
- end
36
- CODE
35
+ # passing nil to the file column causes the file to be deleted.
36
+ send(field).purge
37
+ end
37
38
  end
38
39
  end
39
40
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with the rendering of a Form Column
@@ -6,16 +8,21 @@ module ActiveScaffold
6
8
  record = options[:object]
7
9
  active_storage = record.send(column.name.to_s)
8
10
  content = active_scaffold_column_active_storage_has_one(record, column, ui_options: ui_options) if active_storage.attached?
9
- active_scaffold_file_with_remove_link(column, options, content, 'delete_', 'active_storage_controls', ui_options: ui_options)
11
+ active_scaffold_file_with_content(column, options, content, 'delete_', 'active_storage_controls', ui_options: ui_options)
10
12
  end
11
13
 
12
14
  def active_scaffold_input_active_storage_has_many(column, options, ui_options: column.options)
13
15
  record = options[:object]
14
16
  options[:multiple] = 'multiple'
17
+ options[:include_hidden] = false
15
18
  options[:name] += '[]'
16
19
  active_storage = record.send(column.name.to_s)
17
- content = active_scaffold_column_active_storage_has_many(record, column, ui_options: ui_options) if active_storage.attached?
18
- active_scaffold_file_with_remove_link(column, options, content, 'delete_', 'active_storage_controls', ui_options: ui_options)
20
+ existing = active_storage.map do |file|
21
+ content = hidden_field_tag(options[:name], file.signed_id) <<
22
+ link_for_attachment(file, column, ui_options: ui_options) << h(' | ')
23
+ active_scaffold_file_with_remove_link(content, options, 'active_storage_controls', :remove)
24
+ end
25
+ safe_join existing << hidden_field_tag(options[:name]) << file_field(:record, column.name, options)
19
26
  end
20
27
  end
21
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  module ListColumnHelpers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::ActiveStorage < ActiveScaffold::DataStructures::Bridge
2
4
  cattr_accessor :thumbnail_variant
3
5
  self.thumbnail_variant = {resize_to_limit: [nil, 30]}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Bridges
2
4
  class Ancestry
3
5
  module AncestryBridge
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::Ancestry < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  require File.join(File.dirname(__FILE__), 'ancestry/ancestry_bridge.rb')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class Bitfields
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class Bitfields
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::Bitfields < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  Dir[File.join(__dir__, 'bitfields', '*.rb')].each { |file| require file }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Allow users to easily define aliases for AS actions.
2
4
  # Ability#as_action_aliases should be called by the user in his ability class
3
5
  #
@@ -58,6 +60,7 @@ module ActiveScaffold::Bridges
58
60
  module ModelUserAccess
59
61
  module Controller
60
62
  extend ActiveSupport::Concern
63
+
61
64
  included do
62
65
  prepend_before_action :assign_current_ability_to_models
63
66
  end
@@ -81,17 +84,17 @@ module ActiveScaffold::Bridges
81
84
  end
82
85
 
83
86
  # Instance-level access to the current ability
84
- def current_ability
85
- self.class.current_ability
86
- end
87
+ delegate :current_ability, to: :class
87
88
  end
88
89
  end
89
90
 
90
91
  # plug into AS#authorized_for calls
91
92
  module ActiveRecord
92
93
  extend ActiveSupport::Concern
94
+
93
95
  included do
94
96
  prepend SecurityMethods
97
+
95
98
  class << self
96
99
  prepend SecurityMethods
97
100
  end
@@ -105,12 +108,12 @@ module ActiveScaffold::Bridges
105
108
  # {action: 'edit'}
106
109
  # to allow access cancan must allow both :crud_type and :action
107
110
  # if cancan says "no", it delegates to default AS behavior
108
- def authorized_for?(options = {})
111
+ def authorized_for?(options = {}) # rubocop:disable Naming/PredicateMethod
109
112
  raise InvalidArgument if options[:crud_type].blank? && options[:action].blank?
110
113
 
111
114
  if current_ability.present?
112
- crud_type_result = options[:crud_type].nil? ? true : current_ability.can?(options[:crud_type], self)
113
- action_result = options[:action].nil? ? true : current_ability.can?(options[:action].to_sym, self)
115
+ crud_type_result = options[:crud_type].nil? || current_ability.can?(options[:crud_type], self)
116
+ action_result = options[:action].nil? || current_ability.can?(options[:action].to_sym, self)
114
117
  else
115
118
  crud_type_result = action_result = false
116
119
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::Cancan < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  require File.join(File.dirname(__FILE__), 'cancan', 'cancan_bridge.rb')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class Carrierwave
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Bridges
3
5
  class Carrierwave
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  module FormColumnHelpers
@@ -5,7 +7,7 @@ module ActiveScaffold
5
7
  record = options[:object]
6
8
  carrierwave = record.send(column.name.to_s)
7
9
  content = get_column_value(record, column) if carrierwave.file.present?
8
- active_scaffold_file_with_remove_link(column, options, content, 'remove_', 'carrierwave_controls', ui_options: ui_options) do
10
+ active_scaffold_file_with_content(column, options, content, 'remove_', 'carrierwave_controls', ui_options: ui_options) do
9
11
  cache_field_options = {
10
12
  name: options[:name].gsub(/\[#{column.name}\]$/, "[#{column.name}_cache]"),
11
13
  id: "#{options[:id]}_cache"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  module ListColumnHelpers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::Carrierwave < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  require File.join(File.dirname(__FILE__), 'carrierwave/form_ui')
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  class ActiveScaffold::Bridges::Chosen
4
6
  module Helpers
5
7
  extend ActiveSupport::Concern
8
+
6
9
  included do
7
10
  include FormColumnHelpers
8
11
  include SearchColumnHelpers
@@ -21,11 +24,17 @@ class ActiveScaffold::Bridges::Chosen
21
24
  options.update(ui_options)
22
25
  active_scaffold_select_name_with_multiple html_options
23
26
 
24
- if (optgroup = options.delete(:optgroup))
25
- select(:record, column.name, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
26
- else
27
- collection_select(:record, column.name, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
27
+ html =
28
+ if (optgroup = options.delete(:optgroup))
29
+ select(:record, column.name, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
30
+ else
31
+ collection_select(:record, column.name, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
32
+ end
33
+ if ui_options[:add_new]
34
+ html = content_tag(:div, html, class: 'select-field') <<
35
+ active_scaffold_add_new(column, record, html_options, ui_options: ui_options)
28
36
  end
37
+ html
29
38
  else
30
39
  active_scaffold_input_select(column, html_options, ui_options: ui_options)
31
40
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::Chosen < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  require File.join(File.dirname(__FILE__), 'chosen/helpers.rb')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::Bridges
2
4
  class CountrySelect
3
5
  module FormColumnHelpers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveScaffold::Bridges::CountrySelect < ActiveScaffold::DataStructures::Bridge
2
4
  def self.install
3
5
  require File.join(File.dirname(__FILE__), 'country_select/country_select_bridge_helper.rb')