Skip to content

Instantly share code, notes, and snippets.

@krisleech
Created February 8, 2013 23:44
Show Gist options
  • Save krisleech/4742912 to your computer and use it in GitHub Desktop.
Save krisleech/4742912 to your computer and use it in GitHub Desktop.
services and responders: which protocol (duck type) is best for talking between controller and service
class OrderController
def new
@order = Order.new
end
def create
command = CreateOrder.new(current_user, params[:order])
command.execute(:success => lambda { redirect_to orders_path },
:failure => lambda { |order| @order = order; render :action => :new })
end
end
# Another example:
class OrderController
def new
@order = Order.new
end
def create
command = CreateOrder.new(current_user, params[:order])
command.execute(:success => lambda { redirect_to orders_path },
:failure => CreateFailure.new(self)
end
class CreateFailure < SimpleDelegator
def call(order)
@order = order
render :action => :new
end
end
end
class OrdersController
def create
command = CreateOrder.new(current_user, params[:order])
command.execute(CreateResponder.new(self))
end
class CreateResponder < SimpleDelegator
def success(order)
redirect_to orders_path
end
def failure(order)
@order = order
render :action => :new
end
end
end
class OrdersController
def new
@order = Order.new
end
def create
command = CreateOrder.new(current_user, params[:order])
command.execute(OrderResponder.new(self))
end
def edit
@order = current_user.orders.find(params[:id])
end
def update
command = UpdateOrder.new(current_user, params[:order])
command.execute(OrderResponder.new(self))
end
class OrderResponder < SimpleDelegator
def create_successful(order)
redirect_to orders_path
end
def create_failed(order)
@order = order
render :action => :new
end
def update_successful(order)
flash[:notice] = 'Order updated'
redirect_to order_path(order)
end
def update_failed(order)
@order = order
render :action => :edit
end
end
end
# Matt Wynne-ish
def create
command = CreateOrder.new(self)
command.execute(current_user, params[:order])
end
def order_create_successful(order)
redirect_to order_path(order)
end
def order_create_failed(order)
@order = order
render :action => :new
end
class CreateOrder
def initialize(listener)
@listener = listener
end
def execute(user, attributes)
order = Order.new(attributes)
order.created_by = user
if order.valid?
listener.order_create_successful(order)
else
listener.order_create_failed(order)
end
end
end
# stacking services (adapted from Matt Wynee)
def update
command = UpdateOrder.new(current_user, params[:order])
command.execute(OrderResponder.new(WriteActivityFeed.new(self)))
end
class WriteActivityFeed < SimpleDelegator
def order_create_successful(order)
ActivityFeed.create(:order => order)
super(order)
end
end
# registering listeners
def create
command = CreateOrder.new
command.listeners << self
command.listeners << ActivityFeed.new
command.execute(current_user, params[:order])
end
class CreateOrder
def listeners
@listeners ||= []
end
def execute(user, attributes)
order = Order.new(attributes)
order.created_by = user
if order.valid?
order.save!
notify_listeners(:create_order_successful)
else
notify_listeners(:create_order_failed)
end
end
private
def notify_listeners(event, record)
listeners.each { |listener| listener.send(event, record) }
end
end
# Abstract reuseable elements
class CreateOrder
include ListenerNotifer
def execute(user, attributes)
# ...
end
end
module ListenerNotifier
def listeners
@listeners ||= []
end
private
def notify_listeners(event, record)
listeners.each { |listener| listener.send(event, record) }
end
end
@krisleech
Copy link
Author

Note: multiple_listeners is now packaged as a gem: https://github.com/krisleech/wisper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment