Compare the Content, Not the Results, of Procs - ruby

Using Ruby 1.9.2
Problem
Compare the content, not the results, of two procs. I understand the results can't be tested because of the halting problem but that's OK; I don't want to test the results anyway.
For instance
proc {#x == "x"} == proc {#x == "x"} => false # doh!
That returns false because the objects inside the procs are not the same.
My clunky solution
I have a work around solution that kinda sorta does what I want but it doesn't really test that the proc is "equal" to what I put in it. In my specific case the format of my procs will always be boolean tests on instance variables like this:
{#x == "x" && #y != "y" || #z == String}
I wrote a method that builds classes dynamically and creates instance variables set to specified values:
def create_proc_tester(property_value_hash)
new_class = Class.new.new
new_class.class.class_eval do
define_method(:xql?) { |&block| instance_eval &block }
end
property_value_hash.each do |key, value|
new_class.instance_variable_set("##{key}", value)
end
new_class
end
Which could be used something like this:
class Foo
attr_accessor :block
end
foo = Foo.new
foo.block = proc {#x == "x" && #y != "y" || #z == String}
tester = create_proc_tester(:x => "x", :y => "y", :z => Fixnum)
puts "Test #1: #{tester.xql? &foo.block}"
tester = create_proc_tester(:x => "x", :y => "x", :z => String)
puts "Test #2: #{tester.xql? &foo.block}"
> Test #1: false
> Test #2: true
.
.
That's all great and wonderful but I want to know if there is a better, more meta, way to do this that actually tests the contents of the proc not just a work around that solves my specific problem; something that could be used to test any proc.
I was thinking there might be a way to use the Ruby parser to get something to compare but I have no idea how. I'm researching it now but thought I'd try to see if anyone here has done this before and knows how. That might be a dead-end though because of the dynamic nature of Ruby but that's where I'm looking now.

If you're using Ruby 1.9, you may be able to use the sourcify gem.
$ irb
> require 'sourcify'
=> true
> a = proc {#x == "x"}
=> #<Proc:0x9ba4240#(irb):2>
> b = proc {#x == %{x}}
=> #<Proc:0x9ba23f0#(irb):3>
> a == b
=> false
> a.to_source == b.to_source
=> true
> RUBY_VERSION
=> "1.9.2"
We also ran into the ParseTree/Ruby 1.9 incompatibility problem at my company.

$ sudo gem install ruby2ruby ParseTree
require 'parse_tree'
require 'ruby2ruby'
require 'parse_tree_extensions'
# All of these are the same:
proc { puts 'a' }.to_ruby # => "proc { puts(\"a\") }"
lambda { puts "a" }.to_ruby # => "proc { puts(\"a\") }"
Proc.new { puts %{a} }.to_ruby # => "proc { puts(\"a\") }"
# If you need to do this with classes:
class Bar; define_method(:foo) { 'a' }; end
puts Ruby2Ruby.new.process(Unifier.new.process(ParseTree.translate(Bar)))
# will print this:
# class Bar < Object
# def foo
# "a"
# end
# end

Related

Ruby: methods as array elements - how do they work?

This probably isn't something you should try at home, but for some reason or another I tried to create an array of methods in Ruby.
I started by defining two methods.
irb(main):001:0> def test1
irb(main):002:1> puts "test!"
irb(main):003:1> end
=> nil
irb(main):004:0> def test2
irb(main):005:1> puts "test2!"
irb(main):006:1> end
=> nil
The weird thing happens when you try to put it into an actual array. It seems to run both methods.
irb(main):007:0> array = [test1, test2]
test!
test2!
=> [nil, nil]
And afterwards, the array is empty.
irb(main):008:0> puts array
=> nil
Can someone explain to me why it runs the methods? Other than that the whole excercise is seriously in need of an exorcist?
What you're storing in your array is the result of calling your methods, not the methods themselves.
def test1
puts "foo!"
end
def test2
puts "bar!"
end
You can store references to the actual methods like this:
> arr = [method(:test1), method(:test2)]
# => [#<Method: Object#test1>, #<Method: Object#test2>]
Later, you can call the referenced methods like this:
> arr.each {|m| m.call }
foo!
bar!
#alestanis explained the reason well. If you were trying to store the methods, then you can do what Lars Haugseth says or you could do the folllowing:
test1 = Proc.new { puts "test!" }
test2 = Proc.new { puts "test2!" }
a = [test1, test2]
This may make your code much more readable.
Here is an irb run.
1.9.3p194 :009 > test1 = Proc.new { puts "test!" }
=> #<Proc:0x00000002798a90#(irb):9>
1.9.3p194 :010 > test2 = Proc.new { puts "test2!" }
=> #<Proc:0x00000002792988#(irb):10>
1.9.3p194 :011 > a = [test1, test2]
=> [#<Proc:0x00000002798a90#(irb):9>, #<Proc:0x00000002792988#(irb):10>]
Your array never contains anything else than two nil values. I tricks you by putting the strings when evaluating. But the return value of each function still is nil.
Your code runs the two methods because you're actually calling the methods when you say "test1" and "test2" - parentheses are optional for ruby method calls.
Since both of your methods just contain a "puts", which returns nil, your resulting array is just an array of two nils.
If you had a square method and wanted to create an array with the square values of 2 and 4, you would write
array = [square(2), square(4)]
Here you are doing exactly the same thing, except that your test methods don't return anything and that's why your final array seems empty (actually, it contains [nil, nil]).
Here's my two-pennies worth. Building on the solutions already posted, this is an example of a working example. What might be handy for some here is that it includes method arguments and the use of self (which refers to the instance of the PromotionalRules class when it is instantiated) and the array of symbols, which is neat - I got that from the Ruby docs on the #send method here. Hope this helps someone!
class PromotionalRules
PROMOTIONS = [:lavender_heart_promotion, :ten_percent_discount]
def apply_promotions total, basket
#total = total
if PROMOTIONS.count > 0
PROMOTIONS.each { |promotion| #total = self.send promotion, #total, basket }
end
#total.round(2)
end
def lavender_heart_promotion total, basket
if two_or_more_lavender_hearts? basket
basket.map { |item| total -= 0.75 if item == 001 }
end
total
end
def two_or_more_lavender_hearts? basket
n = 0
basket.each do |item|
n += 1 if item == 001
end
n >= 2
end
def ten_percent_discount total, *arg
if total > 60.00
total = total - total/10
end
total
end
end
Thanks to everyone for their help. I love the open-source nature of coding - threads just get better and better as people iterate over each other's solutions!

ruby syntactic sugar for core only arg parsing

Syntactic sugar for counting the non-switch args?
argn = 0
ARGV.each do |arg|
mission = Mission_DB.new unless mission
if(arg.match(/^\-.*=/)) then
fsav = arg.split('=')[1] if arg.match(/\-save=/)
skip = arg.split('=')[1].split(',') if arg.match(/\-skip=/)
mission.mission = arg.split('=')[1].downcase if arg.match(/\-mission=/)
else
argn += 1
mission.parseP1SCLRV(arg) if argn == 1
template = arg if argn == 2
mission.parseP1SCLRVPLV(arg) if argn == 3
mission.parseDbUserLimits(arg) if argn == 4
end
end
I've tried to find a reusable, clean core only arg parsing syntax that is intuitive (readable without comments). This isn't bad ... but its not best good enough.
I hate the argn crap
I hate the if /-.*=/ followed by each individual switch
each_with_index won't really accomplish what I'm looking for as switches could be embedded anywhere within the non switched args
Feel free to revamp entirely provided we are sufficing the "clean, core only, intuitive" requirements.
Thanks and Cheers --Reed
Have you considered using optparse? It comes with the Ruby standard library.
Here's a class that uses optparse to do option parsing for the arguments in your example. It handles errors, prints its usage if "-h" or "--help" is given, and encapsulates the parsed arguments into a nice class you can pass around:
require 'optparse'
class Arguments
POSITIONAL_ARGS = %w(p1sclrv template p1sclrvplv db_user_limits)
attr_reader :mission
attr_reader :save_path
attr_reader :what_to_skip
attr_reader *POSITIONAL_ARGS
def initialize(argv)
option_parser.parse!(argv)
POSITIONAL_ARGS.each do |positional_arg|
value = argv.shift
instance_variable_set("##{positional_arg}", value)
raise OptionParser::MissingArgument, positional_arg.to_s unless value
end
raise OptionParser::NeedlessArgument, argv.first unless argv.empty?
rescue OptionParser::ParseError => e
puts e
puts option_parser
exit(1)
end
private
def option_parser
OptionParser.new do |op|
op.banner += ' ' + POSITIONAL_ARGS.join(' ')
op.on('--save=PATH', 'Save to PATH') do |value|
#save_path = value
end
op.on('--skip=WHAT', 'Skip WHAT') do |value|
#what_to_skip = value
end
op.on('--mission=NAME', 'Do mission NAME') do |value|
#mission = value
end
end
end
end
In real use, pass ARGV to it:
args = Arguments.new(ARGV)
Here's an example passing a made-up ARGV and printing the parsed arguments:
args = Arguments.new(%w(--skip=FOO alpha bravo charley delta))
p args.p1sclrv # => "alpha"
p args.template # => "bravo"
p args.p1sclrvplv # => "charley"
p args.db_user_limits # => "delta"
p args.mission # => nil
p args.save_path # => nil
p args.what_to_skip # => "FOO"
Here's what the help looks like:
Arguments.new(%w(--help))
# => Usage: foo [options] p1sclrv template p1sclrvplv db_user_limits
# => --save=PATH Save to PATH
# => --skip=WHAT Skip WHAT
# => --mission=NAME Do mission NAME
My own iterating:
fsav = nil
skip = []
template = p1sclrv = p1sclrvplv = dbuserlimits = nil
ARGV.each do |arg|
case arg
when /\-save=/ then fsav = arg.split('=')[1]
when /\-skip=/ then skip = arg.split('=')[1].split(',')
when /\-mission=/ then mission.mission = arg.split('=')[1].downcase
else
unless(p1sclrv) then p1sclrv = arg; next; end
unless(template) then template = arg; next; end
unless(p1sclrvplv) then p1sclrvplv = arg; next; end
unless(dbuserlimits) then dbuserlimits = arg; next; end
end
end
EDIT: Getting better.

Accessing elements of nested hashes in ruby [duplicate]

This question already has answers here:
Ruby Style: How to check whether a nested hash element exists
(16 answers)
How to avoid NoMethodError for nil elements when accessing nested hashes? [duplicate]
(4 answers)
Closed 7 years ago.
I'm working a little utility written in ruby that makes extensive use of nested hashes. Currently, I'm checking access to nested hash elements as follows:
structure = { :a => { :b => 'foo' }}
# I want structure[:a][:b]
value = nil
if structure.has_key?(:a) && structure[:a].has_key?(:b) then
value = structure[:a][:b]
end
Is there a better way to do this? I'd like to be able to say:
value = structure[:a][:b]
And get nil if :a is not a key in structure, etc.
Traditionally, you really had to do something like this:
structure[:a] && structure[:a][:b]
However, Ruby 2.3 added a method Hash#dig that makes this way more graceful:
structure.dig :a, :b # nil if it misses anywhere along the way
There is a gem called ruby_dig that will back-patch this for you.
Hash and Array have a method called dig.
value = structure.dig(:a, :b)
It returns nil if the key is missing at any level.
If you are using a version of Ruby older than 2.3, you can install a gem such as ruby_dig or hash_dig_and_collect, or implement this functionality yourself:
module RubyDig
def dig(key, *rest)
if value = (self[key] rescue nil)
if rest.empty?
value
elsif value.respond_to?(:dig)
value.dig(*rest)
end
end
end
end
if RUBY_VERSION < '2.3'
Array.send(:include, RubyDig)
Hash.send(:include, RubyDig)
end
The way I usually do this these days is:
h = Hash.new { |h,k| h[k] = {} }
This will give you a hash that creates a new hash as the entry for a missing key, but returns nil for the second level of key:
h['foo'] -> {}
h['foo']['bar'] -> nil
You can nest this to add multiple layers that can be addressed this way:
h = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = {} } }
h['bar'] -> {}
h['tar']['zar'] -> {}
h['scar']['far']['mar'] -> nil
You can also chain indefinitely by using the default_proc method:
h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
h['bar'] -> {}
h['tar']['star']['par'] -> {}
The above code creates a hash whose default proc creates a new Hash with the same default proc. So, a hash created as a default value when a lookup for an unseen key occurs will have the same default behavior.
EDIT: More details
Ruby hashes allow you to control how default values are created when a lookup occurs for a new key. When specified, this behavior is encapsulated as a Proc object and is reachable via the default_proc and default_proc= methods. The default proc can also be specified by passing a block to Hash.new.
Let's break this code down a little. This is not idiomatic ruby, but it's easier to break it out into multiple lines:
1. recursive_hash = Hash.new do |h, k|
2. h[k] = Hash.new(&h.default_proc)
3. end
Line 1 declares a variable recursive_hash to be a new Hash and begins a block to be recursive_hash's default_proc. The block is passed two objects: h, which is the Hash instance the key lookup is being performed on, and k, the key being looked up.
Line 2 sets the default value in the hash to a new Hash instance. The default behavior for this hash is supplied by passing a Proc created from the default_proc of the hash the lookup is occurring in; ie, the default proc the block itself is defining.
Here's an example from an IRB session:
irb(main):011:0> recursive_hash = Hash.new do |h,k|
irb(main):012:1* h[k] = Hash.new(&h.default_proc)
irb(main):013:1> end
=> {}
irb(main):014:0> recursive_hash[:foo]
=> {}
irb(main):015:0> recursive_hash
=> {:foo=>{}}
When the hash at recursive_hash[:foo] was created, its default_proc was supplied by recursive_hash's default_proc. This has two effects:
The default behavior for recursive_hash[:foo] is the same as recursive_hash.
The default behavior for hashes created by recursive_hash[:foo]'s default_proc will be the same as recursive_hash.
So, continuing in IRB, we get the following:
irb(main):016:0> recursive_hash[:foo][:bar]
=> {}
irb(main):017:0> recursive_hash
=> {:foo=>{:bar=>{}}}
irb(main):018:0> recursive_hash[:foo][:bar][:zap]
=> {}
irb(main):019:0> recursive_hash
=> {:foo=>{:bar=>{:zap=>{}}}}
I made rubygem for this. Try vine.
Install:
gem install vine
Usage:
hash.access("a.b.c")
I think one of the most readable solutions is using Hashie:
require 'hashie'
myhash = Hashie::Mash.new({foo: {bar: "blah" }})
myhash.foo.bar
=> "blah"
myhash.foo?
=> true
# use "underscore dot" for multi-level testing
myhash.foo_.bar?
=> true
myhash.foo_.huh_.what?
=> false
value = structure[:a][:b] rescue nil
Solution 1
I suggested this in my question before:
class NilClass; def to_hash; {} end end
Hash#to_hash is already defined, and returns self. Then you can do:
value = structure[:a].to_hash[:b]
The to_hash ensures that you get an empty hash when the previous key search fails.
Solution2
This solution is similar in spirit to mu is too short's answer in that it uses a subclass, but still somewhat different. In case there is no value for a certain key, it does not use a default value, but rather creates a value of empty hash, so that it does not have the problem of confusion in assigment that DigitalRoss's answer has, as was pointed out by mu is too short.
class NilFreeHash < Hash
def [] key; key?(key) ? super(key) : self[key] = NilFreeHash.new end
end
structure = NilFreeHash.new
structure[:a][:b] = 3
p strucrture[:a][:b] # => 3
It departs from the specification given in the question, though. When an undefined key is given, it will return an empty hash instread of nil.
p structure[:c] # => {}
If you build an instance of this NilFreeHash from the beginning and assign the key-values, it will work, but if you want to convert a hash into an instance of this class, that may be a problem.
You could just build a Hash subclass with an extra variadic method for digging all the way down with appropriate checks along the way. Something like this (with a better name of course):
class Thing < Hash
def find(*path)
path.inject(self) { |h, x| return nil if(!h.is_a?(Thing) || h[x].nil?); h[x] }
end
end
Then just use Things instead of hashes:
>> x = Thing.new
=> {}
>> x[:a] = Thing.new
=> {}
>> x[:a][:b] = 'k'
=> "k"
>> x.find(:a)
=> {:b=>"k"}
>> x.find(:a, :b)
=> "k"
>> x.find(:a, :b, :c)
=> nil
>> x.find(:a, :c, :d)
=> nil
This monkey patch function for Hash should be easiest (at least for me). It also doesn't alter structure i.e. changing nil's to {}. It would still also apply even if you're reading a tree from a raw source e.g. JSON. It also doesn't need to produce empty hash objects as it goes or parse a string. rescue nil was actually a good easy solution for me as I'm brave enough for such a low risk but I find it to essentially have a drawback with performance.
class ::Hash
def recurse(*keys)
v = self[keys.shift]
while keys.length > 0
return nil if not v.is_a? Hash
v = v[keys.shift]
end
v
end
end
Example:
> structure = { :a => { :b => 'foo' }}
=> {:a=>{:b=>"foo"}}
> structure.recurse(:a, :b)
=> "foo"
> structure.recurse(:a, :x)
=> nil
What's also good is that you can play around saved arrays with it:
> keys = [:a, :b]
=> [:a, :b]
> structure.recurse(*keys)
=> "foo"
> structure.recurse(*keys, :x1, :x2)
=> nil
The XKeys gem will read and auto-vivify-on-write nested hashes (::Hash) or hashes and arrays (::Auto, based on the key/index type) with a simple, clear, readable, and compact syntax by enhancing #[] and #[]=. The sentinel symbol :[] will push onto the end of an array.
require 'xkeys'
structure = {}.extend XKeys::Hash
structure[:a, :b] # nil
structure[:a, :b, :else => 0] # 0 (contextual default)
structure[:a] # nil, even after above
structure[:a, :b] = 'foo'
structure[:a, :b] # foo
You can use the andand gem, but I'm becoming more and more wary of it:
>> structure = { :a => { :b => 'foo' }} #=> {:a=>{:b=>"foo"}}
>> require 'andand' #=> true
>> structure[:a].andand[:b] #=> "foo"
>> structure[:c].andand[:b] #=> nil
There is the cute but wrong way to do this. Which is to monkey-patch NilClass to add a [] method that returns nil. I say it is the wrong approach because you have no idea what other software may have made a different version, or what behavior change in a future version of Ruby can be broken by this.
A better approach is to create a new object that works a lot like nil but supports this behavior. Make this new object the default return of your hashes. And then it will just work.
Alternately you can create a simple "nested lookup" function that you pass the hash and the keys to, which traverses the hashes in order, breaking out when it can.
I would personally prefer one of the latter two approaches. Though I think it would be cute if the first was integrated into the Ruby language. (But monkey-patching is a bad idea. Don't do that. Particularly not to demonstrate what a cool hacker you are.)
Not that I would do it, but you can Monkeypatch in NilClass#[]:
> structure = { :a => { :b => 'foo' }}
#=> {:a=>{:b=>"foo"}}
> structure[:x][:y]
NoMethodError: undefined method `[]' for nil:NilClass
from (irb):2
from C:/Ruby/bin/irb:12:in `<main>'
> class NilClass; def [](*a); end; end
#=> nil
> structure[:x][:y]
#=> nil
> structure[:a][:y]
#=> nil
> structure[:a][:b]
#=> "foo"
Go with #DigitalRoss's answer. Yes, it's more typing, but that's because it's safer.
In my case, I needed a two-dimensional matrix where each cell is a list of items.
I found this technique which seems to work. It might work for the OP:
$all = Hash.new()
def $all.[](k)
v = fetch(k, nil)
return v if v
h = Hash.new()
def h.[](k2)
v = fetch(k2, nil)
return v if v
list = Array.new()
store(k2, list)
return list
end
store(k, h)
return h
end
$all['g1-a']['g2-a'] << '1'
$all['g1-a']['g2-a'] << '2'
$all['g1-a']['g2-a'] << '3'
$all['g1-a']['g2-b'] << '4'
$all['g1-b']['g2-a'] << '5'
$all['g1-b']['g2-c'] << '6'
$all.keys.each do |group1|
$all[group1].keys.each do |group2|
$all[group1][group2].each do |item|
puts "#{group1} #{group2} #{item}"
end
end
end
The output is:
$ ruby -v && ruby t.rb
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]
g1-a g2-a 1
g1-a g2-a 2
g1-a g2-a 3
g1-a g2-b 4
g1-b g2-a 5
g1-b g2-c 6
I am currently trying out this:
# --------------------------------------------------------------------
# System so that we chain methods together without worrying about nil
# values (a la Objective-c).
# Example:
# params[:foo].try?[:bar]
#
class Object
# Returns self, unless NilClass (see below)
def try?
self
end
end
class NilClass
class MethodMissingSink
include Singleton
def method_missing(meth, *args, &block)
end
end
def try?
MethodMissingSink.instance
end
end
I know the arguments against try, but it is useful when looking into things, like say, params.

Is this a reasonable use for &&= in Ruby?

In SO question 2068165 one answer raised the idea of using something like this:
params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])
as a DRYer way of saying
params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]
where the params Hash would be coming from a (Rails/ActionView) form.
It's a kind of corollary to the well-known ||= idiom, setting the value if the LHS is not nil/false.
Is using &&= like this actually a recognised Ruby idiom that I've somehow missed or have I just forgotten a more commonly-used idiom? It is getting rather late...
It ought to be. If nothing else, params[:task] is only evaluated once when using the &&= form.
To clarify:
params[:task][:completed_at] = params[:task][:completed_at] && ...
calls [](:task) on params twice, [](:completed_at) and []=(:completed_at) once each on params[:task].
params[:task][:completed_at] &&= ...
calls [](:task) on params once, and its value is stashed away for both the [](:completed_at) and []=(:completed_at) calls.
Actual example describing what I'm trying to illustrate (based on Marc-Andre's example code; much thanks):
class X
def get
puts "get"
#hash ||= {}
end
end
irb(main):008:0> x = X.new
=> #<X:0x7f43c496b130>
irb(main):009:0> x.get
get
=> {}
irb(main):010:0> x.get[:foo] = 'foo'
get
=> "foo"
irb(main):011:0> x.get[:foo]
get
=> "foo"
irb(main):012:0> x.get[:foo] &&= 'bar'
get
=> "bar"
irb(main):013:0> x.get[:foo] = x.get[:foo] && 'bar'
get
get
=> "bar"
Note that using the "expanded" form causes "get" to be printed out twice, but using the compact form causes it to only be printed once.
Using &&=, in the case of LHS is false, it is only being read once, but not being set. This should make it clearer ...
class Test
def initialize(value)
#v = value
end
def v=(value)
puts "set"
#v = value
end
def v
puts "get=>#{#v}"
#v
end
end
t = Test.new(true)
t.v = t.v && true
puts '----'
t.v &&= true
puts '----'
t = Test.new(false) # lets make LHS false
t.v = t.v && true
puts '----'
t = Test.new(false) # lets make LHS false
t.v &&= true
The result:
get=>true
set
----
get=>true
set
----
get=>false
set
----
get=>false

conditional chaining in ruby

Is there a good way to chain methods conditionally in Ruby?
What I want to do functionally is
if a && b && c
my_object.some_method_because_of_a.some_method_because_of_b.some_method_because_of_c
elsif a && b && !c
my_object.some_method_because_of_a.some_method_because_of_b
elsif a && !b && c
my_object.some_method_because_of_a.some_method_because_of_c
etc...
So depending on a number of conditions I want to work out what methods to call in the method chain.
So far my best attempt to do this in a "good way" is to conditionally build the string of methods, and use eval, but surely there is a better, more ruby, way?
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) }
You can use tap:
my_object.tap{|o|o.method_a if a}.tap{|o|o.method_b if b}.tap{|o|o.method_c if c}
Sample class to demonstrate chaining methods that return a copied instance without modifying the caller.
This might be a lib required by your app.
class Foo
attr_accessor :field
def initialize
#field=[]
end
def dup
# Note: objects in #field aren't dup'ed!
super.tap{|e| e.field=e.field.dup }
end
def a
dup.tap{|e| e.field << :a }
end
def b
dup.tap{|e| e.field << :b }
end
def c
dup.tap{|e| e.field << :c }
end
end
monkeypatch: this is what you want to add to your app to enable conditional chaining
class Object
# passes self to block and returns result of block.
# More cumbersome to call than #chain_if, but useful if you want to put
# complex conditions in the block, or call a different method when your cond is false.
def chain_block(&block)
yield self
end
# passes self to block
# bool:
# if false, returns caller without executing block.
# if true, return result of block.
# Useful if your condition is simple, and you want to merely pass along the previous caller in the chain if false.
def chain_if(bool, &block)
bool ? yield(self) : self
end
end
Sample usage
# sample usage: chain_block
>> cond_a, cond_b, cond_c = true, false, true
>> f.chain_block{|e| cond_a ? e.a : e }.chain_block{|e| cond_b ? e.b : e }.chain_block{|e| cond_c ? e.c : e }
=> #<Foo:0x007fe71027ab60 #field=[:a, :c]>
# sample usage: chain_if
>> cond_a, cond_b, cond_c = false, true, false
>> f.chain_if(cond_a, &:a).chain_if(cond_b, &:b).chain_if(cond_c, &:c)
=> #<Foo:0x007fe7106a7e90 #field=[:b]>
# The chain_if call can also allow args
>> obj.chain_if(cond) {|e| e.argified_method(args) }
Although the inject method is perfectly valid, that kind of Enumerable use does confuse people and suffers from the limitation of not being able to pass arbitrary parameters.
A pattern like this may be better for this application:
object = my_object
if (a)
object = object.method_a(:arg_a)
end
if (b)
object = object.method_b
end
if (c)
object = object.method_c('arg_c1', 'arg_c2')
end
I've found this to be useful when using named scopes. For instance:
scope = Person
if (params[:filter_by_age])
scope = scope.in_age_group(params[:filter_by_age])
end
if (params[:country])
scope = scope.in_country(params[:country])
end
# Usually a will_paginate-type call is made here, too
#people = scope.all
Use #yield_self or, since Ruby 2.6, #then!
my_object.
then{ |o| a ? o.some_method_because_of_a : o }.
then{ |o| b ? o.some_method_because_of_b : o }.
then{ |o| c ? o.some_method_because_of_c : o }
Here's a more functional programming way.
Use break in order to get tap() to return the result. (tap is in only in rails as is mentioned in the other answer)
'hey'.tap{ |x| x + " what's" if true }
.tap{ |x| x + "noooooo" if false }
.tap{ |x| x + ' up' if true }
# => "hey"
'hey'.tap{ |x| break x + " what's" if true }
.tap{ |x| break x + "noooooo" if false }
.tap{ |x| break x + ' up' if true }
# => "hey what's up"
Maybe your situation is more complicated than this, but why not:
my_object.method_a if a
my_object.method_b if b
my_object.method_c if c
I use this pattern:
class A
def some_method_because_of_a
...
return self
end
def some_method_because_of_b
...
return self
end
end
a = A.new
a.some_method_because_of_a().some_method_because_of_b()
If you're using Rails, you can use #try. Instead of
foo ? (foo.bar ? foo.bar.baz : nil) : nil
write:
foo.try(:bar).try(:baz)
or, with arguments:
foo.try(:bar, arg: 3).try(:baz)
Not defined in vanilla ruby, but it isn't a lot of code.
What I wouldn't give for CoffeeScript's ?. operator.
I ended up writing the following:
class Object
# A naïve Either implementation.
# Allows for chainable conditions.
# (a -> Bool), Symbol, Symbol, ...Any -> Any
def either(pred, left, right, *args)
cond = case pred
when Symbol
self.send(pred)
when Proc
pred.call
else
pred
end
if cond
self.send right, *args
else
self.send left
end
end
# The up-coming identity method...
def itself
self
end
end
a = []
# => []
a.either(:empty?, :itself, :push, 1)
# => [1]
a.either(:empty?, :itself, :push, 1)
# => [1]
a.either(true, :itself, :push, 2)
# => [1, 2]

Resources