Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce WebPipe::Pipe and make the DSL optional #47

Merged
merged 1 commit into from
Nov 7, 2021

Conversation

waiting-for-dev
Copy link
Owner

This commit is a vast internal refactor of the whole library plus the
addition of new features. While the public API remains
backward-compatible, the core functionality has been extracted to be
independent of the DSL.

We were already halfway to making the DSL a pure convenience layer.
WebPipe::App already gave us the option to build rack applications by
plugging operations, but we missed the opportunity to add middlewares or
inspect both operations and middlewares.

WebPipe::Pipe is a new addition representing the top abstraction of
the library, besides the DSL, which only adds a thin layer of
convenience. An instance of it has methods to plug operations, use
middlewares and inspect everything. All the other nifty features, say
composing other pipes, are also available. The DSL will only
transparently delegate to instances of it.

A WebPipe::Pipe instance is the rack application itself. As with the
case of WebPipe::Conn, it's an immutable class: i.e., methods like
#plug or #use will return new instances every time.

In the process, we've made two small additions related to how pipes are
composed. In the case of plugs, we allow anything responding to
#to_proc (as WebPipe::Pipe#call belongs to the required rack
interface). For middlewares, we remove ad-hoc checking for a WebPipe
class and instead duck type on a #to_middlewares method (implemented
on WebPipe::Pipe and delegated from the DSL).

For instance, the following rack application written through the DSL:

require 'web_pipe'

WebPipe.load_extensions(:params)

class HelloApp
  include WebPipe

  plug :fetch_name
  plug :render

  private

  def fetch_name(conn)
    conn.add(:name, conn.params['name'])
  end

  def render(conn)
    conn.set_response_body("Hello, #{conn.fetch(:name)}!")
  end
end

run HelloApp.new

is exactly equivalent to:

require 'web_pipe'
require 'web_pipe/pipe'

WebPipe.load_extensions(:params)

app = WebPipe::Pipe.new
                   .plug(:fetch_name, ->(conn) { conn.add(:name, conn.params['name']) })
                   .plug(:render, ->(conn) { conn.set_response_body("Hello, #{conn.fetch(:name)}") })

run app

WebPipe::Pipe.new accepts to parameters container: & context: that
are given on plug resolution. The container parameter is
self-explanatory. context: represents the object expected to implement
methods matching plug names when no specification is given (on the DSL,
that's the class including the WebPipe module).

Other minor changes made during the refactor:

  • Update rubocop target version
  • Improve some docs
  • Remove docs for private stuff
  • Sort the loading of extensions
  • Make a more limited usage of dry-types (only for structs and some
    sanitization)
  • Fix some code style issues

This commit is a vast internal refactor of the whole library plus the
addition of new features. While the public API remains
backward-compatible, the core functionality has been extracted to be
independent of the DSL.

We were already halfway to making the DSL a pure convenience layer.
`WebPipe::App` already gave us the option to build rack applications by
plugging operations, but we missed the opportunity to add middlewares or
inspect both operations and middlewares.

`WebPipe::Pipe` is a new addition representing the top abstraction of
the library, besides the DSL, which only adds a thin layer of
convenience. An instance of it has methods to plug operations, use
middlewares and inspect everything. All the other nifty features, say
composing other pipes, are also available. The DSL will only
transparently delegate to instances of it.

A `WebPipe::Pipe` instance is the rack application itself. As with the
case of `WebPipe::Conn`, it's an immutable class: i.e., methods like
`#plug` or `#use` will return new instances every time.

In the process, we've made two small additions related to how pipes are
composed. In the case of plugs, we allow anything responding to
`#to_proc` (as `WebPipe::Pipe#call` belongs to the required rack
interface). For middlewares, we remove ad-hoc checking for a `WebPipe`
class and instead duck type on a `#to_middlewares` method (implemented
on `WebPipe::Pipe` and delegated from the DSL).

For instance, the following rack application written through the DSL:

```ruby
require 'web_pipe'

WebPipe.load_extensions(:params)

class HelloApp
  include WebPipe

  plug :fetch_name
  plug :render

  private

  def fetch_name(conn)
    conn.add(:name, conn.params['name'])
  end

  def render(conn)
    conn.set_response_body("Hello, #{conn.fetch(:name)}!")
  end
end

run HelloApp.new
```

is exactly equivalent to:

```ruby
require 'web_pipe'
require 'web_pipe/pipe'

WebPipe.load_extensions(:params)

app = WebPipe::Pipe.new
                   .plug(:fetch_name, ->(conn) { conn.add(:name, conn.params['name']) })
                   .plug(:render, ->(conn) { conn.set_response_body("Hello, #{conn.fetch(:name)}") })

run app
```

`WebPipe::Pipe.new` accepts to parameters `container:` & `context:` that
are given on plug resolution. The `container` parameter is
self-explanatory. `context:` represents the object expected to implement
methods matching plug names when no specification is given (on the DSL,
that's the class including the `WebPipe` module).

Other minor changes made during the refactor:

- Update rubocop target version
- Improve some docs
- Remove docs for private stuff
- Sort the loading of extensions
- Make a more limited usage of dry-types (only for structs and some
sanitization)
- Fix some code style issues
@waiting-for-dev waiting-for-dev merged commit 0982eb5 into master Nov 7, 2021
@waiting-for-dev waiting-for-dev deleted the waiting-for-dev/extract_dsl branch November 7, 2021 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant