dryer_routes 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +18 -0
- data/README.md +156 -0
- data/dryer_routes.gemspec +29 -0
- data/lib/dryer/routes/build_from_resource.rb +27 -0
- data/lib/dryer/routes/hash_object.rb +18 -0
- data/lib/dryer/routes/registries/create.rb +11 -0
- data/lib/dryer/routes/registry.rb +103 -0
- data/lib/dryer/routes/resource_schema.rb +50 -0
- data/lib/dryer/routes/route.rb +45 -0
- data/lib/dryer/routes/simple_service.rb +13 -0
- data/lib/dryer/routes/version.rb +9 -0
- data/lib/dryer_routes.rb +1 -0
- metadata +113 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: a0daf5be57e9f83be3336908b4c91e317c379125dfe7ebf169d871069cbbc191
         | 
| 4 | 
            +
              data.tar.gz: d7993a1caac99edfd196e4bfa9ed46b3e0c3f790d78e57759b4645939d6b07f2
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 01035ae9c9ac7ddd871d4f255222af12ffcda7f6b8559669ab33ef442110afdff4683ff722507c43606c57047170500fc8e8410279a8e984a0afd091b2db977f
         | 
| 7 | 
            +
              data.tar.gz: 8629dc965328f7e627e8436e34cb4b20ef7ac00b291eed061d9959c43ec3df07a6d366b2f360bc615c4b9e33b8369ff1fa49e74f4363c984f4b686ba520bd6ca
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            MIT License
         | 
| 2 | 
            +
            Copyright (c) 2023 John Bernier
         | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 5 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            +
            the following conditions:
         | 
| 10 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 11 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 12 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 13 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 14 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
         | 
| 15 | 
            +
            IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
         | 
| 16 | 
            +
            CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
         | 
| 17 | 
            +
            TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
         | 
| 18 | 
            +
            SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,156 @@ | |
| 1 | 
            +
            # Dryer Routes
         | 
| 2 | 
            +
            Dryer Routes is a gem allows for request and response types to be added to a
         | 
| 3 | 
            +
            Rails routes file via [dry-validation](https://dry-rb.org/gems/dry-validation/1.8/) contracts
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Installation
         | 
| 6 | 
            +
            add the following to you gemfile
         | 
| 7 | 
            +
            ```
         | 
| 8 | 
            +
            gem "dryer_routes"
         | 
| 9 | 
            +
            ```
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ## Usage
         | 
| 12 | 
            +
            Add this code to `config/initializers/dry_routes.rb`
         | 
| 13 | 
            +
            ```
         | 
| 14 | 
            +
            RouteRegistry = Dryer::Routes::Registry.new
         | 
| 15 | 
            +
            ```
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            And then in `config/routes.rb` you can register your app's routes, eg.
         | 
| 18 | 
            +
            ```
         | 
| 19 | 
            +
            Rails.application.routes.draw do
         | 
| 20 | 
            +
              RouteRegistry.register(
         | 
| 21 | 
            +
                {
         | 
| 22 | 
            +
                  controller: UsersController,
         | 
| 23 | 
            +
                  url: "/users",
         | 
| 24 | 
            +
                  actions: {
         | 
| 25 | 
            +
                    create: {
         | 
| 26 | 
            +
                      method: :post,
         | 
| 27 | 
            +
                      request_contract: Contracts::Users::Post::Request,
         | 
| 28 | 
            +
                      response_contracts: {
         | 
| 29 | 
            +
                        200 => Contracts::Users::Post::Response,
         | 
| 30 | 
            +
                      }
         | 
| 31 | 
            +
                    }
         | 
| 32 | 
            +
                  }
         | 
| 33 | 
            +
                },
         | 
| 34 | 
            +
                {
         | 
| 35 | 
            +
                  controller: SessionsController,
         | 
| 36 | 
            +
                  url: "/sessions",
         | 
| 37 | 
            +
                  actions: {
         | 
| 38 | 
            +
                    create: {
         | 
| 39 | 
            +
                      method: :post,
         | 
| 40 | 
            +
                      request_contract: Contracts::Sessions::Post::Request,
         | 
| 41 | 
            +
                      response_contracts: {
         | 
| 42 | 
            +
                        200 => Contracts::Sessions::Post::Response,
         | 
| 43 | 
            +
                      }
         | 
| 44 | 
            +
                    }
         | 
| 45 | 
            +
                  }
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
              )
         | 
| 48 | 
            +
              RouteRegistry.to_rails_routes(self)
         | 
| 49 | 
            +
            end
         | 
| 50 | 
            +
            ```
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            ## Features
         | 
| 53 | 
            +
            This gem helps organize and enforce typing for your routes, all relevant data
         | 
| 54 | 
            +
            can be accessed through the gem keeping your code *dry*
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            ### Easy to find route metadata
         | 
| 57 | 
            +
            request contracts: `RouteRegistry.users.create.request_contract`
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            route url: `RouteRegistry.users.create.url`
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            response contracts: `RouteRegistry.users.create.response_contracts._200`
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            ### Generating types in controller tests
         | 
| 64 | 
            +
            ```
         | 
| 65 | 
            +
            class UsersControllerIntegreationTest < ActionDispatch::IntegrationTest
         | 
| 66 | 
            +
              test "POST 200 - successfully creating a user" do
         | 
| 67 | 
            +
                request = Dryer::Factories::BuildFromContract.call(
         | 
| 68 | 
            +
                  RouteRegistry.users.create.request_contract
         | 
| 69 | 
            +
                )
         | 
| 70 | 
            +
                post RouteRegistry.users.create.url, params: request.as_json
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                assert_response :success
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                assert_empty RouteRegistry.users.create.response_contracts._200.new.call(
         | 
| 75 | 
            +
                  JSON.parse(response.body)
         | 
| 76 | 
            +
                ).errors
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
| 79 | 
            +
            ```
         | 
| 80 | 
            +
            Shameless plug for my other gem [dryer_factories](https://github.com/jbernie2/dryer-factories)
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            ### Enforcing types in Controllers
         | 
| 83 | 
            +
            By adding an `around_action` to `ApplicationController`, a controller's requests
         | 
| 84 | 
            +
            and responses can be validated automatically eg:
         | 
| 85 | 
            +
            ```
         | 
| 86 | 
            +
            class ApplicationController < ActionController::Base
         | 
| 87 | 
            +
              include Dry::Monads[:result]
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              around_action :validate_request_and_response
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def validate_request
         | 
| 92 | 
            +
                request_errors = RouteRegistry.validate_request(request)
         | 
| 93 | 
            +
                if request_errors.empty?
         | 
| 94 | 
            +
                  @validated_request_body = Dry::Monads::Success(request.params)
         | 
| 95 | 
            +
                else
         | 
| 96 | 
            +
                  @validated_request_body = Dry::Monads::Failure(request_errors)
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
              attr_reader :validated_request_body
         | 
| 100 | 
            +
             | 
| 101 | 
            +
              def validate_response
         | 
| 102 | 
            +
                response_errors = RouteRegistry.validate_response(
         | 
| 103 | 
            +
                  controller: request.controller_class,
         | 
| 104 | 
            +
                  method: request.request_method_symbol,
         | 
| 105 | 
            +
                  status: response.status,
         | 
| 106 | 
            +
                  body: JSON.parse(response.body)
         | 
| 107 | 
            +
                )
         | 
| 108 | 
            +
                if !response_errors.empty?
         | 
| 109 | 
            +
                  Rails.logger.error("
         | 
| 110 | 
            +
                    #{request.controller_class}##{request.request_method_symbol}
         | 
| 111 | 
            +
                    response errors: #{response_errors}
         | 
| 112 | 
            +
                  ")
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
                response
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              def validate_request_and_response
         | 
| 118 | 
            +
                validate_request
         | 
| 119 | 
            +
                if validated_request_body.success?
         | 
| 120 | 
            +
                  yield
         | 
| 121 | 
            +
                  validate_response
         | 
| 122 | 
            +
                else
         | 
| 123 | 
            +
                  render json: {errors: validated_request_body.failure.to_h}, status: :bad_request
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
            end
         | 
| 127 | 
            +
            ```
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            Allowing the controller to look like
         | 
| 130 | 
            +
            ```
         | 
| 131 | 
            +
            class UsersController < ApplicationController
         | 
| 132 | 
            +
              def create
         | 
| 133 | 
            +
                validated_request_body.bind do |body|
         | 
| 134 | 
            +
                  # Do stuff
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            end
         | 
| 138 | 
            +
            ```
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            ## Development
         | 
| 141 | 
            +
            This gem is set up to be developed using [Nix](https://nixos.org/)
         | 
| 142 | 
            +
            Once you have nix installed you can run
         | 
| 143 | 
            +
            `make bundle`
         | 
| 144 | 
            +
            to install all dependencies and
         | 
| 145 | 
            +
            `make dev-shell`
         | 
| 146 | 
            +
            to enter the development environment.
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            ## Contributing
         | 
| 149 | 
            +
            Please create a github issue to report any problems using the Gem.
         | 
| 150 | 
            +
            Thanks for your help in making testing easier for everyone!
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            ## Versioning
         | 
| 153 | 
            +
            Dryer Routes follows Semantic Versioning 2.0 as defined at https://semver.org.
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            ## License
         | 
| 156 | 
            +
            This code is free to use under the terms of the MIT license.
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            Gem::Specification.new do |spec|
         | 
| 2 | 
            +
              spec.name                  = 'dryer_routes'
         | 
| 3 | 
            +
              spec.version               = "0.0.1"
         | 
| 4 | 
            +
              spec.authors               = ['John Bernier']
         | 
| 5 | 
            +
              spec.email                 = ['john.b.bernier@gmail.com']
         | 
| 6 | 
            +
              spec.summary               = 'Typed routing for rails leveraging dry-validation contracts'
         | 
| 7 | 
            +
              spec.description           = <<~DOC
         | 
| 8 | 
            +
                An extension of the Dry family of gems (dry-rb.org).
         | 
| 9 | 
            +
                This gem allows for rails routes to specify contracts for requests 
         | 
| 10 | 
            +
                and responses
         | 
| 11 | 
            +
              DOC
         | 
| 12 | 
            +
              spec.homepage              = 'https://github.com/jbernie2/dryer-routes'
         | 
| 13 | 
            +
              spec.license               = 'MIT'
         | 
| 14 | 
            +
              spec.platform              = Gem::Platform::RUBY
         | 
| 15 | 
            +
              spec.required_ruby_version = '>= 3.0.0'
         | 
| 16 | 
            +
              spec.files = Dir[
         | 
| 17 | 
            +
                'README.md',
         | 
| 18 | 
            +
                'LICENSE',
         | 
| 19 | 
            +
                'CHANGELOG.md',
         | 
| 20 | 
            +
                'lib/**/*.rb',
         | 
| 21 | 
            +
                'dryer_routes.gemspec',
         | 
| 22 | 
            +
                '.github/*.md',
         | 
| 23 | 
            +
                'Gemfile'
         | 
| 24 | 
            +
              ]
         | 
| 25 | 
            +
              spec.add_dependency "dry-validation", "~> 1.10"
         | 
| 26 | 
            +
              spec.add_dependency "dry-types", "~> 1.7"
         | 
| 27 | 
            +
              spec.add_development_dependency "rspec", "~> 3.10"
         | 
| 28 | 
            +
              spec.add_development_dependency "debug", "~> 1.8"
         | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            require_relative "./simple_service"
         | 
| 2 | 
            +
            require_relative "./route"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Dryer
         | 
| 5 | 
            +
              module Routes
         | 
| 6 | 
            +
                class BuildFromResource < SimpleService
         | 
| 7 | 
            +
                  def initialize(resource)
         | 
| 8 | 
            +
                    @resource = resource
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def call
         | 
| 12 | 
            +
                    resource[:actions].map do |action, config|
         | 
| 13 | 
            +
                      Route.new(
         | 
| 14 | 
            +
                        controller: resource[:controller],
         | 
| 15 | 
            +
                        url: config[:url] || resource[:url],
         | 
| 16 | 
            +
                        method: config[:method],
         | 
| 17 | 
            +
                        controller_action: action,
         | 
| 18 | 
            +
                        request_contract: config[:request_contract],
         | 
| 19 | 
            +
                        response_contracts: config[:response_contracts]
         | 
| 20 | 
            +
                      )
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  attr_reader :resource
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Dryer
         | 
| 2 | 
            +
              module Routes
         | 
| 3 | 
            +
                class HashObject
         | 
| 4 | 
            +
                 def initialize(hash)
         | 
| 5 | 
            +
                    hash.each do |k,v|
         | 
| 6 | 
            +
                      key = k.is_a?(Numeric) ? "_#{k}" : k
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                      self.instance_variable_set(
         | 
| 9 | 
            +
                        "@#{key}", v.is_a?(Hash) ? HashObject.new(v) : v
         | 
| 10 | 
            +
                      )
         | 
| 11 | 
            +
                      self.class.send(
         | 
| 12 | 
            +
                        :define_method, key, proc{self.instance_variable_get("@#{key}")}
         | 
| 13 | 
            +
                      )
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,103 @@ | |
| 1 | 
            +
            require_relative "./build_from_resource.rb"
         | 
| 2 | 
            +
            require_relative "./hash_object.rb"
         | 
| 3 | 
            +
            require_relative "./resource_schema.rb"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Dryer
         | 
| 6 | 
            +
              module Routes
         | 
| 7 | 
            +
                class Registry
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def initialize
         | 
| 10 | 
            +
                    @resources = []
         | 
| 11 | 
            +
                    @routes = []
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def register(*resources)
         | 
| 15 | 
            +
                    validate_resources!(resources)
         | 
| 16 | 
            +
                    @resources = resources
         | 
| 17 | 
            +
                    @routes = resources.map do |r|
         | 
| 18 | 
            +
                      BuildFromResource.call(r)
         | 
| 19 | 
            +
                    end.flatten
         | 
| 20 | 
            +
                    add_accessors_for_resources(resources)
         | 
| 21 | 
            +
                    @routes
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def to_rails_routes(router)
         | 
| 25 | 
            +
                    @routes.map { |r| r.to_rails_route(router) }
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def validate_request(request)
         | 
| 29 | 
            +
                    route_for(
         | 
| 30 | 
            +
                      controller: request.controller_class,
         | 
| 31 | 
            +
                      method: request.request_method_symbol
         | 
| 32 | 
            +
                    ).then do |route|
         | 
| 33 | 
            +
                      if route && route.request_contract
         | 
| 34 | 
            +
                        route.request_contract.new.call(request.params).errors
         | 
| 35 | 
            +
                      else
         | 
| 36 | 
            +
                        []
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def validate_response(controller:, method:, status:, body:)
         | 
| 42 | 
            +
                    route_for(
         | 
| 43 | 
            +
                      controller: controller.class,
         | 
| 44 | 
            +
                      method: method.to_sym
         | 
| 45 | 
            +
                    ).then do |route|
         | 
| 46 | 
            +
                      if route && route.response_contract_for(status)
         | 
| 47 | 
            +
                        route.response_contract_for(status).new.call(body).errors
         | 
| 48 | 
            +
                      else
         | 
| 49 | 
            +
                        []
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def route_for(controller:, method:)
         | 
| 55 | 
            +
                    @routes.filter do |r|
         | 
| 56 | 
            +
                      r.controller == controller
         | 
| 57 | 
            +
                      r.method == method
         | 
| 58 | 
            +
                    end.first
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  attr_reader :routes, :resources
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  private
         | 
| 64 | 
            +
                  attr_writer :routes, :resources
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def add_accessors_for_resources(resources)
         | 
| 67 | 
            +
                    denormalize_resources(resources).inject(self) do |obj, (key, value)|
         | 
| 68 | 
            +
                      obj.define_singleton_method(key) { HashObject.new(value) }
         | 
| 69 | 
            +
                      obj
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  def denormalize_resources(resources)
         | 
| 74 | 
            +
                    resources.inject({}) do | h, resource |
         | 
| 75 | 
            +
                      h[
         | 
| 76 | 
            +
                        resource[:controller].controller_name.to_sym
         | 
| 77 | 
            +
                      ] = denormalize_resource(resource)
         | 
| 78 | 
            +
                      h
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def denormalize_resource(resource)
         | 
| 83 | 
            +
                    resource[:actions].each do |key, value|
         | 
| 84 | 
            +
                      resource[:actions][key][:url] =
         | 
| 85 | 
            +
                        resource[:actions][key][:url] || resource[:url]
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                    resource.merge(resource[:actions])
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def validate_resources!(resources)
         | 
| 91 | 
            +
                    errors = resources.map do |r|
         | 
| 92 | 
            +
                      ResourceSchema.new.call(r)
         | 
| 93 | 
            +
                    end.select { |r| !r.errors.empty? }
         | 
| 94 | 
            +
                    if !errors.empty?
         | 
| 95 | 
            +
                      messages = errors.inject({}) do |messages, e|
         | 
| 96 | 
            +
                        messages.merge(e.errors.to_h)
         | 
| 97 | 
            +
                      end
         | 
| 98 | 
            +
                      raise "Invalid arguments: #{messages}"
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            require 'dry-validation'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dryer
         | 
| 4 | 
            +
              module Routes
         | 
| 5 | 
            +
                class ResourceSchema < Dry::Validation::Contract
         | 
| 6 | 
            +
                  params do
         | 
| 7 | 
            +
                    required(:controller).filled(:class)
         | 
| 8 | 
            +
                    required(:url).filled(:string)
         | 
| 9 | 
            +
                    required(:actions).filled(:hash)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  class ActionSchema < Dry::Validation::Contract
         | 
| 13 | 
            +
                    params do
         | 
| 14 | 
            +
                      required(:method).filled(:symbol)
         | 
| 15 | 
            +
                      optional(:request_contract)
         | 
| 16 | 
            +
                      optional(:response_contracts).hash()
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    rule(:request_contract) do
         | 
| 20 | 
            +
                      if value && !value.ancestors.include?(Dry::Validation::Contract)
         | 
| 21 | 
            +
                        key.failure('must be a dry-validation contract')
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    rule(:response_contracts) do
         | 
| 26 | 
            +
                      values[:response_contracts].each do |key, value|
         | 
| 27 | 
            +
                        if !value.ancestors.include?(Dry::Validation::Contract)
         | 
| 28 | 
            +
                          key(:response_contracts).failure(
         | 
| 29 | 
            +
                            'must be a dry-validation contract'
         | 
| 30 | 
            +
                          )
         | 
| 31 | 
            +
                        end
         | 
| 32 | 
            +
                      end if values[:response_contracts]
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  rule(:actions) do
         | 
| 37 | 
            +
                    values[:actions].each do |key, value|
         | 
| 38 | 
            +
                      res = ActionSchema.new.call(value)
         | 
| 39 | 
            +
                      if !res.success?
         | 
| 40 | 
            +
                        res.errors.to_h.each do |name, messages|
         | 
| 41 | 
            +
                          messages.each do |msg|
         | 
| 42 | 
            +
                            key([key_name, name]).failure(msg)
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module Dryer
         | 
| 2 | 
            +
              module Routes
         | 
| 3 | 
            +
                class Route
         | 
| 4 | 
            +
                  def initialize(route_config)
         | 
| 5 | 
            +
                    @route_config = route_config
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def to_rails_route(router)
         | 
| 9 | 
            +
                    router.send(
         | 
| 10 | 
            +
                      route_config[:method],
         | 
| 11 | 
            +
                      route_config[:url],
         | 
| 12 | 
            +
                      to: "#{
         | 
| 13 | 
            +
                        route_config[:controller].controller_path
         | 
| 14 | 
            +
                        }##{
         | 
| 15 | 
            +
                        route_config[:controller_action]
         | 
| 16 | 
            +
                      }"
         | 
| 17 | 
            +
                    )
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def controller
         | 
| 21 | 
            +
                    route_config[:controller]
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def method
         | 
| 25 | 
            +
                    route_config[:method]
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def request_contract
         | 
| 29 | 
            +
                    route_config[:request_contract]
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def response_contract_for(status)
         | 
| 33 | 
            +
                    route_config[:response_contracts][status]
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def url
         | 
| 37 | 
            +
                    route_config[:url]
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  private
         | 
| 41 | 
            +
                  attr_reader :route_config
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
             | 
    
        data/lib/dryer_routes.rb
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            require_relative "./dryer/routes/registry.rb"
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,113 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: dryer_routes
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - John Bernier
         | 
| 8 | 
            +
            autorequire:
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2023-12-01 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: dry-validation
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1.10'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.10'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: dry-types
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '1.7'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1.7'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: rspec
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '3.10'
         | 
| 48 | 
            +
              type: :development
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '3.10'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: debug
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.8'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.8'
         | 
| 69 | 
            +
            description: "An extension of the Dry family of gems (dry-rb.org).\nThis gem allows
         | 
| 70 | 
            +
              for rails routes to specify contracts for requests \nand responses\n"
         | 
| 71 | 
            +
            email:
         | 
| 72 | 
            +
            - john.b.bernier@gmail.com
         | 
| 73 | 
            +
            executables: []
         | 
| 74 | 
            +
            extensions: []
         | 
| 75 | 
            +
            extra_rdoc_files: []
         | 
| 76 | 
            +
            files:
         | 
| 77 | 
            +
            - Gemfile
         | 
| 78 | 
            +
            - LICENSE
         | 
| 79 | 
            +
            - README.md
         | 
| 80 | 
            +
            - dryer_routes.gemspec
         | 
| 81 | 
            +
            - lib/dryer/routes/build_from_resource.rb
         | 
| 82 | 
            +
            - lib/dryer/routes/hash_object.rb
         | 
| 83 | 
            +
            - lib/dryer/routes/registries/create.rb
         | 
| 84 | 
            +
            - lib/dryer/routes/registry.rb
         | 
| 85 | 
            +
            - lib/dryer/routes/resource_schema.rb
         | 
| 86 | 
            +
            - lib/dryer/routes/route.rb
         | 
| 87 | 
            +
            - lib/dryer/routes/simple_service.rb
         | 
| 88 | 
            +
            - lib/dryer/routes/version.rb
         | 
| 89 | 
            +
            - lib/dryer_routes.rb
         | 
| 90 | 
            +
            homepage: https://github.com/jbernie2/dryer-routes
         | 
| 91 | 
            +
            licenses:
         | 
| 92 | 
            +
            - MIT
         | 
| 93 | 
            +
            metadata: {}
         | 
| 94 | 
            +
            post_install_message:
         | 
| 95 | 
            +
            rdoc_options: []
         | 
| 96 | 
            +
            require_paths:
         | 
| 97 | 
            +
            - lib
         | 
| 98 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 99 | 
            +
              requirements:
         | 
| 100 | 
            +
              - - ">="
         | 
| 101 | 
            +
                - !ruby/object:Gem::Version
         | 
| 102 | 
            +
                  version: 3.0.0
         | 
| 103 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 104 | 
            +
              requirements:
         | 
| 105 | 
            +
              - - ">="
         | 
| 106 | 
            +
                - !ruby/object:Gem::Version
         | 
| 107 | 
            +
                  version: '0'
         | 
| 108 | 
            +
            requirements: []
         | 
| 109 | 
            +
            rubygems_version: 3.4.13
         | 
| 110 | 
            +
            signing_key:
         | 
| 111 | 
            +
            specification_version: 4
         | 
| 112 | 
            +
            summary: Typed routing for rails leveraging dry-validation contracts
         | 
| 113 | 
            +
            test_files: []
         |