Skip to content

Instantly share code, notes, and snippets.

@marten
Last active December 23, 2015 00:49
Show Gist options
  • Save marten/6555938 to your computer and use it in GitHub Desktop.
Save marten/6555938 to your computer and use it in GitHub Desktop.

Let's assume some basic AR stuff:

class Invitation < ActiveRecord::Base
  scope :templates, where(template: true)
end

Then we could set up some Pavlov operations, one interactor and some queries and commands:

module Invitations
  class CreateFromTemplate < Interactor
    attribute :template_id

    def authorized?
      context.current_user.can? :create_invitations
    end

    def execute
      template   = backend.crud.find_template(template_id)
      attributes = template.attributes.slice(:name, :content)
      invitation = backend.invitations.create(attributes)
      backend.notification.email(invitation)
      invitation
    end
  end

  class Crud < Pavlov::Ops
    def find_template(id);  Invitation.templates.find(id) end
    def create(attributes); Invitation.create!(attributes); end
  end

  class Notifications < Pavlov::Ops
    def email(invitation)
      sendgrid.send(InvitationToEmail.new(invitation).as_email)
    end
    handle_asynchronously :email # standard DelayedJob
  end
end

To run this, we need an execution context (obviously, everywhere you see Pavlov::, you could also have your own class):

# these would be done on application initialization
invitations = Pavlov::Backend.for(Invitations, sendgrid: Sendgrid.new(ENV["SENDGRID_API_KEY"]))
router      = Pavlov::Router.new('invitations' => invitations)

# these would be done for each web request, somewhere in a before_filter or so
context = Pavlov::Context.new(current_user: @user)
cortex  = Pavlov::Cortex.new(context, router)

Then a controller can do:

class InvitationController
  def create
    cortex.invitations.create_from_template(template_id: params[:invitation][:id])
  end
end

Thoughts

  • Invitations is self-contained, although all interactors know about the cortex through which they were invoked, and can call out to other modules through that. But that makes it easy to find out which modules depend on which.
  • Obviously, wherever I reference Pavlov::SomeClass, you can imagine we provide a generator that creates a wrapper inside the application instead.
  • A router is simply an object that responds to invitations, and passes the cortex along. If the standard router doesn't suffice, build your own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment