Why does conditional statement and assigning value in ruby fails if the if statement is at end of clause?
It fails because of the way the parser works.
From the parser's point of view the variable tmp2 exists from the point in the code at which it is first assigned until the point where it goes out of scope. For this it does not matter, when (or if) the assignment is actually executed, just when the parser sees the assignment (i.e. it depends on the assignments position in the code).
Edit: To expand on that bit:
The decision whether a name is a local variable or a method call is made by the parser. The parser makes that decision solely based on whether or not it has already seen an assignment to that variable. So when the parser sees tmp2 before seeing tmp2 = ...
, it decides that here tmp2 refers to a method. When that part of the code is actually executed, it tries to call the method tmp2 which does not exist so you get the error.
Is assignment in a conditional clause good ruby style?
It is GOOD style to use assignments in conditionals. If you do so, wrap the condition in parentheses.
# bad (+ a warning)
if v = array.grep(/foo/)
do_something(v)
# some code
end
# good (MRI would still complain, but RuboCop won't)
if (v = array.grep(/foo/))
do_something(v)
# some code
end
# good
v = array.grep(/foo/)
if v
do_something(v)
# some code
end
See the community style guide for more information
Ruby syntax oddity
It's due to the parsing of lines, vs execution time. In the first version, value is parsed and set and then the puts evaluated. In the second line, when the parser gets to the variable puts value
, it has not yet been defined. In other words, it can't run the line to set the variable, until it has first parsed the line.
What is Returned in Ruby if the Last Statement Evaluated is an If Statement
If your if
statement doesn't result in any code being run, it returns nil, Otherwise, it returns the value of the code that was run. Irb is a good tool to experiment with such stuff.
irb(main):001:0> i = if false then end
=> nil
irb(main):002:0> i = if true then end
=> nil
irb(main):007:0> i = if false then "a" end
=> nil
irb(main):008:0> i = if false then "a" else "b" end
=> "b"
Why does Ruby include? behave differently when nested within an if/end conditional?
Here's what's happening in each of these examples:
First Example
This example outputs 1. Your string includes a j or J.
regardless of the previous line. The my_string.include?
check is being ignored as it's not used in a comparison anywhere, so the second line is just a regular puts
.
Second Example
The second example is a little more interesting. ("j" or "J")
is syntax in Ruby which will output the first of the provided arguments which evaluates to true
. "j"
evaluates to true because it's not nil or false, so it becomes the argument of the second include?
method. include?
is case-sensitive, so it will return false
– the string Jack
does not include a lowercase j
.
You can try this out by running irb
and entering something like 1 or 2
or false and 1
; you'll see pretty quickly that the first true argument is returned (or false
if no arguments are true).
There's no good way to make this work as-is, other than updating the include?
check to use something like set intersections. An easier solution may be to downcase
the input before checking characters.
Avdi Grimm posted a good video on using and
and or
in Ruby.
Third Example
The third example is calling include?
twice on the string, and returning true when it hits the second call, hence the if statement being evaluated.
Update
papirtiger's answer got me thinking, so I did a bit of digging with Ripper using the following script:
require 'ripper'
require 'pp'
expression = <<-FOO
if true
puts 'Hello'
end
FOO
pp Ripper.sexp(expression)
Here's the result:
[:program,
[[:if,
[:var_ref, [:@kw, "true", [1, 3]]],
[[:command,
[:@ident, "puts", [2, 2]],
[:args_add_block,
[[:string_literal,
[:string_content, [:@tstring_content, "Hello", [2, 8]]]]],
false]]],
nil]]]
After updating the expression to the following:
expression = <<-FOO
if
true
puts 'Hello'
end
FOO
This was the new output:
[:program,
[[:if,
[:var_ref, [:@kw, "true", [2, 2]]],
[[:command,
[:@ident, "puts", [3, 2]],
[:args_add_block,
[[:string_literal,
[:string_content, [:@tstring_content, "Hello", [3, 8]]]]],
false]]],
nil]]]
It looks as though Ruby does indeed ignore any whitespace and evaluate the next expression. I don't have enough expertise to dig much deeper, but after trying a few more examples (such as throwing a dozen newlines in after an if
statement), I'm convinced.
Case conditional in ruby doesn't enter in when
There are multiple problems here:
case args
when args = "order1"
Firstly, args
is an Array
- so it cannot possibly be equal to a String
. I'm not sure what you intended to happen here, so can't say exactly how to fix it.
Secondly, =
is an assignment operator, whereas ==
performs an equality check.
And lastly, this is a case
statement, not an if
statement, so you shouldn't actually be performing an equality check here... Either of these would have made sense syntactically:
case args
when "order1"
# ...
end
# OR:
case
when args == "order1"
# ...
end
Also, note that your question description is a bit confusing. You said:
the when loop
but this is not a loop. You could call it a "clause", or a "statement", but it's certainly not a "loop".
One line if statement not working
Remove if
from if @item.rigged ? "Yes" : "No"
Ternary operator has form condition ? if_true : if_false
Multiple Conditional Statement Execution in Ruby
You can simply add the values and later filter them out. Will save you from all those checks:
:customer => {
:first_name => @transaction.nome,
:last_name => @transaction.last_name,
:country_name => @transaction.nation.try(:name)
}.reject{ |_, v| v.nil? }
Ruby: Variables defined in If/else statement are accessible outside of if/else?
Variables are local to a function, class or module defintion, a proc
, a block.
In ruby if
is an expression and the branches don't have their own scope.
Also note that whenever the parser sees a variable assignment, it will create a variable in the scope, even if that code path isn't executed:
def test
if false
a = 1
end
puts a
end
test
# ok, it's nil.
It's bit similar to JavaScript, though it doesn't hoist the variable to the top of the scope:
def test
puts a
a = 1
end
test
# NameError: undefined local variable or method `a' for ...
So even if what you were saying were true, it still wouldn't be nil
.
Related Topics
How to Stringize/Serialize Ruby Code
Authlogic and Multiple Sessions for the Same User
Getting a List of Classes That Include a Module
R Statistical Package Gem for a Rails Application
Is the .Each Iterator in Ruby Guaranteed to Give the Same Order on the Same Elements Every Time
Ruby on Rails: How to Add a CSS File with Rails Project
How to Correctly Install Rvm in Docker
How to Receive a JSON Object with Rack
Check to See If an Array Is Already Sorted
/Config/Initializers/Secret_Token.Rb Not Being Generated. Why Not
Undefined Local Variable Based on Syntax in Ruby
Use Ruby Array for a JavaScript Array in Erb. Escaping Quotes