5

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 
>> ....

6 Answers 6

6

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) }
Sign up to request clarification or add additional context in comments.

Comments

5

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] >>
  # ...
)

Comments

2

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.

Comments

1

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.

Comments

0

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]

Comments

-2

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, ...].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.