lib/roda/plugins/endpoints.rb in roda-endpoints-0.1.0 vs lib/roda/plugins/endpoints.rb in roda-endpoints-0.2.0

- old
+ new

@@ -9,34 +9,64 @@ # Module containing {Roda} plugins. module RodaPlugins # Endpoints plugin for {Roda} module Endpoints # @param [Class(Roda)] app - def self.load_dependencies(app) + def self.load_dependencies(app, **_opts) 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 + # @param [Class(Roda)] app + # @param [Hash] opts + def self.configure(app, container: app, **opts) + opts = (app.opts[:endpoints] || {}).merge(opts) + unless container.respond_to? :resolve + require 'dry-container' + container.extend Dry::Container::Mixin + end + app.opts[:endpoints] = opts.merge(container: container) + Roda::Endpoints.roda_class ||= app + end + + # {ClassMethods#register `Roda.register`} && + # {ClassMethods#merge `Roda.merge`} + module ClassMethods + # @param [String, Symbol] name + # @param [Array] args + # @param [Proc] block + def register(name, *args, &block) + opts[:container].register(name, *args, &block) + end + + # @param [String, Symbol] name + # @param [Array] args + # @param [Proc] block + def merge(name, *args, &block) + opts[:container].merge(name, *args, &block) + end + end + # `Roda::RodaRequest` instant extensions. + # rubocop:disable Metrics/ModuleLength module RequestMethods # Implements collection endpoint using given args # # @param name [Symbol] # @param item [{Symbol=>Object}] # @param kwargs [{Symbol=>Object}] + # @param type [Class(Roda::Endpoints::Endpoint::Collection)] + # @param on [String, Symbol] # @param (see Endpoint::Collection#initialize) # @see Endpoint::Collection.defaults # @yieldparam endpoint [Collection] # @yieldreturn [#to_json] # @return [Endpoint] @@ -62,35 +92,32 @@ # 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 + def collection(name, + item: { on: :id }, + type: Roda::Endpoints::Endpoint::Collection, + on: name.to_s, + **kwargs) + endpoint name: name, + item: item, + type: type, + on: on, + **kwargs do |endpoint| 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 + # @param on [Symbol] + # @param kwargs [Hash] # # @example # r.collection :articles do |articles| - # r.child :id + # r.item :id # end # # # is equivalent to # # r.collection :articles do |articles| @@ -103,28 +130,79 @@ # r.get do # article # end # end # end - def child(by: :id, collection: endpoint, **kwargs) + def item(on: :id, + type: Roda::Endpoints::Endpoint::Item, + **kwargs) + unless current_endpoint.is_a?(Roda::Endpoints::Endpoint::Collection) + raise ArgumentError, + "#{self.class}#item called not within a collection endpoint" + end # @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 + endpoint( + on: on, + name: current_endpoint.item_name, + type: type, + **kwargs + ) do |endpoint| + yield endpoint if block_given? end end + # @overload singleton(name, entity, type:, **kwargs) + # @overload singleton(name:, entity:, type:, **kwargs) + def singleton(*args, + name: args.first, + entity: args.last, + type: Roda::Endpoints::Endpoint::Singleton, + **kwargs) + endpoint( + name: name, + entity: entity, + type: type, + **kwargs + ) do |endpoint| + yield endpoint if block_given? + end + end + private + # @param [Class(Roda::Endpoints::Endpoint::Singleton)] type + # @param [Dry::Container::Mixin, #register, #resolve, #merge] container + # @param [Roda::Endpoints::Endpoint] parent + # @param [Hash] kwargs + # rubocop:disable Metrics/ParameterLists + def endpoint(name:, + type:, + container: roda_class.opts[:endpoints][:container], + parent: current_endpoint, + on: name, + **kwargs) + on on do |*captures| + with_current_endpoint parent.child( + name: name, + type: type, + container: container, + parent: parent, + on: on, + captures: captures, + **kwargs + ) do |endpoint| + yield endpoint if block_given? + instance_exec(self, endpoint, &endpoint.route) + end + end + end + # @param [Symbol] verb def match_transaction(verb) - resolve endpoint.transactions.key_for(verb) do |transaction| + resolve current_endpoint.transactions.key_for(verb) do |transaction| transaction.call(params) do |m| - statuses = endpoint.class.statuses[verb] + statuses = current_endpoint.class.statuses[verb] m.success do |result| response.status = statuses[:success] result end @@ -141,20 +219,29 @@ end end end # @return [Endpoint] - def endpoint + def current_endpoint endpoints.last end # @return [<Endpoint>] def endpoints @endpoints ||= [Roda::Endpoints::Endpoint.new( - name: :root, container: roda_class + name: :root, + ns: nil, + container: roda_class.opts[:endpoints][:container] )] end + + def with_current_endpoint(endpoint) + endpoints.push endpoint + yield endpoint + endpoints.pop + end end + # rubocop:enable Metrics/ModuleLength end register_plugin :endpoints, Endpoints end end