Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency injection #70

Closed
fidalgo opened this issue Jul 3, 2022 · 2 comments · Fixed by #74
Closed

Dependency injection #70

fidalgo opened this issue Jul 3, 2022 · 2 comments · Fixed by #74
Labels
enhancement New feature or request

Comments

@fidalgo
Copy link

fidalgo commented Jul 3, 2022

First of all, thank you for the work in this lib! I've found it very useful and I'm giving it a go in a personal project to play around.
I've found a hurdle that I would appreciate if you can provide some tips on how to proceed, hopefully, it could help others as well.

I see play as a way of dependency injection, although inside of an actor I would like to call another actor and perform some logic basic on inputs and outputs.

class ServiceClient < Actor
  input :token, allow_nil: false
  input :term, allow_nil: false

  def call
    Faraday.new('http://example.com/service').get("", query: term)
  end
end
class Persistor < Actor
  input :client, default: ServiceClient
  
  def class
    outcome = ServiceClient.result(token: 'token', term: 'actor')
    puts outcome.status   if outcome.success?
  end
end

The problem is when I use input :client, default: ServiceClient it tries to initialize the class and then failed because of the missing required arguments. If I move provider as a private method it works.

Is there any way to achieve dependency injection?

@sunny
Copy link
Owner

sunny commented Jul 5, 2022

Hi there! I haven’t used actors for dependency injection, so I hadn’t encountered this use-case before. It looks very versatile though, I can think of a few places where this would be very handy in my code!

The issue here is that in order to handle dynamic defaults, the default: keyword tries to trigger .call on whatever is given to it (if it responds to that method). This works well for lambdas, but not so much for actors themselves.

Therefore, one way to fix this is to define the default inside a lambda, as so:

input :client, default: -> { ServiceClient }

The full solution for your use-case would be:

class ServiceClient < Actor
  input :token, allow_nil: false
  input :term, allow_nil: false

  output :outcome

  def call
    self.outcome = Faraday.new('http://example.com/service').get("", query: term)
  end
end

class Persistor < Actor
  input :client, default: -> { ServiceClient }
  
  def call
    outcome = client.result(token: 'token', term: 'actor').outcome
    puts outcome.status if outcome.success?
  end
end

@sunny sunny added the enhancement New feature or request label Jul 16, 2022
@sunny sunny closed this as completed in #74 Jul 18, 2022
@sunny
Copy link
Owner

sunny commented Jul 18, 2022

@fidalgo I have simplified this a bit, so that in v3.3.0 you can now use input :client, default: ServiceClient 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants