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 ofu && u.profile
.But behavior of
&.
should be kept, i.e. it should skipnil
but recognizefalse
.
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
Error When Doing Rake Db:Migrate on Heroku
How to Validate a Non-Model Form in Rails 3
Rails Simpleform with Non-Model Inputs
Retrieving a Braintree Customer's Subscriptions
Private Class (Not Class Method) in a Ruby Module
How to Make a Ruby String Safe for a Filesystem
Modify Ruby Hash in Place( Rails Strong Params)
Why Does Ruby on Rails Use Http://0.0.0.0:3000 Instead of Http://Localhost:3000
Is There a Less Intrusive Alternative to Rspec's 'Should_Receive'
Gem Install Pg Can Not Bind to Libpq
Writing Over Previously Output Lines in the Command Prompt with Ruby
Error While Installing Ruby Using Rvm
Repairing Postgresql After Upgrade to Osx Mavericks
Error While Trying to Load the Gem 'Devise. Activesupport: Duration Can't Be Coerced into Integer