web_pipe 0.1.0
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/.gitignore +11 -0
 - data/.rspec +3 -0
 - data/.travis.yml +10 -0
 - data/.yardopts +8 -0
 - data/CHANGELOG.md +9 -0
 - data/Gemfile +6 -0
 - data/Gemfile.lock +91 -0
 - data/README.md +279 -0
 - data/Rakefile +6 -0
 - data/bin/console +14 -0
 - data/bin/setup +8 -0
 - data/lib/dry/monads/result/extensions/either.rb +42 -0
 - data/lib/web_pipe.rb +16 -0
 - data/lib/web_pipe/app.rb +106 -0
 - data/lib/web_pipe/conn.rb +397 -0
 - data/lib/web_pipe/conn_support/builder.rb +36 -0
 - data/lib/web_pipe/conn_support/errors.rb +16 -0
 - data/lib/web_pipe/conn_support/headers.rb +97 -0
 - data/lib/web_pipe/conn_support/types.rb +50 -0
 - data/lib/web_pipe/dsl/builder.rb +38 -0
 - data/lib/web_pipe/dsl/class_context.rb +62 -0
 - data/lib/web_pipe/dsl/dsl_context.rb +53 -0
 - data/lib/web_pipe/dsl/instance_methods.rb +60 -0
 - data/lib/web_pipe/plug.rb +103 -0
 - data/lib/web_pipe/rack/app_with_middlewares.rb +61 -0
 - data/lib/web_pipe/rack/middleware.rb +33 -0
 - data/lib/web_pipe/types.rb +31 -0
 - data/lib/web_pipe/version.rb +3 -0
 - data/web_pipe.gemspec +51 -0
 - metadata +244 -0
 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rack'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/conn'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/conn_support/headers'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 6 
     | 
    
         
            +
              module ConnSupport
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Helper module to build a {Conn} from a rack's env.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # It always return a {Conn::Clean} subclass.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 12 
     | 
    
         
            +
                module Builder
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @param env [Types::Env] Rack's env
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @return [Conn::Clean]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def self.call(env)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    rr = ::Rack::Request.new(env)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Conn::Clean.new(
         
     | 
| 
      
 19 
     | 
    
         
            +
                      request: rr,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      env: env,
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      scheme: rr.scheme.to_sym,
         
     | 
| 
      
 23 
     | 
    
         
            +
                      request_method: rr.request_method.downcase.to_sym,
         
     | 
| 
      
 24 
     | 
    
         
            +
                      host: rr.host,
         
     | 
| 
      
 25 
     | 
    
         
            +
                      ip: rr.ip,
         
     | 
| 
      
 26 
     | 
    
         
            +
                      port: rr.port,
         
     | 
| 
      
 27 
     | 
    
         
            +
                      script_name: rr.script_name,
         
     | 
| 
      
 28 
     | 
    
         
            +
                      path_info: rr.path_info,
         
     | 
| 
      
 29 
     | 
    
         
            +
                      query_string: rr.query_string,
         
     | 
| 
      
 30 
     | 
    
         
            +
                      request_body: rr.body,
         
     | 
| 
      
 31 
     | 
    
         
            +
                      request_headers: Headers.extract(env)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    )
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ConnSupport
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Error raised when trying to fetch an entry in {Conn}'s bag for
         
     | 
| 
      
 4 
     | 
    
         
            +
                # an unknown key.
         
     | 
| 
      
 5 
     | 
    
         
            +
                class KeyNotFoundInBagError < KeyError
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # @param key [Any] Key not found in the bag
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(key)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    super(
         
     | 
| 
      
 9 
     | 
    
         
            +
                      <<~eos
         
     | 
| 
      
 10 
     | 
    
         
            +
                        Bag does not contain a key with name +key+.
         
     | 
| 
      
 11 
     | 
    
         
            +
                      eos
         
     | 
| 
      
 12 
     | 
    
         
            +
                    )
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ConnSupport
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Helpers to work with headers and its rack's env representation.
         
     | 
| 
      
 4 
     | 
    
         
            +
                #
         
     | 
| 
      
 5 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Headers
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # Headers which come as plain CGI-like variables (without the `HTTP_`
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # prefixed) from the rack server.
         
     | 
| 
      
 9 
     | 
    
         
            +
                  HEADERS_AS_CGI = %w[CONTENT_TYPE CONTENT_LENGTH].freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # Extracts headers from rack's env.
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Headers are all those pairs which key begins with `HTTP_` plus
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # those detailed in {HEADERS_AS_CGI}.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # 
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @param env [Types::Env[]]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @return [Types::Headers[]]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @see HEADERS_AS_CGI
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @see .normalize_key
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def self.extract(env)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    Hash[
         
     | 
| 
      
 24 
     | 
    
         
            +
                      env.
         
     | 
| 
      
 25 
     | 
    
         
            +
                        select { |k, _v| k.start_with?('HTTP_') }.
         
     | 
| 
      
 26 
     | 
    
         
            +
                        map { |k, v| pair(k[5 .. -1], v) }.
         
     | 
| 
      
 27 
     | 
    
         
            +
                        concat(
         
     | 
| 
      
 28 
     | 
    
         
            +
                          env.
         
     | 
| 
      
 29 
     | 
    
         
            +
                            select { |k, _v| HEADERS_AS_CGI.include?(k) }.
         
     | 
| 
      
 30 
     | 
    
         
            +
                            map { |k, v| pair(k, v) }
         
     | 
| 
      
 31 
     | 
    
         
            +
                        )
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # Adds key/value pair to given headers.
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # Key is normalized.
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @param headers [Type::Headers[]]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @param key [String]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @param value [String]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @return [Type::Headers[]]
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @see .normalize_key
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def self.add(headers, key, value)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Hash[
         
     | 
| 
      
 48 
     | 
    
         
            +
                      headers.to_a.push(pair(key, value))
         
     | 
| 
      
 49 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  # Deletes pair with given key form headers.
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # Accepts a non normalized key.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # @param headers [Type::Headers[]]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @param key [String]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [Type::Headers[]]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @see .normalize_key
         
     | 
| 
      
 62 
     | 
    
         
            +
                  def self.delete(headers, key)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    headers.reject { |k, _v| normalize_key(key) == k }
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  # Creates a pair with normalized key and raw value.
         
     | 
| 
      
 67 
     | 
    
         
            +
                  #
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # @param key [String]
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # @param key [String]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  #
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # @return [Array<String>]
         
     | 
| 
      
 72 
     | 
    
         
            +
                  #
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # @see .normalize_key
         
     | 
| 
      
 74 
     | 
    
         
            +
                  def self.pair(key, value)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    [normalize_key(key), value]
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  # Normalizes a header key to convention.
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # As per RFC2616, headers names are case insensitive. This
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # function normalizes them to PascalCase acting on dashes ('-').
         
     | 
| 
      
 82 
     | 
    
         
            +
                  #
         
     | 
| 
      
 83 
     | 
    
         
            +
                  # When a rack server maps headers to CGI-like variables, both
         
     | 
| 
      
 84 
     | 
    
         
            +
                  # dashes and underscores (`_`) are treated as dashes. This
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # function substitutes all '-' to '_'.
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # @param key [String]
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 90 
     | 
    
         
            +
                  #
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def self.normalize_key(key)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    key.downcase.gsub('_', '-').split('-').map(&:capitalize).join('-')
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/types'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rack/request'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/types'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 6 
     | 
    
         
            +
              module ConnSupport
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Types used for {Conn} struct.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # Implementation self-describes them, but you can look at {Conn}
         
     | 
| 
      
 10 
     | 
    
         
            +
                # attributes for documentation.
         
     | 
| 
      
 11 
     | 
    
         
            +
                module Types
         
     | 
| 
      
 12 
     | 
    
         
            +
                  include Dry.Types()
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  Env = Strict::Hash
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Request = Instance(::Rack::Request)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  Scheme = Strict::Symbol.enum(:http, :https)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  Method = Strict::Symbol.enum(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    :get, :head, :post, :put, :delete, :connect, :options, :trace, :patch
         
     | 
| 
      
 20 
     | 
    
         
            +
                  )
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Host = Strict::String
         
     | 
| 
      
 22 
     | 
    
         
            +
                  Ip = Strict::String.optional
         
     | 
| 
      
 23 
     | 
    
         
            +
                  Port = Strict::Integer
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ScriptName = Strict::String
         
     | 
| 
      
 25 
     | 
    
         
            +
                  PathInfo = Strict::String
         
     | 
| 
      
 26 
     | 
    
         
            +
                  QueryString = Strict::String
         
     | 
| 
      
 27 
     | 
    
         
            +
                  RequestBody = WebPipe::Types.Contract(:gets, :each, :read, :rewind)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  BaseUrl = Strict::String
         
     | 
| 
      
 30 
     | 
    
         
            +
                  Path = Strict::String
         
     | 
| 
      
 31 
     | 
    
         
            +
                  FullPath = Strict::String
         
     | 
| 
      
 32 
     | 
    
         
            +
                  Url = Strict::String
         
     | 
| 
      
 33 
     | 
    
         
            +
                  Params = Strict::Hash
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  Status = Strict::Integer.
         
     | 
| 
      
 36 
     | 
    
         
            +
                             default(200).
         
     | 
| 
      
 37 
     | 
    
         
            +
                             constrained(gteq: 100, lteq: 599)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  ResponseBody = WebPipe::Types.Contract(:each).
         
     | 
| 
      
 39 
     | 
    
         
            +
                                   default([''].freeze)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  Headers = Strict::Hash.
         
     | 
| 
      
 42 
     | 
    
         
            +
                              map(Strict::String, Strict::String).
         
     | 
| 
      
 43 
     | 
    
         
            +
                              default({}.freeze)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  Bag = Strict::Hash.
         
     | 
| 
      
 46 
     | 
    
         
            +
                          map(Strict::Symbol, Strict::Any).
         
     | 
| 
      
 47 
     | 
    
         
            +
                          default({}.freeze)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/initializer'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/dsl/class_context'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/dsl/instance_methods'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 6 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 7 
     | 
    
         
            +
                # When an instance of it is included in a module, the module
         
     | 
| 
      
 8 
     | 
    
         
            +
                # extends a {ClassContext} instance and includes
         
     | 
| 
      
 9 
     | 
    
         
            +
                # {InstanceMethods}.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Builder < Module
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Container with nothing registered.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  EMPTY_CONTAINER = {}.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
                  
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @!attribute [r] container
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @return [Types::Container[]]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  include Dry::Initializer.define -> do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    option :container, type: Types::Container, default: proc { EMPTY_CONTAINER }
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [ClassContext]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  attr_reader :class_context
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def initialize(*args)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    super
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @class_context = ClassContext.new(container: container)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def included(klass)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    klass.extend(class_context)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    klass.include(InstanceMethods)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry-initializer'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/types'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/dsl/dsl_context'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 6 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Defines the DSL and keeps the state for the pipe.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # This is good to be an instance because it keeps the
         
     | 
| 
      
 10 
     | 
    
         
            +
                # configuration (state) for the pipe class: the container
         
     | 
| 
      
 11 
     | 
    
         
            +
                # configured on initialization and both rack middlewares and plugs
         
     | 
| 
      
 12 
     | 
    
         
            +
                # added through the DSL {DSLContext}.
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # As the pipe is extended with an instance of this class, methods
         
     | 
| 
      
 15 
     | 
    
         
            +
                # that are meant to be class methods in the pipe are defined as
         
     | 
| 
      
 16 
     | 
    
         
            +
                # singleton methods of the instance.
         
     | 
| 
      
 17 
     | 
    
         
            +
                #
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 19 
     | 
    
         
            +
                class ClassContext < Module
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Methods to be imported from the {DSLContext}.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  DSL_METHODS = %i[middlewares use plugs plug].freeze
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # @!attribute [r] container
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [Types::Container[]]
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  include Dry::Initializer.define -> do
         
     | 
| 
      
 28 
     | 
    
         
            +
                    option :container, type: Types::Container
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [DSLContext]
         
     | 
| 
      
 32 
     | 
    
         
            +
                  attr_reader :dsl_context
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def initialize(*args)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    super
         
     | 
| 
      
 36 
     | 
    
         
            +
                    @dsl_context = DSLContext.new([], [])
         
     | 
| 
      
 37 
     | 
    
         
            +
                    define_container
         
     | 
| 
      
 38 
     | 
    
         
            +
                    define_dsl
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  
         
     | 
| 
      
 41 
     | 
    
         
            +
                  private
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def define_container
         
     | 
| 
      
 44 
     | 
    
         
            +
                    module_exec(container) do |container|
         
     | 
| 
      
 45 
     | 
    
         
            +
                      define_method(:container) do
         
     | 
| 
      
 46 
     | 
    
         
            +
                        container
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def define_dsl
         
     | 
| 
      
 52 
     | 
    
         
            +
                    DSL_METHODS.each do |method|
         
     | 
| 
      
 53 
     | 
    
         
            +
                      module_exec(dsl_context) do |dsl_context|
         
     | 
| 
      
 54 
     | 
    
         
            +
                        define_method(method) do |*args|
         
     | 
| 
      
 55 
     | 
    
         
            +
                          dsl_context.method(method).(*args)
         
     | 
| 
      
 56 
     | 
    
         
            +
                        end
         
     | 
| 
      
 57 
     | 
    
         
            +
                      end
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/initializer'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/types'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/plug'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'web_pipe/rack/middleware'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 7 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Defines the DSL for the pipe class and keeps it state.
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # This allows adding rack middlewares and plugs at the class
         
     | 
| 
      
 11 
     | 
    
         
            +
                # definition level.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 14 
     | 
    
         
            +
                class DSLContext
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @!attribute middlewares
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @return [Array<Rack::Middleware>]
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # @!attribute plugs
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @return [Array<Plug>]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  include Dry::Initializer.define -> do
         
     | 
| 
      
 23 
     | 
    
         
            +
                    param :middlewares,
         
     | 
| 
      
 24 
     | 
    
         
            +
                          type: Types.Array(Rack::Middleware::Instance)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    param :plugs,
         
     | 
| 
      
 27 
     | 
    
         
            +
                          type: Types.Array(Plug::Instance)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Creates and add a rack middleware to the stack.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # @param middleware
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # [WebPipe::Rack::Middleware::MiddlewareClass[]] Rack middleware
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @param middleware [WebPipe::Rack::Options[]] Options to
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # initialize
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [Array<Rack::Middleware>]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def use(middleware, *options)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    middlewares << Rack::Middleware.new(middleware, options)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # Creates and adds a plug to the stack.
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # @param name [Plug::Name[]]
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @param with [Plug::Spec[]]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # @return [Array<Plug>]
         
     | 
| 
      
 48 
     | 
    
         
            +
                  def plug(name, with: nil)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    plugs << Plug.new(name, with)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/initializer'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/types'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/conn'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'web_pipe/app'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'web_pipe/plug'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'web_pipe/rack/app_with_middlewares'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 9 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 10 
     | 
    
         
            +
                # Instance methods for the pipe.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # It is from here that you get the rack application you can route
         
     | 
| 
      
 13 
     | 
    
         
            +
                # to. The initialization phase gives you the chance to inject any
         
     | 
| 
      
 14 
     | 
    
         
            +
                # of the plugs, while the instance you get has the `#call` method
         
     | 
| 
      
 15 
     | 
    
         
            +
                # expected by rack.
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # The pipe state can be accessed through the pipe class, which
         
     | 
| 
      
 18 
     | 
    
         
            +
                # has been configured through {ClassContext}.
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 21 
     | 
    
         
            +
                module InstanceMethods
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # No injections at all.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  EMPTY_INJECTIONS = {}.freeze
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  # Type for how plugs should be injected.
         
     | 
| 
      
 26 
     | 
    
         
            +
                  Injections = Types::Strict::Hash.map(Plug::Name, Plug::Spec)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # @!attribute [r] injections [Injections[]]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   Injected plugs that allow overriding what has been configured.
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  include Dry::Initializer.define -> do
         
     | 
| 
      
 33 
     | 
    
         
            +
                    param :injections,
         
     | 
| 
      
 34 
     | 
    
         
            +
                          default: proc { EMPTY_INJECTIONS },
         
     | 
| 
      
 35 
     | 
    
         
            +
                          type: Injections
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  # @return [Rack::AppWithMiddlewares]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  attr_reader :rack_app
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def initialize(*args)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    super
         
     | 
| 
      
 43 
     | 
    
         
            +
                    middlewares = self.class.middlewares
         
     | 
| 
      
 44 
     | 
    
         
            +
                    container = self.class.container
         
     | 
| 
      
 45 
     | 
    
         
            +
                    operations = Plug.inject_and_resolve(self.class.plugs, injections, container, self)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    app = App.new(operations)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @rack_app = Rack::AppWithMiddlewares.new(middlewares, app)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # Expected interface for rack.
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # @param env [Hash] Rack env
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # @return [Array] Rack response
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def call(env)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    rack_app.call(env)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/initializer'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'web_pipe/types'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'web_pipe/app'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module WebPipe
         
     | 
| 
      
 6 
     | 
    
         
            +
              # A plug is a specification to resolve a callable object.
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              # It is initialized with a {Name} and a {Spec} and, on resolution
         
     | 
| 
      
 9 
     | 
    
         
            +
              # time, is called with a {Types::Container} and an {Object} to act
         
     | 
| 
      
 10 
     | 
    
         
            +
              # in the following fashion:
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              # - When the spec responds to `#call`, it is returned itself as the
         
     | 
| 
      
 13 
     | 
    
         
            +
              # callable object.
         
     | 
| 
      
 14 
     | 
    
         
            +
              # - When the spec is `nil`, then a {Proc} wrapping a method with the
         
     | 
| 
      
 15 
     | 
    
         
            +
              # plug name in `object` is returned.
         
     | 
| 
      
 16 
     | 
    
         
            +
              # - Otherwise, spec is taken as the key to resolve the operation
         
     | 
| 
      
 17 
     | 
    
         
            +
              # from the `container`.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # @private
         
     | 
| 
      
 20 
     | 
    
         
            +
              class Plug
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Error raised when no operation can be resolved from a {Spec}.
         
     | 
| 
      
 22 
     | 
    
         
            +
                class InvalidPlugError < ArgumentError
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @param name [Any] Name for the plug that can't be resolved
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def initialize(name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    super(
         
     | 
| 
      
 26 
     | 
    
         
            +
                      <<~eos
         
     | 
| 
      
 27 
     | 
    
         
            +
                        Plug with name +#{name}+ is invalid. It must be something
         
     | 
| 
      
 28 
     | 
    
         
            +
                        callable, an instance method when `with:` is not given, or
         
     | 
| 
      
 29 
     | 
    
         
            +
                        something callable registered in the container."
         
     | 
| 
      
 30 
     | 
    
         
            +
                      eos
         
     | 
| 
      
 31 
     | 
    
         
            +
                    )
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                # Type for the name of a plug.
         
     | 
| 
      
 36 
     | 
    
         
            +
                Name = Types::Strict::Symbol.constructor(&:to_sym)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # Type for the spec to resolve and {App::Operation} on a
         
     | 
| 
      
 39 
     | 
    
         
            +
                # {Conn} used by {Plug}.
         
     | 
| 
      
 40 
     | 
    
         
            +
                Spec = App::Operation | Types.Constant(nil) | Types::Strict::String | Types::Strict::Symbol
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                # Type for an instance of self.
         
     | 
| 
      
 43 
     | 
    
         
            +
                Instance = Types.Instance(self)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # @!attribute [r] name
         
     | 
| 
      
 46 
     | 
    
         
            +
                #   @return [Name[]]
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # @!attribute [r] spec
         
     | 
| 
      
 49 
     | 
    
         
            +
                #   @return [Spec[]]
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                include Dry::Initializer.define -> do
         
     | 
| 
      
 53 
     | 
    
         
            +
                  param :name, Name
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  param :spec, Spec
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                # Creates a new instance with given `spec` but keeping `name`.
         
     | 
| 
      
 59 
     | 
    
         
            +
                #
         
     | 
| 
      
 60 
     | 
    
         
            +
                # @param new_spec [Spec[]]
         
     | 
| 
      
 61 
     | 
    
         
            +
                # @return [self]
         
     | 
| 
      
 62 
     | 
    
         
            +
                def with(new_spec)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  self.class.new(name, new_spec)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                # Resolves the operation.
         
     | 
| 
      
 67 
     | 
    
         
            +
                #
         
     | 
| 
      
 68 
     | 
    
         
            +
                # @param container [Types::Container[]]
         
     | 
| 
      
 69 
     | 
    
         
            +
                # @param object [Object]
         
     | 
| 
      
 70 
     | 
    
         
            +
                #
         
     | 
| 
      
 71 
     | 
    
         
            +
                # @return [Operation[]]
         
     | 
| 
      
 72 
     | 
    
         
            +
                # @raise [InvalidPlugError] When nothing callable is resolved.
         
     | 
| 
      
 73 
     | 
    
         
            +
                def call(container, pipe)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  if spec.respond_to?(:call)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    spec
         
     | 
| 
      
 76 
     | 
    
         
            +
                  elsif spec.nil?
         
     | 
| 
      
 77 
     | 
    
         
            +
                    pipe.method(name)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  elsif container[spec] && container[spec].respond_to?(:call)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    container[spec]
         
     | 
| 
      
 80 
     | 
    
         
            +
                  else
         
     | 
| 
      
 81 
     | 
    
         
            +
                    raise InvalidPlugError.new(name)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                # Change `plugs` spec's present in `injections` and resolves.
         
     | 
| 
      
 86 
     | 
    
         
            +
                #
         
     | 
| 
      
 87 
     | 
    
         
            +
                # @param plugs [Array<Plug>]
         
     | 
| 
      
 88 
     | 
    
         
            +
                # @param injections [InstanceMethods::Injections[]]
         
     | 
| 
      
 89 
     | 
    
         
            +
                # @container container [Types::Container[]]
         
     | 
| 
      
 90 
     | 
    
         
            +
                # @object [Object]
         
     | 
| 
      
 91 
     | 
    
         
            +
                #
         
     | 
| 
      
 92 
     | 
    
         
            +
                # @return [Array<Operation[]>]
         
     | 
| 
      
 93 
     | 
    
         
            +
                def self.inject_and_resolve(plugs, injections, container, object)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  plugs.map do |plug|
         
     | 
| 
      
 95 
     | 
    
         
            +
                    if injections.has_key?(plug.name)
         
     | 
| 
      
 96 
     | 
    
         
            +
                      plug.with(injections[plug.name])
         
     | 
| 
      
 97 
     | 
    
         
            +
                    else
         
     | 
| 
      
 98 
     | 
    
         
            +
                      plug
         
     | 
| 
      
 99 
     | 
    
         
            +
                    end.(container, object)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     |