Is it possible for a Ruby class to implement a truthiness method or are all objects beside false and nil automatically true by design?
The answer to your second question is: yes, everything else than false and nil is considered "truthy" in Ruby.
So, for example
a = 'foo' if 0
# => "foo"
a = 'foo' if []
# => "foo"
a = 'foo' if ''
# => "foo"
a = 'foo' if nil
# => nil
a = 'foo' if false
# => nil
At least in 1.9.3, you can get some control over !:
$ irb
>> class C
>> def !
>> true
>> end
>> end
=> nil
>> c = C.new
=> #<C:0x7b5b9cd6>
>> !c
=> true
>> !!c
=> false
>>
Everything except false and nil (including 0, which sometimes confuses people coming from other languages) is truthy.
Two similar sentences have different behaviour. Is it ok?
Compare:
a = 123 unless defined? a
a # => nil
but...
unless defined? b
b = 123
end
b # => 123
Yes, this is the correct behaviour. Local variables are created and initialized with nil before assignment. So this code
a = 123 unless defined? a
a # => nil
is a rough equivalent of
a = nil
a = 123 unless defined? a # `a` is not undefined anymore.
a # => nil
Another example (even though c is not defined before this line, this code does not throw a NameError).
c = 2 unless c # => 2
If I have variables as below,
i = 1
k1 = 20
is there any ways to get values of k1 with the interpolation of i?
Something like,
k"#{i}"
=> 20
Thanks in advance.
It depends on whether it's a local variable or a method. send "k#{i}" should do the trick with methods:
class Foo
attr_accessor :i, :k1
def get
send "k#{i}"
end
end
foo = Foo.new
foo.i = 1
foo.k1 = "one"
foo.get
# => "one"
If you really need to, you can access local variables using the current Binding and local_variable_get:
i = 1
k1 = "one"
local_variables
# => [:i, :k1]
binding.local_variable_get("k#{i}")
# => "one"
This is pretty awful though. In this instance you'd be better off using a Hash:
i = 1
k = {1 => "one"}
k[i]
# => "one"
>> a = 5
=> 5
>> b = a
=> 5
>> b = 4
=> 4
>> a
=> 5
how can I set 'b' to actually be 'a' so that in the example, the variable a will become four as well. thanks.
class Ref
def initialize val
#val = val
end
attr_accessor :val
def to_s
#val.to_s
end
end
a = Ref.new(4)
b = a
puts a #=> 4
puts b #=> 4
a.val = 5
puts a #=> 5
puts b #=> 5
When you do b = a, b points to the same object as a (they have the same object_id).
When you do a = some_other_thing, a will point to another object, while b remains unchanged.
For Fixnum, nil, true and false, you cannot change the value without changing the object_id. However, you can change other objects (strings, arrays, hashes, etc.) without changing object_id, since you don't use the assignment (=).
Example with strings:
a = 'abcd'
b = a
puts a #=> abcd
puts b #=> abcd
a.upcase! # changing a
puts a #=> ABCD
puts b #=> ABCD
a = a.downcase # assigning a
puts a #=> abcd
puts b #=> ABCD
Example with arrays:
a = [1]
b = a
p a #=> [1]
p b #=> [1]
a << 2 # changing a
p a #=> [1, 2]
p b #=> [1, 2]
a += [3] # assigning a
p a #=> [1, 2, 3]
p b #=> [1, 2]
You can't. Variables hold references to values, not references to other variables.
Here's what your example code is doing:
a = 5 # Assign the value 5 to the variable named "a".
b = a # Assign the value in the variable "a" (5) to the variable "b".
b = 4 # Assign the value 4 to the variable named "b".
a # Retrieve the value stored in the variable named "a" (5).
See this article for a more in-depth discussion of the topic: pass by reference or pass by value.
As has been noted the syntax you are using can not be done. Just throwing this out there though you could make a wrapper class it depends what you actually want to do
ruby-1.8.7-p334 :007 > class Wrapper
ruby-1.8.7-p334 :008?> attr_accessor :number
ruby-1.8.7-p334 :009?> def initialize(number)
ruby-1.8.7-p334 :010?> #number = number
ruby-1.8.7-p334 :011?> end
ruby-1.8.7-p334 :012?> end
=> nil
ruby-1.8.7-p334 :013 > a = Wrapper.new(4)
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :014 > b = a
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :015 > a.number = 6
=> 6
ruby-1.8.7-p334 :016 > a
=> #<Wrapper:0x100336db8 #number=6>
ruby-1.8.7-p334 :017 > b
=> #<Wrapper:0x100336db8 #number=6>
You can use arrays:
a = [5]
b = a
b[0] = 4
puts a[0] #=> 4
This idea is based on this answer.
Just for the sake of reference.
>> a = 5
=> 5
>> a.object_id
=> 11
>> b = a
=> 5
>> b.object_id
=> 11
>> b = 4
=> 4
>> b.object_id
=> 9
>> a.object_id
=> 11
# We did change the Fixnum b Object.
>> Fixnum.superclass
=> Integer
>> Integer.superclass
=> Numeric
>> Numeric.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil
I hope this gives us all a little better understanding about objects in Ruby.
One option in cases where you feel you would like to have direct pointer operations is to use the replace method of Hashes, Arrays & Strings.
this is useful for when you would like to have a method return a variable that a proc the method sets up will change at a later date, and don't want the annoyance of using a wrapper object.
example:
def hash_that_will_change_later
params = {}
some_resource.on_change do
params.replace {i: 'got changed'}
end
params
end
a = hash_that_will_change_later
=> {}
some_resource.trigger_change!
a
{i: 'got changed'}
It's probably better generally to use explicit object wrappers for such cases, but this pattern is useful for building specs/tests of asynchronous stuff.
I'm no Ruby expert. But for a technically crazy kluge...that would only work if you felt like going through eval every time you worked with a variable:
>> a = 5
=> 5
>> b = :a
=> :a
>> eval "#{b} = 4"
=> 4
>> eval "#{a}"
=> 4
>> eval "#{b}"
=> 4
Note that a direct usage of b will still give you :a and you can't use it in expressions that aren't in eval:
>> b
=> :a
>> b + 1
NoMethodError: undefined method `+' for :a:Symbol
...and there are certainly a ton of caveats. Such as that you'd have to capture the binding and pass it around in more complex scenarios...
'pass parameter by reference' in Ruby?
#Paul.s has an answer for if you can change the point of declaration to be a wrapper object, but if you can only control the point of reference then here's a BasicReference class I tried:
class BasicReference
def initialize(r,b)
#r = r
#b = b
#val = eval "#{#r}", #b
end
def val=(rhs)
#val = eval "#{#r} = #{rhs}", #b
end
def val
#val
end
end
a = 5
puts "Before basic reference"
puts " the value of a is #{a}"
b = BasicReference.new(:a, binding)
b.val = 4
puts "After b.val = 4"
puts " the value of a is #{a}"
puts " the value of b.val is #{b.val}"
This outputs:
Before basic reference
the value of a is 5
After b.val = 4
the value of a is 4
the value of b.val is 4
I was under the impression that conditions joined with && were executed in sequence, such that the following would return true:
a = "adasd"
> b = a && b.present?
=> false
Thoughts?
Thanks!
--Peter
note:
b = a
=> "adasd"
b.present?
=> true
When you say this:
b = a && b.present?
You're declaring b as a local variable but it will be nil until the right side of the assignment is evaluated. In particular, b will be nil when you call present? on it and the conjunction will be false making b false.
When you do this:
a = 'pancakes'
b = a
b.present?
b will have the value 'pancakes' when you call present? on it so you get a true return from b.present?.
As per the rails doc
present is checking for a variable to be non blank
As per assignment timing, ruby declares the variable in the scope as soon as it sees it, so b will be in the scope but with no value, so present will return false.
You should maybe compared this with defined?
a = "abc"
=> "abc"
defined? a
=> "local-variable"
defined? b
=> nil
b = defined? b
=> "local-variable"