Dry-transaction injection

dry-transaction isn’t designed out of the box to automatically handle injection of arbitrary dependencies — it’s designed to help you compose multiple operations to run as steps in a sequence, and it will help you inject those objects.

e.g.

class MyTransaction
  step :one_thing
  step :another_thing
end

# inject operation object for "one_thing" step
my_trans = MyTransaction.new(one_thing: -> input { do_something })
my_trans.("input")

Now, injecting other things into a transaction object is a different matter. I’ve never tried it myself, which is why I couldn’t give you a quick answer to your questions.

I’ve just looked into it now, and it seems like it’s now possible with the initialize that dry-transaction provides. I’m going to file a couple of issues for this, since it’s something that I would like to make possible, eventually.

Sorry this doesn’t help you in the short term, though.

May I make one suggestion, though? I don’t think you need a transaction in this case. I certainly wouldn’t use one for an object like the one you shared. I think it’s perfectly fine to build up that kind of logic by hand, for example:

require "dry-auto_inject"
require "dry-monads"

Import = Dry::AutoInject(MyContainer)

class CreateArticle
  # dry-auto_inject can take care of importing this for you
  include Import[article_repo: "repositories.articles"]

  include Dry::Monads::Result::Mixin

  def call(input)
    validation = ArticleSchema.(input)

    if validation.success?
      article = article_repo.create(validation.to_h)
      Success(article)
    else
      Failure(validation.errors)
    end
  end
end

This is exactly like how I build up standard CRUD operation objects in the apps I build. I think it’s more obvious than the transaction approach and will more easily support adding in nuanced operation-specific behaviour in future.