ravenx icon indicating copy to clipboard operation
ravenx copied to clipboard

Consider following plug conventions and implementing pipeline/pipe macros

Open sitch opened this issue 8 years ago • 3 comments

Consider following plug conventions:init/1 and call/2. Also would be more natural to have pipeline/pipe macros. I.e.:

defmodule MyApp.MyEvent do
  use Ravenx.Pipeline
  use MyApp.PhoenixSlackPipe, view: MyApp.SlackView, template: "new_slack_event.text"
  use MyApp.PhoenixEmailPipe, view: MyApp.EmailView, template: "new_email_event.text"

  pipe :slack, channel: "events", async: true
  pipe :email, contact: & &1.assigns.user.email, subject: &"Welcome #{&1.assigns.user.name}"

  def call(pipeline, _opts, payload) do
    user = MyApp.Repo.preload(payload, :friends)
    pipeline.assign(:user, user)
  end
end

and for a pipe:

defmodule MyApp.PhoenixSlackPipe do
  use Ravenx.Pipe
  use MyApp.PhoenixSlack # `channel/2`, `render_title/2`, `render_body/3`, `send/1`

  def init(_pipeline, options) do
    options
  end

  def call(pipeline, options) do
    pipeline
    |> channel(options[:channel])
    |> render_title(options[:title])    
    |> render_body(options[:view], options[:template]) # renders with `pipeline.assigns`
    |> send
  end
end

This type of design would make it so you don't have to load strategies in your config (which doesn't seem necessary), instead you just use them in your Pipe files.

sitch avatar Aug 18 '17 13:08 sitch

Hi @Sitch !

First of all, thanks a lot for sharing the idea. We think it's amazing and it's definitively the way we should focus the next major release of Ravenx.

This change alongside with #28 to reduce unnecessary dependencies are two improvements that can make this library a better one.

If you feel comfortable to participate in the development, you are more than welcome. Otherwise don't worry, the fact of giving us the idea is more than enough.

Thanks! ❤️

odarriba avatar Aug 21 '17 08:08 odarriba

Thanks :) Take a look at https://github.com/appunite/piper

sitch avatar Aug 21 '17 16:08 sitch

Thinking about the approach we can take on this, these are the ideas that came up to my mind (are a version of what was firstly exposed by @Sitch)

defmodule MyApp.Notifications.NewEvent do
  use Ravenx.Pipeline

  @slack_config [
    view: MyApp.NotificationsView, 
    template: "slack_event.text", 
    channel: "events", 
    async: true
  ]

  @email_config [
    view: MyApp.NotificationsView, 
    template: "email_event.text",
    subject: &("Welcome #{&1.assigns.user.name}")
  ]

  pipe MyApp.SlackStrategy, @slack_config
  pipe MyApp.EmailStrategy, @email_config

  def init do
    []
  end

  def call(instance, _opts, payload) do
    user = MyApp.Repo.preload(payload, :friends)
    instance.assign(:user, user)
  end
end
defmodule MyApp.SlackStrategy do
  use Ravenx.Pipe
  use Ravenx.Strategies.Slack # `channel/2`, `render_title/2`, `render_body/3`, `send/1`

  def init(opts) do
    options
  end

  def call(instance, opts) do
    instance
    |> channel(opts[:channel])
    |> render_title(opts[:title])    
    |> render_body(opts[:view], opts[:template])
    |> deliver
  end
end

The app's strategies are intended to be a custom configuration (and use) of the integration itself, specifying how a (for example, Slack) integration is built in the app itself.

And the Ravenx.Strategies.Slack should integrate the helper functions used to fill the instance data and the deliver function.

The Ravenx.Pipeline macro will inject the deliver and deliver_async methods in the notification modules.

Also, the init methods will be evaluated on compile time, so they can be used, like in Plug library, to pre-calculate configurations.

odarriba avatar Sep 02 '17 12:09 odarriba