# frozen_string_literal: true require 'roda/endpoints' require 'roda/endpoints/transactions' require 'inflecto' class Roda module Endpoints class Endpoint module ClassInterface # @return [] attr_accessor :attributes # @return [{Symbol=>Object}] attr_accessor :defaults # @return [{Symbol=>{Symbol=>Symbol}}] attr_accessor :statuses # @return [] attr_accessor :verbs # @param [Symbol] key def define_attribute(key) attr_reader key end # @return [Dry::Container] def container @container ||= Roda::Endpoints.container end # @param [Class] child def inherited(child) child.attributes = attributes.dup child.defaults = defaults.dup child.statuses = statuses.dup child.verbs = verbs.dup child.transactions = transactions.dup child.route(&@route_block) super end def ns @ns ||= name.gsub(/^Roda::Endpoints::/, '').underscore.tr('/', '.') end # @return [Symbol] def type @type ||= Inflecto.underscore(Inflecto.demodulize(name)).to_sym end # @param [Symbol] verb # @param [Proc] block # @return [Symbol] name of the defined method def verb(verb, rescue_from: [], &block) self.verbs ||= superclass.verbs (self.verbs += [verb]).freeze rescue_from = Array(rescue_from).flatten if rescue_from.any? block = proc do |*args| begin instance_exec(*args, &block) rescue *rescue_from Left($ERROR_INFO) end end end define_method(verb, &block) key = "operations.#{type}.#{verb}" container.register key, block end # @param [String, Symbol] key # @param [Proc] block # # @example # r.collection :articles do |articles| # # register validation at 'validations.endpoints.articles.default' # articles.validate do # required(:title).filled? # required(:contents).filled? # end # # redefine validation for patch method at # # 'validations.endpoints.articles.patch' # articles.validate(:patch) do # required(:title).filled? # required(:contents).filled? # require(:confirm_changes). # end # end def validate(key = :default, &block) key = "validations.#{ns}.#{key}" if key.is_a?(Symbol) schema = Dry::Validation.Form(&block) container.register(key) do |params| validation = schema.call(params) if validation.success? Right(validation.output) else Left([:unprocessable_entity, {}, validation]) end end schema end # rubocop:enable Metrics/MethodLength # @param [Proc] block # @return [Proc] def route(&block) @route_block = block if block_given? @route_block end def transaction(name, &block) transactions << [name, block] end # @return [(Symbol, Hash, Proc)] def transactions @transactions ||= [] end attr_writer :transactions end end end end