or equal vs equal [duplicate] - ruby

This question already has answers here:
What does ||= (or-equals) mean in Ruby?
(23 answers)
Closed 8 years ago.
I'm looking over someone's codes and wondering about the difference between:
def blah
#hello ||= [1,2,3].collect{|x| x+1}
end
and
def blah
#hello = [1,2,3].collect{|x| x+1}
end
I understand that ||= means "or equal", but why do we need it? An example would be great.
Also, for the collect method, let's say I have an array:
a = [1,2,4,5]
and I wanted to find the array that contains integers that are greater than 2, how can I use collect with that?
a.collect{|x| x>2} # => [false, false, true, true]
I want [4,5].

||= does nothing if the variable has been assigned a value (other than false or nil):
irb(main):001:0> blah ||= 'foo'
=> "foo"
irb(main):002:0> blah ||= 'bar'
=> "foo"
irb(main):003:0> puts blah
foo
=> nil
irb(main):004:0> blah = nil
=> nil
irb(main):005:0> blah ||= 'bar'
=> "bar"
irb(main):006:0> puts blah
bar
=> nil
One possible use is to assign variables in a fault-tolerant way. Compare:
#data = get_data_from_some_source
#data ||= get_data_from_fallback_source
#data ||= get_data_from_last_resort_source
with:
#data = get_data_from_some_souce
if #data == nil
#data = get_data_from_fallback_source
end
if #data == nil
#data = get_data_from_last_resort_source
end
or even (saints preserve us):
begin
#data = get_data_from_some_source
rescue GettingDataDidntWorkException
begin
#data = get_data_from_fallback_source
rescue GettingDataDidntWorkException
#data = get_data_from_last_resort_source
end
end
Haskell programmers will recognize this as similar in use to the Maybe monad.

a = [1,2,4,5] and I wanted to find the array that contains integers that are greater than 2,
Here we go using Array#select:
a.select{|e| e > 2 } # => [4,5]
Explanation of the part #hello ||= [1,2,3].collect{|x| x+1} can be found What does ||= (or equals) mean in Ruby?

The page What does ||= (or-equals) mean in Ruby? does not mention the "proxy design pattern", which it is in a nutshell.
The first time you call that line, the .select stuff gets evaluated. The second time, the system re-uses the #hello value, without re-assigning it. Due to "boolean short-circuiting", things to the right of a || don't evaluate, if the left side is not false or nil.
(Also, if you never evaluated #hello before, Ruby automagically creates one, and assigns nil to it. So Ruby lets you proxy in one line, as an optimization.)

Ok, so ||= means if you already have a previous value for your variable it will keep it
else, if it doesn't have any pre-assigned value, then it will take whatever value you set it equal to.
EX:
#hello = [1]
def blah
#hello ||=[1,2,3]
end
blah
#hello => [1], not [1,2,3]

Related

setter method return self not work, it's a bug? [duplicate]

This question already has answers here:
Is it possible to have class.property = x return something other than x?
(3 answers)
Closed 8 years ago.
I want to iterate an array of strings, and assign each of them to a fresh instance of class User, and I expect that I will got an array of User objects:
class User
def name=(name)
#name = name
self
end
end
original_array = ["aaa", "bbb", "bbb"]
result = original_array.collect { |str| User.new.name = str }
but the result is an array of strings!
puts result.inspect # => ["aaa", "bbb", "bbb"]
puts result === original_array # => true
I have no idea of where I went wrong?
What's wrong here is that User.new.name = str returns str, so the value of str gets collected.
Why does it return str? Because, opposed to any other Ruby method, every Ruby setter method returns the passed value, regardless the returned value in the method. For more infos about this behaviour you can check this other SO answer.
Below a IRB-ready Proof of Concept:
def name=(name)
#name = 'another value'
end
returned_value = (self.name = 'a value')
returned_value #=> 'a value'
#name #=> 'another value'
What you want can be done in this ways:
This syntax is valid for any Ruby object, as it uses Object#tap:
User.new.tap { |v| v.name = str }
If User is an ActiveRecord model, as I guess, you can use one of these slightly shorter syntaxes:
User.new name: str
User.new { |v| v.name = str }

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!

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 the ruby operator ||= intelligent?

I have a question regarding the ||= statement in ruby and this is of particular interest to me as I'm using it to write to memcache. What I'm wondering is, does ||= check the receiver first to see if it's set before calling that setter, or is it literally an alias to x = x || y
This wouldn't really matter in the case of a normal variable but using something like:
CACHE[:some_key] ||= "Some String"
could possibly do a memcache write which is more expensive than a simple variable set. I couldn't find anything about ||= in the ruby api oddly enough so I haven't been able to answer this myself.
Of course I know that:
CACHE[:some_key] = "Some String" if CACHE[:some_key].nil?
would achieve this, I'm just looking for the most terse syntax.
This is extremely easy to test:
class MyCache
def initialize
#hash = {}
end
def []=(key, value)
puts "Cache key '#{key}' written"
#hash[key] = value
end
def [](key)
puts "Cache key '#{key}' read"
#hash[key]
end
end
Now simply try the ||= syntax:
cache = MyCache.new
cache["my key"] ||= "my value" # cache value was nil (unset)
# Cache key 'my key' read
# Cache key 'my key' written
cache["my key"] ||= "my value" # cache value is already set
# Cache key 'my key' read
So we can conclude that no assignment takes place if the cache key already exists.
The following extract from the Rubyspec shows that this is by design and should not be dependent on the Ruby implementation:
describe "Conditional operator assignment 'obj.meth op= expr'" do
# ...
it "may not assign at all, depending on the truthiness of lhs" do
m = mock("object")
m.should_receive(:foo).and_return(:truthy)
m.should_not_receive(:foo=)
m.foo ||= 42
m.should_receive(:bar).and_return(false)
m.should_not_receive(:bar=)
m.bar &&= 42
end
# ...
end
In the same file, there is a similar spec for [] and []= that mandates identical behaviour.
Although the Rubyspec is still a work in progress, it has become clear that the major Ruby implementation projects intend to comply with it.
According to §11.3.1.2.2 of the Draft ISO Specification,
CACHE[:some_key] ||= "Some String"
expands to
o = CACHE
*l = :some_key
v = o.[](*l)
w = "Some String"
x = v || w
l << x
o.[]=(*l)
x
Or, in the more general case
primary_expression[indexing_argument_list] ω= expression
(I am using ω here to denote any operator, so it could be ||=, +=, *=, >>=, %=,…)
Expands to:
o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
x = v ω w
l << x
o.[]=(*l)
x
So, according to the specification, []= will always get called. But that is actually not the case in current implementations (I tested MRI, YARV, Rubinius, JRuby and IronRuby):
def (h = {}).[]=(k, v) p "Setting #{k} to #{v}"; super end
h[:key] ||= :value # => :value
# "Setting key to value"
h[:key] ||= :value # => :value
So, obviously either the specification is wrong or all five currently released implementations are wrong. And since the purpose of the specification is to describe the behavior of the existing implementations, it's obviously that the specification must be wrong.
In general, as a first approximation
a ||= b
expands to
a || a = b
However, there's all kinds of subleties involved, for example, whether or not a is undefined, whether a is a simple variable or a more complex expression like foo[bar] or foo.bar and so on.
See also some of the other instances of this same question, that have already been asked and answered here on StackOverflow (for example, this one). Also, the question has been discussed so many times on the ruby-talk mailinglist, that there are now discussion threads whose sole purpose it is to summarize the other discussion threads. (Although please note that that list is far from complete.)
Here's another demonstration that's a bit different than the other answers in that it explicitly shows when the Hash is being written to:
class MyHash < Hash
def []=(key, value)
puts "Setting #{key} = #{value}"
super(key, value)
end
end
>> h = MyHash.new
=> {}
>> h[:foo] = :bar
Setting foo = bar
=> :bar
>> h[:bar] ||= :baz
Setting bar = baz
=> :baz
>> h[:bar] ||= :quux
=> :baz
And by way of comparison:
// continued from above
>> h[:bar] = h[:bar] || :quuux
Setting bar = baz
=> :baz
CACHE[:some_key] ||= "Some String"
is equivalent to
CACHE[:some_key] = "Some String" unless CACHE[:some_key]
(which is equivalent to if + nil? unless CACHE[:some_key] is a boolean value).
In other words: yes, ||= will only write if the LHS is nil or false.
[I removed my example that was less accurate than other's. I leave my answer for the benchmarks that might be interesting to some. My point was:]
So basically
CACHE[:some_key] ||= "Some String"
is the same as
CACHE[:some_key] = "Some String" unless CACHE[:some_key]
I'm more for the first syntax, but then it's up to you since readibility is a bit reduced in that case.
I was curious, so here's some benchmarks:
require "benchmark"
CACHE = {}
Benchmark.bm do |x|
x.report {
for i in 0..100000
CACHE[:some_key] ||= "Some String"
end
}
x.report {
for i in 0..100000
CACHE[:some_key] = "Some String" unless CACHE[:some_key]
end
}
end
user system total real
0.030000 0.000000 0.030000 ( 0.025167)
0.020000 0.000000 0.020000 ( 0.026670)

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

Resources