# frozen_string_literal: true require 'roda' require 'roda/endpoints/endpoint/collection' require 'roda/endpoints/endpoint/item' require 'rom/struct/to_json' class Roda # Module containing {Roda} plugins. module RodaPlugins # Endpoints plugin for {Roda} module Endpoints # @param [Class(Roda)] app def self.load_dependencies(app) app.plugin :all_verbs app.plugin :head app.plugin :caching app.plugin :monads app.plugin :symbol_status app.plugin :symbol_matchers app.plugin :slash_path_empty app.plugin :json_parser app.plugin :indifferent_params app.plugin :json, classes: [Array, Hash, ROM::Struct] return if app.respond_to?(:[]) require 'dry-container' app.extend Dry::Container::Mixin app.plugin :flow end # `Roda::RodaRequest` instant extensions. module RequestMethods # Implements collection endpoint using given args # # @param name [Symbol] # @param item [{Symbol=>Object}] # @param kwargs [{Symbol=>Object}] # @param (see Endpoint::Collection#initialize) # @see Endpoint::Collection.defaults # @yieldparam endpoint [Collection] # @yieldreturn [#to_json] # @return [Endpoint] # # @example # r.collection :articles, item: { only: %i(get delete) } # # # is equivalent to # # r.on 'articles', item: { only: %i(get delete) } do # articles = Endpoint.new( # name: :articles, # container: container, # ) # # r.last_modified articles.last_modified # # r.get do # articles.call(:get, r.params) # end # # r.post do # articles.call(:post, r.params) # end # # r.child :id, only: %i(get delete) # end def collection(name, item: { by: :id }, path: name, **kwargs) endpoint = Roda::Endpoints::Endpoint::Collection.new( name: name, container: roda_class, item: item, **kwargs ) endpoints.push endpoint on path.to_s do # @route /:name yield endpoint if block_given? instance_exec(self, endpoint, &endpoint.route) end endpoints.pop #=> endpoint end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize # @param [Symbol] by # @param [Endpoint::Collection] collection # @param [Hash] kwargs # # @example # r.collection :articles do |articles| # r.child :id # end # # # is equivalent to # # r.collection :articles do |articles| # r.on :id do |id| # # def article # @article ||= articles.repository.fetch(id) # end # # r.get do # article # end # end # end def child(by: :id, collection: endpoint, **kwargs) # @route /{collection.name}/{id} on by do |identifier| item = collection.child(id: identifier, identifier: by, **kwargs) endpoints.push item yield item if block_given? instance_exec(self, item, &item.route) endpoints.pop end end private # @param [Symbol] verb def match_transaction(verb) resolve endpoint.transactions.key_for(verb) do |transaction| transaction.call(params) do |m| statuses = endpoint.class.statuses[verb] m.success do |result| response.status = statuses[:success] result end m.failure do |result| if result.is_a?(Array) && result.size == 2 response.status, value = result value else response.status = statuses[:failure] result end end end end end # @return [Endpoint] def endpoint endpoints.last end # @return [] def endpoints @endpoints ||= [Roda::Endpoints::Endpoint.new( name: :root, container: roda_class )] end end end register_plugin :endpoints, Endpoints end end