Jan 09, 2019
Simpler parameter handling for Interactors
At BuyCoins, we use the Interactor gem for business logic. It works like this:
Say you have an Interactor called SendCoins. You call can trigger it’s functionality by doing
SendCoins.call(amount: 0.5, currency: Ethereum…)
The has passed into #call will be available within the Interactor as attributes of an object called context.
def call amount = context.amount end
As your Interactors become more complex and as such accept more parameters, it can become tedious to do this every time for each Interactor.
def call amount = context.amount currency = context.currency address = context.address … etc etc
The first step to eliminating this annoying repetitiveness was revealed to me by Alessandro Desantis. He realized that the delegate method was perfect.
Delegate is included in ActiveSupport and as such, in every rails application.
It is a pretty simple method but it’s very useful.
delegate :amount, :currency, :address, to: :context
What this does is; whenever amount is called on (or within the interactor), the method call is literally delegated to context and shall return what context.amount returns.
To make it a bit neater, I decided to go a step further.
All our Interactors are subclasses of ApplicationInteractor. There’s some shared logic in that file related to failures and error reporting.
Decided to add this method to it.
def self.parameters(*params) delegate *params, to: : context end
This way, in your Interactors, all you need to do is.
parameters: :amount, :currency, :address
Which kind of reminds me of attr_reader.
That’s all folks.
Update (June 8, 2019)
The above code only lets you read the parameters but doesn’t let you write them.
The code below adds to the self.parameters method to make that happen.
def self.parameters(*parameters)
delegate *parameters, to: :context
parameters.each do |parameter|
method_name = "update_#{parameter}"
define_method(method_name) do |new_value|
context.send("#{parameter}=", new_value)
end
end
endThis is a simple bit of metaprogramming that uses define_method to dynamically create update_* methods for every parameter.
Doing this:
parameters: :amount, :currency, :address
Lets you update the values of context.amount, context.currency and context.address like this:
update_amount 0.2 update_currency Bitcoin update_address "bc1q7tmpy0pqn4mzu02h3ywv7a4t98xdepxgym6tr5"