module PgEngine
module Resource
def self.included(clazz)
# clazz.before_action :authenticate_user!
clazz.helper_method :atributos_para_listar
clazz.helper_method :atributos_para_mostrar
clazz.helper_method :current_page_size
clazz.helper_method :show_filters?
clazz.helper_method :available_page_sizes
clazz.layout :set_layout
end
def set_layout
if action_name == 'index'
'pg_layout/base'
else
'pg_layout/containerized'
end
end
# Public endpoints
def abrir_modal
pg_respond_abrir_modal
end
def buscar
pg_respond_buscar
end
def index
@collection = filtros_y_policy(atributos_para_buscar, default_sort)
pg_respond_index
end
def show
pg_respond_show
end
def respond_with_modal(klass_or_string)
if klass_or_string.is_a?(Class)
content = klass_or_string.new(instancia_modelo).render_in(view_context)
elsif klass_or_string.is_a?(String)
content = ModalContentComponent.new.with_body_content(klass_or_string)
.render_in(view_context)
end
if can_open_modal?
modal = ModalComponent.new.with_content(content)
render turbo_stream: turbo_stream.append_all('body', modal)
else
render html: content
end
end
def new
if respond_with_modal?
respond_with_modal(FormModalComponent)
else
add_breadcrumb instancia_modelo.submit_default_value
end
end
def edit
if respond_with_modal?
respond_with_modal(FormModalComponent)
else
add_breadcrumb instancia_modelo.to_s_short, instancia_modelo.target_object
add_breadcrumb 'Editando'
end
end
def create
pg_respond_create
end
def update
pg_respond_update
end
def destroy
pg_respond_destroy(instancia_modelo, params[:redirect_to])
end
# End public endpoints
protected
def default_sort
nil
end
def available_page_sizes
[10, 20, 30, 50, 100].push(current_page_size).uniq.sort
end
def show_filters?
cur_route = pg_current_route
idtf = cur_route[:controller] + '#' + cur_route[:action] + '#open-filters'
if params[:ocultar_filtros]
session[idtf] = nil
elsif params[:mostrar_filtros]
session[idtf] = true
end
session[idtf]
end
def current_page_size
if params[:page_size].present?
session[page_size_session_key] = params[:page_size].to_i
end
session[page_size_session_key].presence || default_page_size
end
def page_size_session_key
"#{controller_name}/#{action_name}/page_size"
end
def default_page_size
10
end
def pg_respond_update
object = instancia_modelo
if (@saved = object.save)
if in_modal?
body = <<~HTML.html_safe
HTML
render html: ModalContentComponent.new.with_body_content(body)
.render_in(view_context)
else
redirect_to object.decorate.target_object
end
elsif in_modal?
render html: FormModalComponent.new(instancia_modelo.decorate)
.render_in(view_context)
else
add_breadcrumb instancia_modelo.decorate.to_s_short, instancia_modelo.decorate.target_object
add_breadcrumb 'Editando'
# TODO: esto solucionaría el problema?
# self.instancia_modelo = instancia_modelo.decorate
#
render :edit, status: :unprocessable_entity
end
end
def pg_respond_create
object = instancia_modelo
if (@saved = object.save)
if in_modal?
body = <<~HTML.html_safe
HTML
render html: ModalContentComponent.new.with_body_content(body)
.render_in(view_context)
else
redirect_to object.decorate.target_object
end
elsif in_modal?
render html: FormModalComponent.new(instancia_modelo.decorate)
.render_in(view_context)
else
add_breadcrumb instancia_modelo.decorate.submit_default_value
# TODO: esto solucionaría el problema?
# self.instancia_modelo = instancia_modelo.decorate
render :new, status: :unprocessable_entity
end
end
def pg_respond_index
respond_to do |format|
format.json { render json: @collection }
format.html { render_listing }
format.xlsx do
render xlsx: 'download',
filename: "#{@clase_modelo.nombre_plural.gsub(' ', '-').downcase}" \
"-#{Time.zone.now.strftime('%Y-%m-%d-%H.%M.%S')}.xlsx"
end
end
end
def accepts_turbo_stream?
request.headers['Accept'].present? &&
request.headers['Accept'].include?('text/vnd.turbo-stream.html')
end
def in_modal?
request.headers['turbo-frame'] == 'modal_generic'
end
def respond_with_modal?
can_open_modal? || in_modal?
end
def can_open_modal?
request.get? &&
params[:start_modal] == 'true' &&
accepts_turbo_stream? &&
!in_modal?
end
def pg_respond_show
if respond_with_modal?
respond_with_modal(ShowModalComponent)
else
add_breadcrumb instancia_modelo.to_s_short, instancia_modelo.target_object
end
end
def destroyed_message(model)
"#{model.model_name.human} #{model.gender == 'f' ? 'borrada' : 'borrado'}"
end
def pg_respond_destroy(model, redirect_url = nil)
if destroy_model(model)
if respond_with_modal?
body = <<~HTML.html_safe
HTML
respond_with_modal(body)
elsif redirect_url.present?
redirect_to redirect_url, notice: destroyed_message(model), status: :see_other
elsif accepts_turbo_stream?
body = <<~HTML.html_safe
HTML
render turbo_stream: turbo_stream.append_all('body', body)
else
redirect_back(fallback_location: root_path,
notice: destroyed_message(model), status: 303)
end
elsif in_modal?
flash.now[:alert] = @error_message
render turbo_stream: render_turbo_stream_flash_messages(to: '.modal-body .flash')
elsif accepts_turbo_stream?
flash.now[:alert] = @error_message
render turbo_stream: render_turbo_stream_flash_messages
else
flash[:alert] = @error_message
redirect_back(fallback_location: root_path, status: 303)
end
end
def destroy_model(model)
@error_message = 'No se pudo eliminar el registro'
begin
destroy_method = model.respond_to?(:discard) ? :discard : :destroy
return true if model.send(destroy_method)
@error_message = model.errors.full_messages.join(', ').presence || @error_message
false
rescue ActiveRecord::InvalidForeignKey => e
model_name = t("activerecord.models.#{model.class.name.underscore}")
@error_message = "#{model_name} no se pudo borrar porque tiene elementos asociados."
pg_warn(e)
end
false
end
def render_listing
@collection = @collection.page(params[:page]).per(current_page_size)
@records_filtered = default_scope_for_current_model.any? if @collection.empty?
end
def buscar_instancia
if Object.const_defined?('FriendlyId') && @clase_modelo.is_a?(FriendlyId)
@clase_modelo.friendly.find(params[:id])
elsif @clase_modelo.respond_to? :find_by_hashid!
@clase_modelo.find_by_hashid!(params[:id])
else
@clase_modelo.find(params[:id])
end
rescue ActiveRecord::RecordNotFound
raise PgEngine::PageNotFoundError
end
def set_instancia_modelo
if action_name.in? %w[new create]
self.instancia_modelo = @clase_modelo.new(modelo_params)
else
self.instancia_modelo = buscar_instancia
instancia_modelo.assign_attributes(modelo_params) if action_name.in? %w[update]
end
# FIXME: estaría bueno delegar directamente a pundit, pero habría que
# arreglar tema policies
Current.user&.developer? || authorize(instancia_modelo)
# TODO: problema en create y update cuando falla la validacion
# Reproducir el error antes de arreglarlo
self.instancia_modelo = instancia_modelo.decorate if action_name.in? %w[show edit new]
end
def instancia_modelo=(val)
instance_variable_set(:"@#{nombre_modelo}", val)
end
def instancia_modelo
instance_variable_get(:"@#{nombre_modelo}")
end
def modelo_params
if action_name == 'new'
params.permit(atributos_permitidos)
else
params.require(nombre_modelo).permit(atributos_permitidos)
end
end
def nombre_modelo
@clase_modelo.name.underscore
end
def clase_modelo
# agarro la variable o intento con el nombre del controller
@clase_modelo ||= self.class.name.singularize.gsub('Controller', '').constantize
end
def filtros_y_policy(campos, dflt_sort = nil)
@filtros = PgEngine::FiltrosBuilder.new(
self, clase_modelo, campos
)
scope = policy_scope(clase_modelo)
scope = @filtros.filtrar(scope)
shared_context = Ransack::Adapters::ActiveRecord::Context.new(scope)
@q = @clase_modelo.ransack(params[:q], context: shared_context)
@q.sorts = dflt_sort if @q.sorts.empty? && dflt_sort.present?
shared_context.evaluate(@q)
end
def default_scope_for_current_model
PgEngine::FiltrosBuilder.new(
self, clase_modelo, []
).filtrar(policy_scope(clase_modelo))
end
end
end