lib/active_scaffold/actions/update.rb
module ActiveScaffold::Actions
module Update
def self.included(base)
base.before_action :update_authorized_filter, :only => %i[edit update]
base.helper_method :update_refresh_list?
end
def edit
do_edit
respond_to_action(:edit)
end
def update
do_update
respond_to_action(:update)
end
# for inline (inlist) editing
def update_column
do_update_column
@column_span_id = params.delete(:editor_id) || params.delete(:editorId)
end
protected
def edit_respond_to_html
if successful?
render(:action => 'update')
else
return_to_main
end
end
def edit_respond_to_js
render(:partial => 'update_form')
end
def update_respond_on_iframe
do_refresh_list if successful? && active_scaffold_config.update.refresh_list && !render_parent?
responds_to_parent do
render :action => 'on_update', :formats => [:js], :layout => false
end
end
def update_respond_to_html
if successful? # just a regular post
message = as_(:updated_model, :model => ERB::Util.h(@record.to_label))
if params[:dont_close]
flash.now[:info] = message
render(:action => 'update')
else
flash[:info] = message
return_to_main
end
else
render(:action => 'update')
end
end
def record_to_refresh_on_update
if update_refresh_list?
do_refresh_list
else
reload_record_on_update
end
end
def reload_record_on_update
@updated_record = @record
# get_row so associations are cached like in list action
# if record doesn't fullfil current conditions remove it from list
get_row
rescue ActiveRecord::RecordNotFound
nil
end
def update_respond_to_js
if successful?
record_to_refresh_on_update if !render_parent? && active_scaffold_config.actions.include?(:list)
flash.now[:info] = as_(:updated_model, :model => ERB::Util.h((@updated_record || @record).to_label)) if active_scaffold_config.update.persistent
end
render :action => 'on_update'
end
def update_respond_to_xml
response_to_api(:xml, update_columns_names)
end
def update_respond_to_json
response_to_api(:json, update_columns_names)
end
def update_columns_names
active_scaffold_config.update.columns.visible_columns_names
end
# A simple method to find and prepare a record for editing
# May be overridden to customize the record (set default values, etc.)
def do_edit
@record = find_if_allowed(params[:id], :update)
end
# A complex method to update a record. The complexity comes from the support for subforms,
# and saving associated records.
# If you want to customize this algorithm, consider using the +before_update_save+ callback
def do_update
do_edit
update_save
end
def update_save(attributes: params[:record], no_record_param_update: false)
active_scaffold_config.model.transaction do
unless no_record_param_update
@record = update_record_from_params(@record, active_scaffold_config.update.columns, attributes)
end
before_update_save(@record)
# errors to @record can be added by update_record_from_params when association fails
# to set and ActiveRecord::RecordNotSaved is raised
# this syntax avoids a short-circuit, so we run validations on record and associations
self.successful = [@record.keeping_errors { @record.valid? }, @record.associated_valid?].all?
unless successful?
# some associations such as habtm are saved before saved is called on parent object
# we have to revert these changes if validation fails
raise ActiveRecord::Rollback, "don't save habtm associations unless record is valid"
end
@record.save! && @record.save_associated!
after_update_save(@record)
end
rescue ActiveRecord::StaleObjectError
@record.errors.add(:base, as_(:version_inconsistency))
self.successful = false
rescue ActiveRecord::RecordNotSaved => exception
logger.warn do
"\n\n#{exception.class} (#{exception.message}):\n " +
Rails.backtrace_cleaner.clean(exception.backtrace).join("\n ") +
"\n\n"
end
@record.errors.add(:base, as_(:record_not_saved)) if @record.errors.empty?
self.successful = false
rescue ActiveRecord::ActiveRecordError => ex
flash[:error] = ex.message
self.successful = false
end
def do_update_column
# delete from params so update :table won't break urls, also they shouldn't be used in sort links too
value = params.delete(:value)
column = params.delete(:column)
params.delete(:original_html)
params.delete(:original_value)
@column = active_scaffold_config.columns[column]
value_record = record_for_update_column
return unless value_record
value = value_for_update_column(value, @column, value_record)
value_record.send("#{@column.name}=", value)
before_update_save(@record)
self.successful = value_record.save
if !successful?
flash.now[:error] = value_record.errors.full_messages.presence
elsif active_scaffold_config.actions.include?(:list)
if @column.inplace_edit_update == :table
params.delete(:id)
do_list
elsif @column.inplace_edit_update
get_row
end
end
after_update_save(@record)
end
def record_for_update_column
@record = find_if_allowed(params[:id], :read)
return unless @record.authorized_for?(:crud_type => :update, :column => @column.name)
if @column.delegated_association
value_record = @record.send(@column.delegated_association.name)
value_record ||= @record.association(@column.delegated_association.name).build
value_record if value_record.authorized_for?(:crud_type => :update, :column => @column.name)
else
@record
end
end
def value_for_update_column(param_value, column, record)
unless param_value
param_value = ActiveScaffold::Core.column_type_cast column.default_for_empty_value, column.column
param_value = false if param_value == true
end
value = column_value_from_param_value(record, column, param_value)
value = [] if value.nil? && column.form_ui && column.association&.collection?
value
end
# override this method if you want to inject data in the record (or its associated objects) before the save
def before_update_save(record); end
# override this method if you want to do something after the save
def after_update_save(record); end
# should we refresh whole list after update operation
def update_refresh_list?
active_scaffold_config.update.refresh_list
end
# The default security delegates to ActiveRecordPermissions.
# You may override the method to customize.
def update_authorized?(record = nil, column = nil)
(!nested? || !nested.readonly?) && (record || self).authorized_for?(crud_type: :update, column: column, reason: true)
end
def update_ignore?(record = nil)
!authorized_for?(:crud_type => :update)
end
private
def update_authorized_filter
link = active_scaffold_config.update.link || self.class.active_scaffold_config.update.class.link
raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
end
def edit_formats
(default_formats + active_scaffold_config.formats).uniq
end
def update_formats
(default_formats + active_scaffold_config.formats + active_scaffold_config.update.formats).uniq
end
end
end