Ruby Rails "if" with condition of "false" actually executes? - ruby-on-rails

I really don't know what I am not seeing here, because it looks to me that "if false" is acting like "if true". To make it clear, I actually took screenshots of my RubyMine 6.0.3 screens. This one is probably simple, but it surely looks crazy to me...
The code is in DevicesController#update. It has a breakpoint that occurs within an "if false" statement that shouldn't be executing. See in this shot that I am on line 48 within the if:
The variable dump at this time follows, that shows that params[:device][:chg_pwd], the if condition, is false.
So, why am I within this if statement when its condition is false?

"false" is true in ruby. you want == "true"
In Ruby, only nil and false are false. For more, you can read this gist
if params[:device][:chg_pwd] == "true"
Depending on your situation, the other options are
Set to nil, or false. Then your existing code would work.
Set to "" and check with blank? as a previous version of RSB's answer
used to say, this is Rails only though, and does involve a small
semantic difference (blank vs false)

It returns "false", which is a string, so it returns true.
if params['is_admin'] == 'true'
# YOUR CODE goes HERE
end

Use this instead of "false" as "false" is a string and not a boolean value in your code
if params[:devise][:chg_pwd].eql?("false")
# code
else
# code
end
This would explain it better
2.0.0p247 :012 > if "true"
2.0.0p247 :013?> p "hello"
2.0.0p247 :014?> end
"hello"
=> "hello"
2.0.0p247 :015 > if "false"
2.0.0p247 :016?> p "hi"
2.0.0p247 :017?> end
"hi"
=> "hi"

Related

Rails - map 1 to true in if statement

I am getting a request from mobile app. They are sending a variable isbookmarked that can be either 1 or 0. I am checking in if statement.
if isbookmarked
do something
else
do something
end
The else part is never executed because 0 is not recognised as false
How can I achieve this?
In Ruby everything is truthy except for nil and false. Thus both 0 and 1 are truthy - you will never reach the else part with this set up.
So in your case you want to check exactly for 0 or 1:
if isbookmarked == 1
do something
elsif isbookmarked == 0
do something
end
You can use this gem called wannabe_bool, you will get a to_b method which can convert almost everything to boolean, you can check the gem documentation for all the options available.
if isbookmarked.to_b
do something
else
do something
end
I have tried few of the things on console that might help you
2.0.0-p451 :002 > "1".to_b
=> true
2.0.0-p451 :003 > "0".to_b
=> false
2.0.0-p451 :004 > 1.to_b
=> true
2.0.0-p451 :005 > 0.to_b
=> false
https://github.com/prodis/wannabe_bool
Hope that helps!
A helper method like below may come in handy.
def truthy?(val)
val.present? && !val.to_s.strip.in?(['false', '0'])
end
So, in addition to #AndreyDaineko, you also can use case
case isbookmarked
when 1
do something
when 0
do something
else
something go wrong
end
I would argue that you should use the same logic that Rails uses to identify booleans. Rails implements type casting to booleans like this:
FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"].to_set
def cast_value(value)
if value == ""
nil
else
!FALSE_VALUES.include?(value)
end
end
You can use this implementation like this (Rails 5 version):
ActiveModel::Type::Boolean.new.cast(isbookmarked)
Or the Rails 4.2 versions would look like this:
ActiveRecord::Type::Boolean.new.type_cast_from_user(isbookmarked)
You can also use zero? to check whether the value is zero or not. But there is no special method to check 1 only way isbookmarked == 1
if isbookmarked.zero?
do something
else
do something
end

Rails authentication from scratch

Actually I am new to rails I was trying to build authentication from scratch but didn't have any idea. So I was reading some code on internet and I am struck at a function.What does !! means over here. Is it first making it false then true? What it is and why we are using it?
def logged_in?
!!current_user
end
! means "not", so !! means "not-not". ! is often called "bang" (because 'exclamation mark' is too long, i assume) and so !! is often called "double bang". See also "hashbang" or "shebang" which mean #!.
It has the effect of converting all "truthy"* things into the boolean true and all "falsy" things into the boolean false.
Note that in Ruby, unlike some languages, the only things that are "falsy" are false and nil. 0, "", [], {}, and any other object that isn't false or nil, such as a User object, are "truthy".
*"truthy" means it will pass an "if" test.
We use double bang to get value as Boolean (true/false). Here is demonstration:
2.1.2 :001 > my_var = "string"
=> "string"
2.1.2 :002 > !my_var
=> false
2.1.2 :003 > !!my_var
=> true
2.1.2 :005 > null_var = nil
=> nil
2.1.2 :006 > !!null_var
=> false
So, as you can see that, if we want to get corresponding boolean value for an object then we use double bang (!!).
In your case, I assume current_user can have user object OR nil value. Instead of getting user object or nil, it's using !! to convert it into corresponding boolean.
Hope it helps. Happy Learning!
EDIT:
I would like to add that in Ruby, it's convention that method name ending with ? will return boolean value. So, your mentioned code us using !! in logged_in? to return boolean instead of User or NULL class objects.
You are trying to do something like this
def logged_in?
current_user
you are asking here if it is a current user
def logged_in?
!current_user
while you are asking here is the logged in user is not current_user.

Regex global variables are not being set

I came across something that seems unusual and I was wondering if anyone could explain why.
1.8.7 :001 > some_str = "Hello World"
=> "Hello World"
1.8.7 :002 > some_str.try(:match, /^(\w*)/)
=> #<MatchData "Hello" 1:"Hello">
1.8.7 :003 > $1
=> nil
1.8.7 :004 > some_str.match(/^(\w*)/)
=> #<MatchData "Hello" 1:"Hello">
1.8.7 :005 > $1
=> "Hello"
I'm not sure why the global variable $1 is not being set the first time, but is set the second. Any insights?
Let me show you how try is implemented. If you want to see it yourself, then take a look at the activesupport source. It's defined in /lib/active_support/core_ext/object/try.rb
class Object
def try(*a, &b)
if a.empty? && block_given?
yield self
else
public_send(*a, &b)
end
end
end
What this basically does, is just sending the method name and the complete arguments to the Object. public_send is the same as send, but can only be used to call public methods.
So I rewrote this, to debug your issue:
class Object
def try(*a)
result = public_send(*a)
puts $1.inspect
result
end
end
string = "Hello"
string.try(:match, /^(\w*)/)
puts $1.inspect
This outputs
"Hello"
nil
So the great question arises: Is this a bug in the ruby interpreter?. Maybe. At least it's not documented in any official source. I found a reference that tells the following (See Global variables.)
[...], $_ and $~ have local scope. Their names suggest they should be global, but they are much more useful this way, and there are historical reasons for using these names.
So it seems like $1 is not a global variable as well, even though it is reported by the Kernel as a global variable:
1.9.3-p194 :001 > global_variables
=> [:$;, :$-F, :$#, :$!, :$SAFE, :$~, :$&, :$`, :$', :$+, :$=, :$KCODE, :$-K,
:$,, :$/, :$-0, :$\, :$_, :$stdin, :$stdout, :$stderr, :$>, :$<, :$.,
:$FILENAME, :$-i, :$*, :$?, :$$, :$:, :$-I, :$LOAD_PATH, :$",
:$LOADED_FEATURES, :$VERBOSE, :$-v, :$-w, :$-W, :$DEBUG, :$-d, :$0,
:$PROGRAM_NAME, :$-p, :$-l, :$-a, :$binding, :$1, :$2, :$3, :$4, :$5, :$6,
:$7, :$8, :$9]
To make sure, I forwarded this incosistency to the Ruby Bug Tracker. See Ruby Bug #6723
try is defined as
def try(method, *args, &block)
send(method, *args, &block)
end
except of course on nil where it just returns nil. Why does this matter? Because the regexp globals aren't real globals: they're maintained on a per method and per thread basis (it's easy enough to see this by perusing the ruby source). When you call match via try the globals are set in the scope for try but in the next case they are set at the top level. It's easy to verify this
def do_match string, regexp
string =~ regexp
$1
end
do_match "Hello World", /^(\w*)/ #=> returns 'Hello'
$1 #=> returns nil

ROR + Error nil.empty? while running code

My "Project" Table have invoice as integer attribute, Here I put nil object to this attribute in DB. During evaluation nil.empty? occurs.
Code written at HAML extentions
- #project.each do |proj|
=proj.invoice if !proj.invoice.blank? || !proj.invoice.empty? || !proj.invoice.nil?
- #project_invoice=proj.invoice
=#project_invoice=0 if proj.invoice.blank? || proj.invoice.empty? || proj.invoice.nil
I receive this error while running code.
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
There's a few standard tests provided by Ruby and rails that can help, but you usually don't need to use all of them at once:
# Rails provided Object#blank? method
nil.blank? # => true
false.blank? # => true
''.blank? # => true
[ ].blank? # => true
# Ruby provided Object#nil? method
nil.nil? # => true
false.nil? # => false
''.nil? # => false
[ ].nil # => false
# Ruby class-specific #empty? method
nil.empty? # => error
false.empty? # => error
''.empty? # => true
[ ].empty? # => true
In your case the test you're probably looking for is actually a different one altogether. The opposite of blank? is present? and it comes in very handy for situations like this. You can even collapse down both of your inverted logical tests into a simple ternary query:
- #project_invoice = proj.present? ? proj.invoice : 0
More verbosely it looks like this:
- if (proj.present?)
#project_invoice = proj.invoice
- else
#project_invoice = 0
The present method verifies that the variable represents a non-nil, non-blank value of some sort.
The second condition has a misspelled variable name. It should be proj, not projt.
That would cause your issue.
if the invoice column is nill, then !proj.invoice.blank? evaluates to false, and the next test is done, !projt.invoice.empty?
since invoice is nil, you have nil.empty? which is an error, as empty? can not run on nil.
ruby-1.9.2-p0 > !nil.blank? || !nil.empty?
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
I think you are doing an overkill, since an integer should not be an array. I think you should just shorten it to only test blank?, as that catches an empty array too.
Is this is the code you are using. If this is the same code, I see a spelling mistake in the
second line.
Can you try with
=proj.invoice if !proj.invoice.blank? || !proj.invoice.empty? || !proj.invoice.nil?

How to check if a param is true or false?

This is really racking my brain, but maybe I'm trying to hard.
I'm passing a param via a URL (example.com?debug=true)
So I basically want to say:
if params[:debug] == true
do xyz
else
do abc
end
But for whatever reason that if statement just isn't doing like it seems like it should.
Is there a better way to do that if/else statement based on a param being true or false?
The debug param will either have a value of true, no value, or a value of false (as far as the URL goes).
params come in as strings, so you need to compare against "true", not true.
You could use ActiveRecord's method of checking truthful values if you don't want to reinvent the wheel (this is what is used when passing params inside an ActiveRecord object
Rails 3-4.1
if ActiveRecord::ConnectionAdapters::Column.value_to_boolean(params[:debug])
do xyz
else
do abc
Rails 4.2.0
ActiveRecord::Type::Boolean.new.type_cast_from_database(params[:debug])
Rails 5
ActiveModel::Type::Boolean.new.cast(params[:debug])
Might be worth wrapping in a helper but never the less it's quite flexible:
rails c
Loading development environment (Rails 3.2.6)
1.9.3p194 :001 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean '1'
=> true
1.9.3p194 :002 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean '0'
=> false
1.9.3p194 :003 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean 1
=> true
1.9.3p194 :004 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean true
=> true
1.9.3p194 :005 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean 'true'
=> true
1.9.3p194 :006 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean 'on'
=> true
1.9.3p194 :007 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean 'off'
Custom extension
Some people frown on extending core classes but this does fit with the DRY principle.
# config/initializer/boolean.rb
class Boolean
def self.parse(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
end
Then used like
if Boolean.parse(params[:debug])
then xyz
Since Rails/ActiveRecord 4.2.0 it is
if ActiveRecord::Type::Boolean.new.type_cast_from_user params[:debug]
do xyz
else
do abc
end
In Rails 5 it is:
if ActiveModel::Type::Boolean.new.cast params[:debug]
do xyz
else
do abc
end
But for whatever reason that if statement just isn't doing like it seems like it should.
I can almost guarantee that it is doing exactly what it should. When things don't make sense, one of our assumptions is wrong.
Is the value actually a boolean or is it string (or something else?). If the value is a string then of course the comparison to the boolean value true will fail. Try comparing to 'true' and see if that works.
how about this?
params[:debug].to_s.downcase == 'true'

Resources