# frozen_string_literal: true require 'roda/endpoints/endpoint' require 'roda/endpoints/endpoint/data' require 'roda/endpoints/endpoint/caching' class Roda module Endpoints class Endpoint # HTTP endpoint representing a specific item of collection uniquely # identified by some parameter. class Singleton < Roda::Endpoints::Endpoint include Data include Caching self.attributes += %i(finder last_modified) self.defaults = defaults.merge( last_modified: :updated_at, by: :fetch, on: :id, finder: -> { repository.public_send(by, id) } ) # @return [Symbol] attr_reader :by # @return [Symbol] attr_reader :on # @return [Symbol] attr_reader :finder # @return [Integer] def id @id ||= captures.first end # @return [ROM::Struct] def entity @entity ||= fetch_entity end attr_writer :entity # @return [ROM::Struct] def fetch_entity instance_exec(&finder) end # @return [Time] def last_modified @last_modified ? entity.public_send(@last_modified) : super end route do |r, endpoint| endpoint.verbs.each do |verb| # @route #{verb} /{collection.name}/{id} r.public_send(verb, transaction: verb) end end transaction :get do |endpoint| step :retrieve, with: endpoint.operation_for(:get) end transaction :patch do |endpoint| step :validate, with: endpoint.validation_for(:patch) step :persist, with: endpoint.operation_for(:patch) end transaction :put do |endpoint| step :validate, with: endpoint.validation_for(:put) # step :reset, with: 'endpoints.operations.reset' step :persist, with: endpoint.operation_for(:put) end transaction :delete do |endpoint| step :validate, with: endpoint.validation_for(:delete) step :persist, with: endpoint.operation_for(:delete) end # @route GET /{collection.name}/{id} # @!method get(params) # @param [Hash] params # @return [Dry::Monads::Either] verb :get do |_params| Right(entity) end # @route PATCH /{collection.name}/{id} # @!method patch(params) # @param [Hash] params # @return [Dry::Monads::Either] verb :patch do |params| changeset = params[name] Right(repository.update(id, changeset)) end # @route PUT /{collection.name}/{id} # @!method put(params) # @param [Hash] params # @return [Dry::Monads::Either] verb :put do |params| changeset = entity.to_hash.keys.each_with_object({}) do |key, changes| changes[key] = nil end.merge(params[name] || {}) Right(repository.update(id, changeset)) end # @route DELETE /{collection.name}/{id} # @!method delete(params) # @param [Hash] params # @return [Dry::Monads::Either] verb :delete do |_params| if (result = repository.delete(id)) Right(nil) else Left(result) end end end end end end