# frozen_string_literal: true
module DynamicScaffold
module Controller # rubocop:disable Metrics/ModuleLength
extend ActiveSupport::Concern
include ControllerUtilities
included do
helper_method :dynamic_scaffold_path, :dynamic_scaffold, :request_queries
helper DynamicScaffoldHelper
attr_reader :dynamic_scaffold
before_action lambda {
@dynamic_scaffold = Config.new(self.class.dynamic_scaffold_model, self)
self.class.dynamic_scaffold_initializer.call(@dynamic_scaffold)
}
end
class_methods do
attr_accessor :dynamic_scaffold_model, :dynamic_scaffold_initializer
def dynamic_scaffold(model, &block)
self.dynamic_scaffold_model = model
self.dynamic_scaffold_initializer = block
end
end
# Actions
def index # rubocop:disable Metrics/AbcSize
@records = dynamic_scaffold.list.build_sql(scope_params)
@count = @records.count unless dynamic_scaffold.max_count.nil?
@records = handle_pagination(@records)
@records = @records.order dynamic_scaffold.list.sorter if dynamic_scaffold.list.sorter
@records = @records.order(*dynamic_scaffold.list.order) unless dynamic_scaffold.list.order.empty?
ret = yield(@records) if block_given?
@records = ret unless ret.nil?
unless @records.is_a? ::ActiveRecord::Relation
raise(
Error::InvalidOperation,
'You must return ActiveRecord::Relation from super block'
)
end
@records
end
def new # rubocop:disable Metrics/AbcSize
raise ActionController::RoutingError, '`Add Button` is disabled.' unless dynamic_scaffold.list.add_button
unless dynamic_scaffold.max_count.nil?
count = dynamic_scaffold.list.build_sql(scope_params).count
raise Error::InvalidOperation, 'You can not add any more.' if dynamic_scaffold.max_count?(count)
end
@record = dynamic_scaffold.model.new
defaults = dynamic_scaffold.form.items.each_with_object({}) do |item, memo|
memo[item.name] = item.default_value(view_context) if dynamic_scaffold.model.column_names.include?(item.name.to_s)
end
@record.attributes = defaults.merge(scope_params)
end
def edit
@record = find_record(edit_params)
raise ActionController::RoutingError, '`Edit Button` is disabled.' unless dynamic_scaffold.list.edit_buttons(@record)
end
def create
@record = dynamic_scaffold.model.new
@record.attributes = update_values
bind_sorter_value(@record) if dynamic_scaffold.list.sorter
dynamic_scaffold.model.transaction do
check_max_count!
yield(@record) if block_given?
if @record.save
redirect_to dynamic_scaffold_path(:index, request_queries)
else
render "#{params[:controller]}/new"
end
end
end
def update # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
values = update_values
@record = find_record(dynamic_scaffold.model.primary_key => params['id'])
datetime_select_keys = []
prev_values = values.keys.map do |k|
# handle paramters like `start_time(1i)`
match = k.match(/(.*?)\(\d+i\)/)
if match
next if datetime_select_keys.include?(match[1])
datetime_select_keys << k = match[1]
end
# globalize
next [:translations_attributes, @record.translations] if k == 'translations_attributes'
# skip nested_attributes
next unless @record.respond_to? k
[k, @record.public_send(k)]
end.compact.to_h.with_indifferent_access
@record.attributes = values
dynamic_scaffold.model.transaction do
yield(@record, prev_values) if block_given?
if @record.save
redirect_to dynamic_scaffold_path(:index, request_queries)
else
render "#{params[:controller]}/edit"
end
end
end
def destroy # rubocop:disable Metrics/AbcSize
# `Destroy` also does not support multiple primary keys too.
record = find_record(dynamic_scaffold.model.primary_key => params['id'])
begin
dynamic_scaffold.model.transaction do
yield(record) if block_given?
record.destroy
end
rescue ::ActiveRecord::InvalidForeignKey => e
flash[:dynamic_scaffold_danger] = I18n.t('dynamic_scaffold.alert.destroy.invalid_foreign_key')
logger.error(e)
rescue StandardError => e
flash[:dynamic_scaffold_danger] = I18n.t('dynamic_scaffold.alert.destroy.failed')
logger.error(e)
end
flash[:dynamic_scaffold_danger] = record.errors[:base].join('
') if record.errors[:base].any?
redirect_to dynamic_scaffold_path(:index, request_queries)
end
def sort # rubocop:disable Metrics/AbcSize
pkeys_list = sort_params
reset_sequence(pkeys_list.size)
begin
dynamic_scaffold.model.transaction do
pkeys_list.each do |pkeys|
rec = find_record(pkeys.to_hash)
next_sec = next_sequence!
rec[dynamic_scaffold.list.sorter_attribute] = next_sec
yield(rec) if block_given?
rec.save!
end
end
rescue ActiveRecord::RecordInvalid => e
messages = [I18n.t('dynamic_scaffold.message.sort_error'), '', sort_validation_error_title(e.record)]
messages.concat(e.record.errors.full_messages)
flash[:dynamic_scaffold_danger] = messages.join('
')
end
redirect_to dynamic_scaffold_path(:index, request_queries)
end
end
end