Conditional Chaining in Ruby

conditional chaining in ruby

You could put your methods into an array and then execute everything in this array

l= []
l << :method_a if a
l << :method_b if b
l << :method_c if c

l.inject(object) { |obj, method| obj.send(method) }

Object#send executes the method with the given name. Enumerable#inject iterates over the array, while giving the block the last returned value and the current array item.

If you want your method to take arguments you could also do it this way

l= []
l << [:method_a, arg_a1, arg_a2] if a
l << [:method_b, arg_b1] if b
l << [:method_c, arg_c1, arg_c2, arg_c3] if c

l.inject(object) { |obj, method_and_args| obj.send(*method_and_args) }

Conditionally call chained method

This is simple and who will come after you will understand immediately:

klass = klass.foo
klass = klass.bar if a == 2
etc...

This works well if the chained methods take no arguments

klass.define_singleton_method :chain_if do |b, *s|
return unless b
klass = self
s.each do |x|
klass = klass.send x
end
klass
end

klass.foo.chain_if(true, :foo, :bar).chain_if(false, :bar)

Here some duplicated threads!

conditional chaining in ruby

Add method to an instanced object

Here I found another solution that I personally like:

my_object.tap{|o|o.method_a if a}.tap{|o|o.method_b if b}.tap{|o|o.method_c if c}

EDIT:

beware tap is defined as follows:

class Object
def tap
yield self
self
end
end

What you need might look like this, if the chained method returns a new immutable object:

class Object
def tap_and_chain
yield self
end
end

Conditional chaining in a scope

You can use scope like this here:

scope :search_provider, ->(search) {
scope = where('TRUE')
if search['organisation'].present?
scope = scope.where({ :some_condition => :some_value })
end
if search['website'].present?
scope = scope.where({ :some_condition => :some_value })
end
scope
}

Conditional chaining with ActiveRecord

I want try to clarify the purpose of doing conditional chaining this way: the idea is to hide condition inside some method, and chain methods, so resulting method will be simple.

It is possible, but child of ActiveRecord::Base class can't be chained itself. Only relation can be chained.

So instead of doing this:

def self.started_before(range_stop)
if range_stop.blank?
self
else
self.where('start_date<=?', range_stop)
end
end

one should do this:

def self.started_before(range_stop)
if range_stop.blank?
scoped
else
self.where('start_date<=?', range_stop)
end
end

Only one change: self was replaced with scoped, and now method always returns a scope, and can be chained.

Thank to this article for hint: http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods/

conditional chaining in ruby

Inject passes the return value of the block to the next iteration. Now your obj has the return value of obj.send(:method_a) after the first iteration. Fix your inject like this:

l.inject(some) { |obj, method|
obj.send(method)
obj
}

Why does Ruby use its own syntax for safe navigation operator?

This answer is based on the discussion of the feature request in Ruby's issue tracking. According to Ruby's author Yukihiro Matsumoto it wouldn't be possible to introduce operator ?. in Ruby because foo? is valid method name and thus it couldn't be parsed. The first candidate for operator was reversed sequence .?. That syntax was already implemented (by Nobuyoshi Nakada) but was later discarded as it was thought to be too close to original syntax introduced by the other languages (that was not feasible as mentioned earlier). The final syntax &. was accepted as suggested by Matsumoto.

Here's the justification for this syntax given by Matsumoto

I think about this for a while, and thinking of introducing &. instead of .?, because:

  • .? is similar to ?. in Swift and other languages, but is different anyway.
  • Since ? is a valid suffix of method names in Ruby, we already see a lot of question marks in our programs.
  • u&.profile reminds us as short form of u && u.profile.

But behavior of &. should be kept, i.e. it should skip nil but recognize false.

This syntax was then released as part of Ruby 2.3.0-preview1.

Ruby—Nice way to chain lots of OR statements? (checking against array out-of-bounds)

First off, use of or and and over || and && in conditions is not idiomatic in Ruby since they have different precedence and may not always do what you want (Style Guide reference).
As for the actual question, something like this is more idiomatic Ruby:

(0...minemap.length).cover?(x) && (0...minemap[0].length).cover?(y)

This uses Range#cover? to check that x and y are inside the correct ranges and returns false unless that is true.

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)

Best way to conditionally chain scopes

Since relations are chainable, it's often helpful to "build up" your search query. The exact pattern for doing that varies widely, and I'd caution against over-engineering anything, but using plain-old Ruby objects (POROs) to build up a query is common in most of the large Rails codebases I've worked in. In your case, you could probably get away with just simplifying your logic like so:

relation = Order.join(:store)

if params[:store_id]
relation = relation.store(params[:store_id])
end

if params[:status]
relation = relation.status(params[:status])
end

@orders = relation.all

Rails even provides ways to "undo" logic that has been chained previously, in case your needs get particularly complex.

Conditionally chaining where clauses in Rails ActiveRecord queries

Queries aren't strings; they're query objects. So you want something like

query = YourModel.scoped # Rails 3; in Rails 4, use .all
if params.has_key?('size')
query = query.where(size: params['size'])
end

etc.



Related Topics



Leave a reply



Submit