# frozen_string_literal: true module Maquina module Resourceful extend ActiveSupport::Concern class ResourceResponse def response(&block) @block = block end def code @block end end included do class_attribute :resource_class, instance_writer: false class_attribute :namespace, instance_writer: false class_attribute :find_by_param, instance_writer: false class_attribute :list_attributes, instance_writer: false class_attribute :form_attributes, instance_writer: false class_attribute :show_attributes, instance_writer: false class_attribute :policy_class, instance_writer: false class_attribute :show_link, instance_writer: false attr_reader :resource, :collection protected # Route proxies def namespace_path @namespace_path ||= namespace.present? ? "#{namespace&.to_s&.underscore}_" : "" end def base_singular_path_name @base_path_name ||= "#{namespace_path}#{resource_class.model_name.singular_route_key}_path" end def base_plural_path_name @base_plural_path_name ||= "#{namespace_path}#{resource_class.model_name.route_key}_path" end def new_resource_path(options = {}) Rails.application.routes.url_helpers.send("new_#{base_singular_path_name}", **options) end def edit_resource_path(resource, options = {}) Rails.application.routes.url_helpers.send("edit_#{base_singular_path_name}", resource, **options) end def resource_path(resource) Rails.application.routes.url_helpers.send(base_singular_path_name, resource) end def collection_path(params = {}) Rails.application.routes.url_helpers.send(base_plural_path_name, **params) end def submit_path(params = {}) end # Controller secure params def resource_secure_params method_name = :secure_params method_action_name = "#{action_name}_#{method_name}".to_sym if respond_to?(method_action_name, true) send(method_action_name) elsif respond_to?(method_name, true) send(method_name) else params.require(resource_class.model_name.element.to_sym).permit(form_attributes.flat_map { |field| field.keys }) end end def set_flash_message(status) key = (%w[created accepted].include?(status.to_s) || (status == :no_content && action_name == "destroy")) ? :notice : :alert message_hash = t( "flash.#{controller_name}.#{action_name}.#{key}", default: t("flash.#{action_name}.#{key}") ) flash[key] = message_hash if !%w[create update].include?(action_name) || (%w[create update].include?(action_name) && %w[created accepted].include?(status.to_s)) flash.now[key] = message_hash if %w[create update].include?(action_name) && status == :unprocessable_entity end def dual_action_response(object, &block) has_errors = object&.errors&.any? responded = false case block.try(:arity) when 2 responded = true success = ResourceResponse.new failure = ResourceResponse.new block.call success, failure if has_errors failure.code.call else success.code.call end when 1 if !has_errors responded = true success = ResourceResponse.new block.call success success.code.call end end if !responded respond_to do |format| if has_errors format.html { render object.persisted? ? :edit : :new } # format.turbo_stream format.json { render json: {errors: object.errors.map { |error| {error.attribute => error.message} }} } else format.html { redirect_to collection_path, status: :see_other } format.json { render json: {data: Array(object), links: build_json_links(object)} } end end end end def build_json_links(object) [ { rel: :self, uri: resource_path(object) } ] end helper_method :resource_class, :policy_class, :resource, :list_attributes, :form_attributes, :collection, :collection_path, :resource_path, :new_resource_path, :edit_resource_path, :submit_path, :show_link end class_methods do def resourceful(resource_class: nil, namespace: nil, find_by_param: :id, only: [], except: [], list_attributes: [], form_attributes: [], show_attributes: [], policy_class: nil, show_link: nil) self.resource_class = resource_class || controller_path.classify.safe_constantize self.find_by_param = find_by_param || :id self.list_attributes = Array(list_attributes).compact self.form_attributes = Array(form_attributes).compact self.show_attributes = Array(show_attributes).compact self.policy_class = policy_class self.show_link = show_link self.namespace = namespace valid_rest_actions = { index: Maquina::Index, new: Maquina::New, create: Maquina::Create, edit: Maquina::Edit, update: Maquina::Update, show: Maquina::Show, destroy: Maquina::Destroy }.freeze sanitized_only = Array(only).compact sanitized_except = Array(except).compact rest_actions_keys = valid_rest_actions.keys calculated_only = sanitized_only.empty? ? rest_actions_keys : rest_actions_keys & sanitized_only (calculated_only - sanitized_except).each do |action| rest_action = valid_rest_actions[action] if rest_action.present? include rest_action else Rails.logger.error "[#{self.class} :: Action #{action} is undefined]" end end end end end end