How to Properly Chain Custom Methods in Ruby

How can I properly chain custom methods in Ruby?

Are you trying to do something like this?

class SimpleMath
def initialize
@result = 0
end

#1 add function
def add(val)
@result += val
self
end

#2 Subtract function
def subtract(val)
@result -= val
self
end

def to_s
@result
end
end

newNumber = SimpleMath.new
p newNumber.add(2).add(2).subtract(1)

For any number of arguments

class SimpleMath
def initialize
@result = 0
end

#1 add function
def add(*val)
@result += val.inject(&:+)
self
end

#2 Subtract function
def subtract(*val)
@result -= val.inject(&:+)
self
end

def to_s
@result
end
end

newNumber = SimpleMath.new
p newNumber.add(1, 1).add(1, 1, 1, 1).subtract(1)

How to chain methods in ruby passing the output of one method to consecutive methods

You can leave the ()

def a s
"hello #{s}"
end

def b s
"hi #{s}"
end

puts b a "Tom"

If you have many methods :

puts [:a,:b].inject("Tom"){|result,method| self.send(method,result)}

If you want to use those methods with any object (including Classes) :

module Kernel
def chain_methods(start_value, *methods)
methods.inject(start_value){|result,method| self.send(method,result)}
end
end

class D
def a s
"hello #{s}"
end

def b s
"hi #{s}"
end
end

class E
class << self
def a s
"hello #{s}"
end

def b s
"hi #{s}"
end
end
end

# Example with instance methods
puts D.new.chain_methods("Tom", :a, :b)

# Example with class methods
puts E.chain_methods("Tom", :a, :b)

# Thanks mudasobwa :
E.chain_methods("Tom", :a, :b, :puts)

Ruby method chaining - `tap` with replacement

class Object
def as
yield self
end
end

With this, you can do [1,2,3].as{|l| l << 4}.as{|l| l << 5}

Rails active record model method chaining and getting the initial input

Class Comment < ApplicationRecord

scope :for_user, ->(u) { where(user_id: u.id) }

def self.do_thing
# How do I get the results from the chain?
# For example how do I get the IDs?
self.ids
end
end

Use self. The way scopes work is that they return an ActiveRecord::Relation object that proxies method calls back to the model class.

Although in this case the method will actually break chaining since it returns an array and not self or an ActiveRecord::Relation.

Rails Handling Nil in Class Method Chaining

I agree with @Michael Gaskill that you probably should only call the scopes that actually affect the final query (i.e. that have meaningful params).

However, if you insist on the scopes ignoring nil parameters, you may make them return current_scope instead (which is an undocumented but useful method):

def self.by_state(state)
return current_scope if state.nil?
where(addresses: {state: state})
end

Chaining Rspec Custom Matchers

Add a new method with the name of chain, which normally should return self. Typically you save the provided chained state. Which you then update the matches? method to use. This state can also be used in the various output message methods too.

So for your example:

class BeInZone
# Your code

def matches?(target)
@target = target

matches_zone? && matches_name?
end

def with_name(name)
@target_name = name
self
end

private
def matches_zone?
@target.current_zone.eql?(Zone.new(@expected))
end

def matches_name?
true unless @target_name

@target =~ @target_name
end
end

Then to use it: expect(zoneA_1).to be_in_zone(zoneA).with_name('1')

The reason this works is that you are building the object that you are passing to either the should or expect(object).to methods. These methods then call matches? on the provided object.

So it's no different than other ruby code like puts "hi there".reverse.upcase.gsub('T', '7'). here the string "hi there" is your matcher and the chained methods are called on it, passing the final object returned from gsub to puts.

The built-in expect change matcher is a good example to review.

What is the proper way to add a method to a built-in class in ruby?

tenebrousedge there was probably hinting at refinements.

Or, rather, not patching String at all. More often than not, monkeypatching creates more problems than it solves. What if String already knew alpha? and it did something different?

For example, future versions of ruby might add String#alpha? that will handle unicode properly

'新幹線'.alpha? # => true

and your code, as it is, would overwrite this built-in functionality with an inferior version. Now your app is breaking in all kinds of places, because stdlib/rails assumes the new behaviour. Chaos!

This is to say: avoid monkeypatching whenever possible. And when you can't avoid, use refinements.

Method chaining - why is it a good practice, or not?

I agree that this is subjective. For the most part I avoid method chaining, but recently I also found a case where it was just the right thing - I had a method which accepted something like 10 parameters, and needed more, but for the most time you only had to specify a few. With overrides this became very cumbersome very fast. Instead I opted for the chaining approach:

MyObject.Start()
.SpecifySomeParameter(asdasd)
.SpecifySomeOtherParameter(asdasd)
.Execute();

The method chaining approach was optional, but it made writing code easier (especially with IntelliSense). Mind you that this is one isolated case though, and is not a general practice in my code.

The point is - in 99% cases you can probably do just as well or even better without method chaining. But there is the 1% where this is the best approach.

How to decorate a method in Ruby without alias_method_chain

I would either perform subclassing (per @iain's answer) or a custom module inclusion in your own instances:

module LoggerLiner
def message(*args)
puts "="*15
super
end
end

log = Logger.new(STDOUT)
log.extend(LoggerLiner)


Related Topics



Leave a reply



Submit