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
In a Sinatra App on Heroku, Session Is Not Shared Across Dynos
Start Using Ruby on Rails, Web Services and Oauth
How to Mock Aws Sdk (V2) with Rspec
Unpacking/Freezing Gems into a Non-Rails Ruby App
Ruby on Rails Named Scope Implementation
Rvm: How to Use Gems from a Different Ruby
Ruby on Rails with Imap Idle for Multiple Accounts
How to Negate a Scope in Rails
Programmatically Getting Full Ruby Version
How to Make a Before_Save Conditional
Elasticsearch Terms Aggregation by Entire Field
File.Open with Block VS Without
Difference Between As_JSON and To_JSON Method in Ruby
How to Enter Password in a Popup Using Watir
Equivalent of Iconv.Conv("Utf-8//Ignore",...) in Ruby 1.9.X
Rails 3 - Best Way to Handle Nested Resource Queries in Your Controllers
Rails 4 Strong Parameters Failing When Creating Instances in Rails Console