Technique to inject dependencies in Ruby without using DI frameworks
Looks fine to me. Classes are objects in Ruby, so dependency injection is just setting a variable at runtime. In your case, you are setting the @http_client
instance variable of the AppClient
class. Once that variable is accessed by the code that needs the implementation, the class you configured the application with will be returned and it will do the job as long as it responds to the correct messages.
Be careful, though. Remember that classes are singleton objects that are shared within a single Ruby process. Other code could call that accessor method and change that variable as well. This could happen at any point, not just at start up, and will affect all code that uses the implementation. The potential for conflict exists.
This makes this pattern specially unsuitable for use in libraries. Passing the implementation class as a parameter on the call site should be a more robust solution in that case.
Need a simple explanation of the inject method
You can think of the first block argument as an accumulator: the result of each run of the block is stored in the accumulator and then passed to the next execution of the block. In the case of the code shown above, you are defaulting the accumulator, result, to 0. Each run of the block adds the given number to the current total and then stores the result back into the accumulator. The next block call has this new value, adds to it, stores it again, and repeats.
At the end of the process, inject returns the accumulator, which in this case is the sum of all the values in the array, or 10.
Here's another simple example to create a hash from an array of objects, keyed by their string representation:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
In this case, we are defaulting our accumulator to an empty hash, then populating it each time the block executes. Notice we must return the hash as the last line of the block, because the result of the block will be stored back in the accumulator.
Ruby/Rails Dependency Injection
Move initialization of Saw
to the default argument.
class Builder
def saw(saw = Saw.new(4))
@saw = saw
end
def cut_wood
Saw.saw
end
end
Builder#saw
supports dependency injection now.
Remember to remove attr_reader :saw
from your code because it's being overridden by your custom reader.
What's the best practice to achieve dependency injection with ruby-graphql?
As you've mentioned, injection through the initializer might not be super straight forward, so if you want to go fully into dependency injection and inversion of control, you could leverage an IOC Container library like Dry Auto Inject. I know it might be a full blown solution, and it could possibly be too heavy handed for your use case (not sure), but since you're already using repositories in Ruby, it might not be.
Does dependency injection exist in Rails?
Dependency injection is usually unnecessary with Ruby. Jamis Buck blogged extensively about the reasons why. Well worth a read.
Inject a line of code into an existing method at runtime
The reason I need this is so that I can use the Ruby Tracepoint Class to extract some run time information about the method signature.
Specifically the default parameter values in b = 1 and d: 1
TracePoint
allows you to fetch information without patching the method. You just have to set up a :call
hook and then call the method:
TracePoint.trace(:call) do |tp|
tp.parameters.each do |type, name|
value = tp.binding.local_variable_get(name)
puts format('%8s: %s = %s', type, name, value.inspect)
end
end
A.new.some_method(123)
Output: (omitting the method's output)
req: a = 123
opt: b = 1
rest: c = []
key: d = 1
keyrest: e = {}
Related Topics
How to Merge Two Equally Sized Arrays into One Array with Sub-Arrays of Merged Values
How to Remove Devise Password Resetting During Email Confirmation
Iterate and Set Ruby Object Instance Variables
Module and Class with the Same Name in Rails Project
Ruby Roo Loaderror: Cannot Load Such File -- Spreadsheet/Note
Access Image from Different View in a View with Paperclip Gem Ruby on Rails
Knowing When Resque Worker Had Completed Job
Boot Up Rails App, Make Request to App from Outside Local Network
Split String into Equal Slices/Chunks
Method Gives Activerecord::Relation Error
Ruby Net-Ssh Calling Bash Script with Interactive Prompts
How to Return the Number of Devise Users Currently Logged In