Ruby Method Arguments - Can they change truth value automatically? - ruby

I have this method
def forest(sword, armour, ring)
with its arguments having true or false values, which I declare in the main program as
forest false, false, false
If during the program sword=true && armour=true, is there any way for Ruby to automatically assess whether the arguments are true or false?
Could I write something like
forest sword-truth-value, armour-truth-value, ring-truth-value?
The program I'm writing is very long and it would take too many code lines to take every single case into consideration.
Thanks for the help!

To achieve what you are looking for, you should wrap the forest method in a class and define each argument as an instance variable.
class Forest
attr_accessor :sword, :armour, :ring
def initialize(sword = false, armour = false, ring = false)
#sword = sword
#armour = armour
#ring = ring
end
end
So now, you can declare an instance of Forest,
forest = Forest.new
All the variables default to false, unless you explicitly write true.
With the attr_accessor, you can access and set all the variables.
forest.sword #=> false
forest.sword = true
forest.sword #=> true

The values true and false are atomic. If you don't want pass them as literal values you have to set a variable, i.e.
is_sword = false
is_armor = false
is_ring = false
forest is_sword, is_armor, is_ring
It seems like that should address your question, but it might be worth introducing the concepts of "mutable" objects as well. For example hashes and arrays are mutable:
hash = { armor: false, sword: false, ring: false }
def delete_nil_values(options)
# delete all key-vals from the hash where the val is falsey
options.each_key { |key| options.delete(key) if !options[key]}
options
end
delete_nil_values(hash)
puts hash
# => {}
# it is empty
This may or may not be what you intend; if you want to write 'side effect free' code you should be aware of this.
If you "clone" the input at the top of the delete_nil_values method using options = Marshal.load(Marshal.dump(options)) then it will be immutable.
To summarize: A function evaluates its arguments at runtime. So its variables will be the same that you passed in but if you have side-effects in your code the variables may have mutated.

Related

Can we overide true and false methods in Ruby?

Is it possibile to override boolean method like true and false in Ruby?
For example
def true
rand()
end
def false
rand()
end
true #=> 0.7548583661438558
false #=> 0.19239076750079454
If I try the above, I get:
true #=> true
false #=> false
I'm trying to understand where these true, false methods are defined.
I know that && and || can't be overriden, because && and || are short circut operators.
Are true and false similar? or they are written inside Ruby somewhere?
I'm just experimenting with Ruby.
true and false cannot be overridden because they are keywords, not methods. Their values are hard-coded and there's no way to re-assign a different value. (at least not from within Ruby)
Even when implementing a method with the same name, a literal true will still resolve to the built-in value.
What you can do is change the object, true is resolving to for interesting results: (this is just for fun, don't do this in actual code)
def true.inspect
rand.to_s
end
true #=> 0.8499100640573652
true #=> 0.22279583303254913
However, the above example doesn't actually change the value of true in any way. Also, don't think of true as a method invocation returning a random float every time it is called. We are looking at the very same object:
a = true
a #=> 0.5813338288658235
a #=> 0.6447125630746154
What we did is change its representation by overriding inspect, which:
Returns a string containing a human-readable representation of obj.
We see those floats because IRB calls inspect in an attempt to print the last expression's result in a readable way.
If we set IRB's output mode to :to_s, our change is bypassed and we see true again:
IRB.CurrentContext.inspect_mode = :to_s
a #=> true
true #=> true

Ruby Set with custom class to equal basic strings

I want to be able to find a custom class in my set given just a string. Like so:
require 'set'
Rank = Struct.new(:name, keyword_init: true) {
def hash
name.hash
end
def eql?(other)
hash == other.hash
end
def ==(other)
hash == other.hash
end
}
one = Rank.new(name: "one")
two = Rank.new(name: "two")
set = Set[one, two]
but while one == "one" and one.eql?("one") are both true, set.include?("one") is still false. what am i missing?
thanks!
Set is built upon Hash, and Hash considers two objects the same if:
[...] their hash value is identical and the two objects are eql? to each other.
What you are missing is that eql? isn't necessarily commutative. Making Rank#eql? recognize strings doesn't change the way String#eql? works:
one.eql?('one') #=> true
'one'.eql?(one) #=> false
Therefore it depends on which object is the hash key and which is the argument to include?:
Set['one'].include?(one) #=> true
Set[one].include?('one') #=> false
In order to make two objects a and b interchangeable hash keys, 3 conditions have to be met:
a.hash == b.hash
a.eql?(b) == true
b.eql?(a) == true
But don't try to modify String#eql? – fiddling with Ruby's core classes isn't recommended and monkey-patching probably won't work anyway because Ruby usually calls the C methods directly for performance reasons.
In fact, making both hash and eql? mimic name doesn't seem like a good idea in the first place. It makes the object's identity ambiguous which can lead to very strange behavior and hard to find bugs:
h = { one => 1, 'one' => 1 }
#=> {#<struct Rank name="one">=>1, "one"=>1}
# vs
h = { 'one' => 1, one => 1 }
#=> {"one"=>1}
what am i missing?
What you are missing is that "one" isn't in your set. one is in your set, but "one" isn't.
Therefore, the answer Ruby is giving you is perfectly correct.
All that you have done with your implementation of Rank is that any two ranks with the same name are considered to be the same by a Hash, Set, or Array#uniq. But, a Rank is not the same as a String.
If you want to be able to have a set-like data structure where you can look up things by one of their attributes, you will have to write it yourself.
Something like (untested):
class RankSet < Set
def [](*args)
super(*args.map(&:name))
end
def each
return enum_for(__callee__) unless block_given?
super {|e| yield e.name }
end
end
might get you started.
Or, instead of writing your own set, you can just use the fact that any arbitrary rank with the right name can be used for lookup:
set.include?(Rank.new(name: "one"))
#=> true
# even though it is a *different* `Rank` object

be false vs be_falsey in rspec [duplicate]

This question already has an answer here:
What is the difference between "be_true" and "be true" in RSpec
(1 answer)
Closed 8 years ago.
I know be false will just return true if the expected value is a pure false value, wherease be_falsey will return true if the expected value is either false or nil.
However, I fail to understand when would I use one or the other. I fail to see an example where be_falsey would be more useful than be false.
There are lots of situations in Ruby where you get a nil result and want to treat it like false. For example, suppose we have a method that handles an options hash, and we want the absence of an option to be the same as the option set to false:
def verbose?(opts)
opts[:verbose]
end
opts = { verbose: false }
expect(verbose?(opts)).to be_falsey # => PASS
opts = { verbose: true }
expect(verbose?(opts)).to be_falsey # => FAIL
opts = {}
expect(verbose?(opts)).to be_falsey # => PASS
Obviously this is a simplistic example (you could argue that verbose? should always return true or false), but similar scenarios are common in Ruby.
One example I can think of is for a unit test for a method. The method would be used in such a way that you are not really interested in the result, but only whether it is truthy/falsey.
Consider a method that checks if there is a record with a given value is in the db:
Class Foo
def self.has_value? value
Foo.find(value: value)
end
This method would be used as:
if Foo.has_value?(value)
...
Now, you could write a test like this:
let(:foo ){ create :foo )
expect(Foo.has_value?(1)).to == foo
But this obscures the intent of the method. The method's intent is not to find a foo. It is to tell you whether a foo with a given value exists or not.
To express the intent of the method it might be better to use
expect(Foo.has_value(1).to be_truthy
expect(Foo.has_value(2).to be_falsey
In Rails, if model.save is often used. This is the same pattern.

Is there a simple way to evaluate a value to true/false without using an expression?

Is there a simple way in Ruby to get a true/false value from something without explicitly evaluating it to true or false
e.g. how would one more succinctly express
class User
def completed_initialization?
initialization_completed == 1 ? true : false
end
end
is there some way to do something along the lines of
initialization_completed.true?
There's obviously not much in it but since I'm in the zen garden of Ruby I might as well embrace it
EDIT (I've updated the example)
This question was extremely badly phrased as was very gently pointed out by #Sergio Tulentsev. The original example (below) does of course evaluate directly to a boolean. I'm still struggling to find an example of what I mean however Sergio's double-negative was in fact exactly what I was looking for.
Original example
class User
def top_responder
responses.count > 10 ? true : false
end
end
> operator already returns boolean value. So it can be just
def top_responder
responses.count > 10
end
To convert arbitrary values to booleans, I offer you this little double-negation trick.
t = 'foo'
!!t # => true
t = 1
!!t # => true
t = 0
!!t # => true
t = nil
!!t # => false
The first negation "casts" value to boolean and inverts it. That is, it will return true for nil / false and false for everything else. We need another negation to make it produce "normal" values.

What does !! mean in ruby?

Just wondering what !! is in Ruby.
Not not.
It's used to convert a value to a boolean:
!!nil #=> false
!!"abc" #=> true
!!false #=> false
It's usually not necessary to use though since the only false values to Ruby are nil and false, so it's usually best to let that convention stand.
Think of it as
!(!some_val)
One thing that is it used for legitimately is preventing a huge chunk of data from being returned. For example you probably don't want to return 3MB of image data in your has_image? method, or you may not want to return your entire user object in the logged_in? method. Using !! converts these objects to a simple true/false.
It returns true if the object on the right is not nil and not false, false if it is nil or false
def logged_in?
!!#current_user
end
! means negate boolean state, two !s is nothing special, other than a double negation.
!true == false
# => true
It is commonly used to force a method to return a boolean. It will detect any kind of truthiness, such as string, integers and what not, and turn it into a boolean.
!"wtf"
# => false
!!"wtf"
# => true
A more real use case:
def title
"I return a string."
end
def title_exists?
!!title
end
This is useful when you want to make sure that a boolean is returned. IMHO it's kind of pointless, though, seeing that both if 'some string' and if true is the exact same flow, but some people find it useful to explicitly return a boolean.
Note that this idiom exists in other programming languages as well. C didn't have an intrinsic bool type, so all booleans were typed as int instead, with canonical values of 0 or 1. Takes this example (parentheses added for clarity):
!(1234) == 0
!(0) == 1
!(!(1234)) == 1
The "not-not" syntax converts any non-zero integer to 1, the canonical boolean true value.
In general, though, I find it much better to put in a reasonable comparison than to use this uncommon idiom:
int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
It's useful if you need to do an exclusive or. Copying from Matt Van Horn's answer with slight modifications:
1 ^ true
TypeError: can't convert true into Integer
!!1 ^ !!true
=> false
I used it to ensure two variables were either both nil, or both not nil.
raise "Inconsistency" if !!a ^ !!b
It is "double-negative", but the practice is being discouraged. If you're using rubocop, you'll see it complain on such code with a Style/DoubleNegation violation.
The rationale states:
As this is both cryptic and usually redundant, it should be avoided
[then paraphrasing:] Change !!something to !something.nil?
Understanding how it works can be useful if you need to convert, say, an enumeration into a boolean. I have code that does exactly that, using the classy_enum gem:
class LinkStatus < ClassyEnum::Base
def !
return true
end
end
class LinkStatus::No < LinkStatus
end
class LinkStatus::Claimed < LinkStatus
def !
return false
end
end
class LinkStatus::Confirmed < LinkStatus
def !
return false
end
end
class LinkStatus::Denied < LinkStatus
end
Then in service code I have, for example:
raise Application::Error unless !!object.link_status # => raises exception for "No" and "Denied" states.
Effectively the bangbang operator has become what I might otherwise have written as a method called to_bool.
Other answers have discussed what !! does and whether it is good practice or not.
However, none of the answers give the "standard Ruby way" of casting a value into a boolean.
true & variable
TrueClass, the class of the Ruby value true, implements a method &, which is documented as follows:
Returns false if obj is nil or false, true otherwise.
Why use a dirty double-negation when the standard library has you covered?

Resources