Hi David,

Just my own experience, DI as I understand it isn't something you add in
later, as the project grows, it can be as fundamental as injecting a clock,
and never calling a method on an object you weren't passed in, seen another
way you could take it as far as expecting to be able to simply `call` (e.g
`to_proc`) on any object, passing them arround and just calling it, to give
an example (somewhat contrived):

Here's some takeaways:


   - *Sometimes you want to pass in a *class* and let your objects
   construct it with their own params*. (UserValidator, in this case), it's
   implemented this way so that we can stick to "do all DI in initialize, call
   call without params" (which might be a bridge too far, but I like it)
   - *Even mundane things like the system clock are worth DI'ing.* You
   might think not, until suddenly someone sneaks TimeCop into your app and
   now you're thinking "*van Damme, I hate global state.*"
   - *I make liberal use of BasicObject in these Ruby styles, because a
   typical Ruby "Object" comes with ~200 methods which have *zero* meaning to
   your app.* By making a BasicObject you severely restrict what people can
   call on the things you pass around, and this makes refactoring and safe and
   sane testing much simpler. It's also a nice toy in Pry/IRB when you call
   "something.methods" and get a list of precisely two things.
   - *I hate the implementation of UserStore here.* I normally would split
   it into UserStore and UserRetriever, but to keep the example small, and to
   give myself a chance to demonstrate passing in the UserValidatorType (and
   the UserSignupHandler constructing it's own dependency's constructor) I did
   a simple "Store" type which violates SRP for me. I've worked in a lot of
   apps where storing, and retrieving things work very differently (think on
   retrieval wanting to use views and caching)

class UTCClock < BasicObject
def now; return Time.now.utc; end
end
class StaticClock < BasicObject
def now; return Time.new(1970); end
end
# This is a service object, it's included to
# demonstrate the ".call() everything" mantra
class UserSignupHandler < BasicObject
def initialize(user:, clock:, user_store:, user_validator_class:)
@user, @clock, @user_store, @user_validator = user, clock, user_store,
user_validator_class
end
def call
if r = @user_validator_class.new(@user, @user_store).call; not r.success?
return E_USER_CAN_T_BE_CREATED # (actually pass `r` back up the stack,
# and have the railsish frontend know how
# to deal with "result" types)
end
return @user_store.store(@user) # expect that this returns the user with
it's db pkey filled, for example
end
end
class UserValidator < BasicObject
def initialize(user:, user_store:)
@user, @user_store = user, user_store
end
def call
# validation here, return some kind of ResultObject with #errors
# and #can_continue?
res = ResultObject.new
if @user.name.length < 3
res.errors.push E_USER_ALREADY_EXISTS
end
if @user_store.find_by_email(@user.email)
res.errors.push E_USER_ALREADY_EXISTS
end
return res
end
end
# Somewhere in a rails App
class ApplicationController < ActionController::API
def signup
# check METHOD for `POST`
# get params, rely on strong params to do preliminary validation, etc
user_store = UserStore.new($dbBackend, $cacheBackend)
clock = UTCClock.new
render json: UserSignupHandler.new(User.new(params), user_store, clock,
UserValidator).call
end
end

​Code also in this gist (
https://gist.github.com/leehambley/8b4e17e95520058a3708f011939e7816), I
hope the ML doesn't eat the formatting here, it's nice to have coloured
code in an email :)

Hope this helps, at least this is my understanding of DI informed by having
done a lot of functional, and typesafe coding in other languages (and the
functional stuff also in Ruby) , partly including stuff I've learned from
Jim Gay's book on "Clean Ruby" but I take some of what he does a little bit
further


Lee Hambley
http://lee.hambley.name/
+49 (0) 170 298 5667

On 12 July 2016 at 14:02, David Craddock <[email protected]> wrote:

> We are getting to the stage on the product I'm working on, that it might
> be useful to use Dependency Injection.
> https://en.wikipedia.org/wiki/Inversion_of_control
>
> What do you fellow Rubyists think about using DI in Ruby? Are there any
> frameworks or gems that can help this?
>
> --
> You received this message because you are subscribed to the Google Groups
> "North West Ruby User Group (NWRUG)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send an email to [email protected].
> Visit this group at https://groups.google.com/group/nwrug-members.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"North West Ruby User Group (NWRUG)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send an email to [email protected].
Visit this group at https://groups.google.com/group/nwrug-members.
For more options, visit https://groups.google.com/d/optout.

Reply via email to