Skip to content

Instantly share code, notes, and snippets.

@Spaceghost
Forked from banister/user_creator.rb
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Spaceghost/25b9ff8616b3d35f8d39 to your computer and use it in GitHub Desktop.
Save Spaceghost/25b9ff8616b3d35f8d39 to your computer and use it in GitHub Desktop.
commit 95bcc8ac6463e63001b33094ede2e21796da6c23
Author: Johnneylee Jack Rollins <Johnneylee.Rollins@gmail.com>
Date: Mon Jun 30 07:17:59 2014 -0700
Add Callable and Bindable for services/controllers
* Callable depends on the extending class implementing #call as an
instance method. Another instance method name might be preferable, I
don't know. I've even seen fun dynamic things like `extend
Callable.call_method(:execute)`
* Bindable handles adding listeners with #bind as well as doing too much
with implementing #success and #failure. These depend on any object
calling #bind also implementing these methods.
The virtues of this kind of decoupling is that there's two separate
minimal APIs, those being Callable and Bindable. The collaborators are
fully decoupled from each other and will only receive hashes of data
that /should/ be fit for some kind of consumption. At this point, I
would add view objects, but that's not important here.
The coupling moves from interface to data, but only temporarily until I
find a nice way to pass data from a controller to a kind of locator
object that gets view objects and injects them with data based on what I
assume would be hashes or a hash interface.
With all that said, please disregard the coupling of the actual response
hash. If you'd like to see what I'd do to clean that up, I'd love to
show you.
Signed-off-by: Johnneylee Jack Rollins <Johnneylee.Rollins@gmail.com>
commit 8c602fce61d92189afe0b5845761c4bbba47fedb
Author: Johnneylee Jack Rollins <Johnneylee.Rollins@gmail.com>
Date: Mon Jun 30 08:33:10 2014 -0700
Clean Callback interface
* Removing the unnecessary callback methods #successful and #failure
allows the controller in this example to pass blocks which are called
in the context of the controller closure style from within the
response data from the service object.
I included an example where the controller would call a method on some
objects returned within the response data, which I feel a bit unsure
about, but wanted to show some flexibility.
My goal here is to have the controller extract data useful for forming
the http response. Perhaps the service or application entrypoint could
return some kind of status, be it a 1:1 mapping to HTTP or not, and a
dependency on #errors. Perhaps a proper response object is in order, one
that implements a hash like interface as well as implements methods such
as #errors and #status. The rest should be passed to objects responsible
for rendering the rest of the data.
Signed-off-by: Johnneylee Jack Rollins <Johnneylee.Rollins@gmail.com>
module Bindable
def bind(*subscribers, &block)
subscribers.each do |subscriber|
subscriber.tap(&block)
end
end
end
module Callable
def call(*args, **kwargs)
raise NotImplementedError, "#{self.inspect} must implement #call" unless self.instance_methods.include?(:call)
new.call(*args, **kwargs)
end
alias_method :call, :[]
end
class UserCreator
extend Callable
include Bindable
def call(**attributes)
user = User.new(attributes).save
if user.errors.none?
success(user: user)
else
failure(user: user)
end
end
end
class UsersController < ApplicationController
def create
UserCreator.call(user_params).bind(self) do |data|
{ head :bad_request } unless data[:orders]
{ redirect_to ticket_thank_you_path(invoice_id: invoice_id) } if data[:orders].any? &:free?
{ redirect_to checkout_orders_path, flash: { errors: data[:errors] } } if data[:errors].any?
end
end
private
def user_params
params.require(:user).permit(:foo, :bar)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment