Reputation: 149
What is the best way of chaining multiple custom methods together? I want to put the output of my method directly into the next method in an elegant way. The first one below is what I have now.
verified = search_verified(@providers)
matching = search_matching(verified)
deactivated = search_deactivated(matching)
networks = search_networks(deactivated)
designations = search_designations(networks)
disorders = search_disorders(designations)
age_groups = search_age_groups(disorders)
governing = search_governing(age_groups)
search_availabilities(governing)
maybe something more along the lines of:
search_verified(@providers)
>> search_matching
>> search_deactivated
>> search_networks
>> ....
Upvotes: 5
Views: 630
Reputation: 21110
You can do something along those lines. Ruby 2.6.0 introduces then
, and the function composition operators <<
and >>
.
You do have to select your methods with method(:method_name)
because method_name
by itself invokes the method and does not return it.
@providers.then(&
method(:search_verified) >>
method(:search_matching) >>
method(:search_deactivated) >>
method(:search_networks) >>
method(:search_designations) >>
method(:search_disorders) >>
method(:search_age_groups) >>
method(:search_governing) >>
method(:search_availabilities)
)
If you don't like the character "overhead". You could shrink the amount of characters by storing the method
method in a shorter variable first:
fn = method(:method)
@providers.then(&
fn[:search_verified] >>
# ...
)
Upvotes: 5
Reputation: 3371
I've not actually tried this other than a quick attempt in the console, but from looking at: https://andersmurphy.com/2019/12/07/ruby-functional-programming.html it looks like something like the following should be possible:
pipe = -> *fns {fns.reverse.reduce {|f, g| -> x {f.(g.(x))}}}
add_one = -> x {x + 1}
times_two = -> x {x * 2}
add_one_and_times_two = pipe.(add_one, times_two)
add_one_and_times_two.(2)
=> 6
pipe.(add_one, times_two).(3)
=> 8
If you want to use this with methods you can possibly (this seems to work in the console) do something like:
def divide_by_three(x); x / 3.0 end
pipe.(
add_one,
times_two,
method(:divide_by_three)
).(4)
=> 3.3333333333333335
using the method function as shown in @3limin4t0r's answer.
Upvotes: 1
Reputation: 42182
If all the methods are in one class you can chain these methods by returning self in each method. For the sake of clarity I take your example but the providers are just numbers.
class MyClass
def initialize
@@providers = [2, 6, 4, 8, 7]
end
def search_matching
# do stuff
@@providers.select!{ |n| n > 3 }
self
end
def search_deactivated
# do other stuff
@@providers.select!{ |n| n < 8 }
self
end
def providers
@@providers
end
end
MyClass.new.search_matching.search_deactivated.providers # [6, 4, 7]
Upvotes: 0
Reputation: 110665
You could write that as follows.
METHODS = [:verified, :matching, :deactivated, :networks, :designations,
:disorders, :age_groups, :governing, :search_availabilities]
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| send(meth, arg) }
end
This would be called
doit(@providers)
For example:
def a(arg)
arg * 2
end
def b(arg)
arg + 1
end
def c(arg)
arg/5.0
end
METHODS = [:a, :b, :c]
doit(3)
#=> 1.4
One could alternatively write
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| method(meth).call(arg) }
end
doit(3)
#=> 1.4
One advantage of this approach is that if methods are added, removed or renamed is is only necessary to change the constant METHODS
; the method doit
is unaffected.
Upvotes: 2
Reputation: 106792
You might want to use then
to chain your methods and numbers parameter to simplify the blocks:
search_verified(@providers)
.then { search_matching(_1) }
.then { search_deactivated(_1) }
.then { search_networks(_1) }
.then { search_designations(_1) }
.then { search_disorders(_1) }
.then { search_age_groups(_1) }
.then { search_governing(_1) }
.then { search_availabilities(_1) }
Upvotes: 6
Reputation: 1
it depends on what data do you really need and how you define the architecture of your code, i usually make a Service object.
if you only want to return only the last method output. return search_availabilities(governing)
.
If you need all variables, you can make it return an array with all the variables or the ones that you need. return [verified, matching, deactivated, ...]
.
Upvotes: -2