Skip to content

Commit 79b6314

Browse files
committed
Add subform_includes to column, and preload_for_form controller method
So associations can be preloaded on edit action, speed up on big forms with long subforms or many nested subforms
1 parent c835604 commit 79b6314

File tree

5 files changed

+52
-8
lines changed

5 files changed

+52
-8
lines changed

CHANGELOG.rdoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
- Integrate LogicalQueryParser, supporting full logical search, or simplified searches with 'all keywords' and 'any keyword' operators
22
- Display unauthorized column in form as show action, so show_ui or show override is used if exists
33
- Display unauthorized subform with show_ui as show action, to display readonly subform tables
4+
- Add subform_includes to column, and preload_for_form method to preload associations on edit action, speed up on big forms with long subforms or many nested subforms
45

56
= 4.1.1 (not released yet)
67
- Load dartsass-sprockets, so it isn't required in the Gemfile

lib/active_scaffold/actions/core.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,8 @@ def parent_controller_name
148148

149149
def setup_parent
150150
cfg = main_form_controller.active_scaffold_config
151-
parent_model = cfg.model
152-
parent = parent_model.new
153-
copy_attributes(find_if_allowed(params[:parent_id], :read, parent_model), parent) if params[:parent_id]
154-
parent.id = params[:parent_id]
151+
parent = cfg.model.new
152+
copy_data_from_saved_record(params[:parent_id], cfg, parent) if params[:parent_id]
155153
apply_constraints_to_record(parent) unless params[:parent_id]
156154
if @column.send_form_on_update_column
157155
parent = update_record_from_params(parent, cfg.send(params[:parent_id] ? :update : :create).columns, params[:record], true)
@@ -166,6 +164,12 @@ def setup_parent
166164
parent
167165
end
168166

167+
def copy_data_from_saved_record(id, config = active_scaffold_config, record = nil)
168+
preload_values = preload_for_form(config.update.columns) if config.actions.include?(:update)
169+
saved_record = find_if_allowed(id, :read, config.model.preload(preload_values))
170+
copy_attributes(saved_record, record).tap { |new_record| new_record.id = id }
171+
end
172+
169173
def find_from_scope(parent, scope)
170174
parts = scope[1..-2].split('][')
171175
record = parent
@@ -190,6 +194,9 @@ def find_from_scope(parent, scope)
190194
def copy_attributes(orig, dst = nil)
191195
dst ||= orig.class.new
192196
orig.attributes.each { |attr, value| dst.send :write_attribute, attr, value }
197+
orig.class.reflect_on_all_associations.each do |assoc|
198+
dst.association(assoc.name).target = orig.association(assoc.name).target if orig.send(:association_cached?, assoc.name)
199+
end
193200
dst
194201
end
195202

@@ -203,6 +210,27 @@ def parent_sti_controller
203210
@parent_sti_controller
204211
end
205212

213+
def preload_for_form(columns, preloaded_models = [])
214+
association_columns = []
215+
columns.each_column(flatten: true, skip_authorization: true) do |column|
216+
association_columns << column if column.association && column.subform_includes
217+
end
218+
219+
association_columns.filter_map do |column|
220+
if column.form_ui.nil? && column.subform_includes == true && preloaded_models.exclude?(column.association.klass)
221+
config = active_scaffold_config_for(column.association.klass)
222+
if config.actions.include?(:subform)
223+
preload_values = preload_for_form(config.subform.columns, preloaded_models + [column.association.klass])
224+
end
225+
preload_values ? {column.name => preload_values} : column.name
226+
elsif column.subform_includes != true
227+
{column.name => column.subform_includes}
228+
else
229+
column.name
230+
end
231+
end
232+
end
233+
206234
# override this method if you want to do something after render_field
207235
def after_render_field(record, column); end
208236

lib/active_scaffold/actions/update.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ def update_columns_names
9797
# A simple method to find and prepare a record for editing
9898
# May be overridden to customize the record (set default values, etc.)
9999
def do_edit
100-
@record = find_if_allowed(params[:id], :update)
100+
preload_values = preload_for_form(active_scaffold_config.update.columns)
101+
@record = find_if_allowed(params[:id], :update, filtered_query.preload(preload_values))
101102
end
102103

103104
# A complex method to update a record. The complexity comes from the support for subforms,

lib/active_scaffold/data_structures/action_columns.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def each_column(options = {}, &proc)
8686
yield item
8787
end
8888
else
89-
next if skip_column?(item, options)
89+
next if !options[:skip_authorization] && skip_column?(item, options)
9090

9191
yield columns[item] || ActiveScaffold::DataStructures::Column.new(item.to_sym, columns.active_record_class)
9292
end

lib/active_scaffold/data_structures/column.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ module ProxyableMethods
9292
# a collection of associations to pre-load when finding the records on a page
9393
attr_reader :includes
9494

95+
# a collection of associations to pre-load when the column is used as a subform,
96+
# defaults to true, which means get associations from subform columns in the associated controller
97+
# set to any other value to avoid checking the associated controller, false or nil to prevent preloading
98+
attr_reader :subform_includes
99+
95100
# a place to store dev's column specific options
96101
attr_writer :options
97102

@@ -286,6 +291,14 @@ def includes=(value)
286291
end
287292
end
288293

294+
def subform_includes=(value)
295+
@subform_includes =
296+
case value
297+
when Array, TrueClass then value
298+
else value ? [value] : value # not convert nil to [nil]
299+
end
300+
end
301+
289302
# a collection of associations to do left join when this column is included on search
290303
def search_joins
291304
@search_joins || includes
@@ -515,12 +528,13 @@ def initialize(name, active_record_class, delegated_association = nil) # :nodoc:
515528
if delegated_association
516529
self.includes = includes ? [delegated_association.name => includes] : [delegated_association.name]
517530
end
531+
self.subform_includes = true if association
518532

519533
# default all the configurable variables
520534
self.css_class = ''
521535
validators_force_require_on = active_record_class.validators_on(name)
522-
.map { |val| validator_force_required?(val) }
523-
.compact_blank
536+
.map { |val| validator_force_required?(val) }
537+
.compact_blank
524538
self.required = validators_force_require_on.any?(true) ||
525539
validators_force_require_on.reject { |opt| opt == true }.flatten.presence
526540
self.sort = true

0 commit comments

Comments
 (0)