Ruby variable not read in loop - ruby

Ruby newbie getting even more confused.
If I remove the discountgiven == false from the 4th line, code in the condition works.
If I add it back, the code doesn't work.
What am I doing wrong?
Is it something wrong in the discountgiven variable scope?
discountgiven = false
Input.cart.line_items.each do |item|
product = item.variant.product
if product.tags.include? 'device-ePod' && discountgiven == false
discount_to_apply = item.line_price * (1.0)
item.change_line_price(item.line_price - discount_to_apply, {message: 'Free Epod'})
discountGiven = true
end
end

use parentheses in conditions:
Change :
if product.tags.include? 'device-ePod' && discountgiven == false
to
if product.tags.include?('device-ePod') && discountgiven == false
Your former logic is being evaluated as if product.tags.include?('device-ePod' && discountgiven == false)
Also, what spickermann suggested, you probably typo in discountGiven. It should be discountgiven.

Related

&& operator not stopping excution with nil

The class below defies my understanding that nil && 'foo' should return nil and not execute 'foo'
no matter what I tried, with or without parenthesis, #user.another_boolean always returns undefined method another_boolean for nil nilclass. I thought if #user is nil it should stop evaluating there and return nil.
class MyClass
def initialize(user, variable = nil)
#user = user
#variable = variable || user.try(:variable)
end
def result
#result ||= !!(#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean)
end
end
I also tried to look for the documentation of the && operator inside the ruby documentation but could only find a reference to and which shouldn't be the same thing given their precedence difference.
Any help much appreciated.
Ruby version: 2.2.5
Edit:
#user and #variable are rails model
Rails version: 4.2
It is standard practice in software for && to have a higher precedence than ||.
So the following are all logically equivalent:
b && a || c
a && b || c
c || b && a
c || a && b
Now, your code is a little longer:
#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
But again we can group the && operators together to show what it's equivalent to:
(#user && #variable && #variable.a_boolean) ||
(#user.another_boolean?) ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
Therefore if #user && #variable && #variable.a_boolean == false, then #user.another_boolean? will be evaluated.
I'm not clear what it is you're trying to achieve - so I don't know if the above logic is correct, or how one might "fix" it, but there's your explanation for why the method is being called.
Your expression has a form of:
aaa &&
bbb &&
bbb.foo ||
aaa.bar ||
aaa.baz && something
it may be reformatted as:
aaa && bbb && bbb.foo
||
aaa.bar
||
aaa.baz && something
It's the same, just whitespaces are laid out differently.
As you can see here, not all terms are protected by the initial aaa&&bbb test.
Most probably you meant this:
#result ||= !!( (
#user &&
#variable
)
&&
(
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean?
)
&& instance_method_retuning_a_boolean
)
I've added way too many parentheses than needed, but this way you exactly see what's going on.
Hi Yann and welcome to Stackoverflow. Let me give you some examples that may help you understand the reason for your observation.
You correctly stated that:
nil && true
=> nil
but if you chain additional operators without explicitly use brackets then the following happens:
nil && true || true
=> true
This is because the && operator has higher precedence so you could write the same thing like this, and then its clear why the expression does not stop after the first nil:
(nil && true) || true
I found this article pretty helpful: https://womanonrails.com/operator-precedence-ruby.
So for your case if we would put the brackets as it is now we would have the following:
(#user && #variable && #variable.a_boolean) ||
#user.another_boolean? ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
This means that even if the first part of the expression results in false, the #user.another_boolean? still gets evaluated.
So what you probably want is putting brackets explicitly:
(#user && #variable) &&
(#variable.a_boolean || #user.another_boolean? || #user.a_third_boolean?) &&
instance_method_retuning_a_boolean
So now you have the first part, which will check if both #user and #variable are not nil. If any of those is nil, the second part will not be evaluated anymore.
Hope this brings some clarity.
You can probably avoid an overly complex boolean expression by adding a guard clause (or two) that separates the prerequisite conditions from the actual result:
def result
return unless #user
return unless #variable
#result ||= #variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
end
I'm not sure if this returns the expected result, but you get the idea.

simple ruby unless method mistake

I am very new to ruby. This might sound very naive to you but I can't understand what is happening here
matched_criteria = match_criteria(c1, c2)
puts "answer is #{matched_criteria}"
def match_criteria(crit_val1, crit_val2)
if crit_val1.present?
puts "present"
else
puts "absent"
end
return true unless crit_val1.present? || crit_val2.present?
return false unless crit_val1.present? && crit_val2.present?
end
Output:
absent
answer is false
please explain
def match_criteria(crit_val1, crit_val2)
# Check if #1 is present? or blank?
if crit_val1.present?
puts "present"
else
puts "absent"
end
# Return true unless EITHER (||) are present
# ...or you could think of it like...
# Return true if BOTH (&&) are blank
return true unless crit_val1.present? || crit_val2.present?
# Return false unless BOTH are present
# ..or...
# Return false if EITHER is blank
return false unless crit_val1.present? && crit_val2.present?
end
This is actually a Ruby on Rails question. blank? and present? are instance methods only available in Rails, but not in the standard Ruby library.
Whenever you call match_criteria with one parameter that is a non-blank value, the condition
crit_val1.present? || crit_val2.present?
will be true, but since you are negating it the first return statement does not get called.
In that same situation the second conditional evaluates to false, and since you're negating it the second return successfully returns false.
Your problem has nothing to do with Ruby (or Rails) per se. It has to do with misunderstanding the logical operators involved, which of course would be a problem in any language.
Consider this:
def test(x, y)
return true unless x || y
false unless x && y
end
p test(true, false) # => false
p test(true, true) # => nil
p test(false, false) # => true
In the first test (x = true, y = false) the first condition fails because x || y is true (it would succeed only if x || y evaluated to false, since you're using unless instead of if). The second condition succeeds because x && y is not true. So, the method returns false.
In the second test, neither condition succeeds, because both x || y and x && y evaluate to true. (Again, since you are using unless instead of if, a condition can only succeed if it evaluates to false.) So, the method returns nil.
In the third test, the first condition succeeds, because x || y evaluates to false. So, the method returns false.
It's most unlikely that you want these logical conditions. In the first place, you probably don't want a set of logical conditions that doesn't cover all the possibilities. Also, I don't see a reason for using unless instead of if; it seems like you're needlessly complicating your logic.
Of course, you haven't explained exactly what logical conditions you're looking for, so we're all playing "blind mechanic" here to some extent. But my feeling is that you are attempting to check whether your crit_val1 or crit_val2 are present, and return false if either of them is. If so, all you would have to do is this:
def match_criteria(crit_val1, crit_val2)
# other stuff
return false if crit_val1.present? || crit_val2.present?
true
end

How best to loop through multiple functions in Ruby?

Is there a more concise way to loop through a number of conditions than this?
def is_this_whole_thing_true?
result = false
result = true if condition_1?
result = true if condition_2?
result = true if condition_3?
result = true if condition_4?
result = true if condition_5?
result = true if condition_6?
result = true if condition_7?
result
end
Thanks for any help.
If you don't care to create a whole array I think this is the best option
def is_this_whole_thing_true?
conditions = [condition1?, condition2?, condition3?, condition4?]
conditions.any?
end
you can simply put && condition here like:
def is_this_whole_thing_true?
condition_1? && condition_2? && condition_3? && ...
end
Infact, what you are trying to do in the method is return true if any of the condition is true, then use ||
def is_this_whole_thing_true?
condition_1? || condition_2? || condition_3? || ...
end
An advantage of this is, it won't check all of the conditions, as soon as any one of the condition turns out to be true, it will return.

Ruby If and Unless conditional explanation

I want to ask for a little explanation about if and unless, I know the two of this is an opposite of each other. All of this time I have used both without no problem until now I find something weird. Let's check my code :
ausysdir = AUDIO_SYSTEM_FOLDER_NAME
Dir.mkdir(ausysdir) if ausysdir != "" && !File.directory?(ausysdir) # Number 1
Dir.mkdir(ausysdir) unless ausysdir == "" && File.directory?(ausysdir) # Number 2
Now in the directory I work the folder ausdyr is already exist.
But from the code above the the number one conditional which is if not return an error(this is what I expected, so great), But not with number 2 conditional which is unless, when I think that if and unless is an opposite, then my syntax above should be correct, am I right? Or my syntax is wrong? Or Unless have a different behaviour? Or do I missed something here?
Thank you very much.
EDIT : When I mean error, is an exception because the file is already exist.
unless expr is the same as if !expr. But !(a && b) is not !a && !b as you seem to think, it is actually !a || !b (this is one of De Morgan's laws BTW). So your if and unless statements are not at all equivalent.
Build the truth table if you don't believe me. Then study some Propositional Calculus (which every programmer should be familiar with) if you don't believe your truth table.
If your if works then you're unless would be:
unless ausysdir == "" || File.directory?(ausysdir)
TL;DR Don't use unless with more than one condition. Most people have to think too hard to understand it, and then have about a 50/50 chance of getting it right.
Here is a test of two booleans using if/unless and &&/or:
def test(first, second)
puts "#{first}, #{second}"
puts " ...passes if #{first} && #{second}" if first && second
puts " ...passes unless #{first} && #{second}" unless first && second
puts " ...passes if #{first} || #{second}" if first || second
puts " ...passes unless #{first} || #{second}" unless first || second
end
test(true, true)
test(true, false)
test(false, true)
test(false, false)
The results:
true && true
...passes if true && true
...passes if true || true
true && false
...passes unless true && false
...passes if true || false
false && true
...passes unless false && true
...passes if false || true
false && false
...passes unless false && false
...passes unless false || false
Your first condition boils down to if false && false, according to the false, false results above, if false && false doesn't pass. Reaction: Pretty straightforward and easy to see.
Your second condition boils down to unless false && true, according to the false, true results above, unless false && true does pass. Reaction: That seems like it possibly might make sense if I think about it some more.

Ruby: Clean code for checking nil /false conditional statement?

I always meet this Ruby problem, I want to write it more cleanly.
var a can be nil
a.value can also be nil
a.value has possible true or false value
if (not a.nil?) && (not a.value.nil?) && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
The problem is that the conditional statement is too clumsy and hard to read.
How can I improve the checking nil and false conditional statement?
Thanks, I am a Ruby newbie
Ruby on rails has an extension called try which allows you to write:
if a.try(:value) == false
which is very clean. Without try, you can just write
if a && a.value == false
If a.value is nil, it is not false, so that is ok :)
If it is possible that a.value is not defined (which would raise an exception), I would write that as follows:
if a && a.respond_to?(:value) && a.value == false
[UPDATE: after ruby 2.3]
Since ruby 2.3 there is an even shorter version:
if a&.value == false
which is almost equivalent to a.try(:value) (but is pure ruby). Differences:
if value does not exist, the &. operator will throw, try will just return nil (preferable or not?)(note: try! would also throw).
when cascading try or &. they also handle false differently. This follows logically from previous difference, try will return nil, while &. will throw because false knows no methods :P
You can achieve it in more compacted way using Safe Navigation Operator (&.):
if a&.value == false
Source : http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
if a && a.value!=false
puts "a value is true"
else
puts "a value is not available"
end
or just
puts a && a.value!=false ? "a value is true" : "a value is not available"
The simplest and cleanest way is to flip it and reverse it. Check for the truthy value rather than the falsey values
if a && a.value
puts "a value is true"
else
puts "a value is not available"
end
Of course in Rails you could do it either way by using blank? or present?
Your condition is redundant. If a.value is to be false, then it would not be nil.
if a.nil?.! && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
This will always return a boolean, if nil it will return false; if false it returns false; if true returns true. Try it out, it's nice and short:
!!a.try(:value) == false

Resources