Ruby on rails, multiple check for nil attributes - ruby-on-rails

I'm trying to check multiple attributes for nil, I've found this post simplify... but I'm not getting the results I want. I have a user whom I want to update their profile if needed. This user however has all the data I want.
#user.try(:age_id).nil?
#returns false
#user.try(:customer).nil?
#returns false
#user.try(:country).nil?
#returns false
#user.try(:age_id).try(:customer).try(:country).nil?
#returns true
Why is it responding with true here when all the other single instances of tries responds with false?

You are chaining the .try(), which fails after the try(:age_id):
It tries to call age_id on the #user object
if #user.nil? # => returns nil
if #user.age_id != nil # => returns a Fixnum
Then you call the method try(:customer) on a Fixnum which obviously fails # => returns nil
etc.
An example from the IRB console:
1.9.3p448 :049 > nil.try(:nothing).try(:whatever).try(:try_this_also).nil?
=> true
If you want to test that all of these attributes are not nil, use this:
if #user.present?
if #user.age_id.presence && #user.customer.presence && #user.country.presence
# they are all present (!= nil)
else
# there is at least one attribute missing
end
end

Related

Rails ActiveSupport::Concern and method evaluation

I have this application which uses Devise with current_user helper. When I create a module, current_user becomes nil after I mention an attribution to it even though it never happens.
class PagesController < ApplicationController
include ModuleTest
def index
a_test_method
end
end
And the ModuleTest:
module ModuleTest
extend ActiveSupport::Concern
def a_test_method
puts "(BEFORE)===========> #{current_user.inspect}"
current_user = nil if false
puts "(AFTER) ===========> #{current_user.inspect}"
end
end
Output:
(BEFORE)===========> #<User id: 1>
(AFTER) ===========> nil
However, if I delete/comment out this line # current_user = nil if false, current_user remains valid:
(BEFORE)===========> #<User id: 1>
(AFTER) ===========> #<User id: 1>
Would this be related to lazy evaluation somewhat?
EDIT
The whole problem relies on how Ruby defines variables when a statement is not evaluated:
2.3.4 (main):0 > defined? this_never_seen_variable_before
=> nil
2.3.4 (main):0 > this_never_seen_variable_before = "value" if false
=> nil
2.3.4 (main):0 > defined? this_never_seen_variable_before
=> "local-variable"
2.3.4 (main):0 >
2.3.4 (main):0 > this_never_seen_variable_before_2
NameError: undefined local variable or method `this_never_seen_variable_before_2' for main:Object
from (pry):119:in `<main>'
2.3.4 (main):0 > this_never_seen_variable_before_2 = "value" if false
=> nil
2.3.4 (main):0 > this_never_seen_variable_before_2
=> nil
2.3.4 (main):0 >
How does this work underneath?
current_user is a helper method provided by Devise, not a local variable.
There is no such helper method named current_user=. You can prove this by changing current_user = nil to self.current_user = nil and see it crash. But this is irrelevant to your issue.
So the result is, you defined a local variable current_user between the 2 puts, which shadows the helper method with the same name.
The weird thing is, although current_user = nil is not executed because of the if false, the local variable still gets defined, and its value is implicitly set to nil. This is why your second puts shows nil. Even if you change your current_user = nil to current_user = :someone, your second puts should still show nil.

Rails Cookie Issue

I have the following new method in a ruby on rails app:
def new
if cookies[:owner].empty?
cookies[:owner] = SecureRandom.hex
end
#movie = Movie.new
#movie.owner = cookies[:owner]
end
Basically, each new user is supposed to be issued a code which identifies them (though just by the cookie). So when the user creates a movie, the cookie that was created is stored in the owner field.
So two problems:
Using the .empty? method when I delete the cookie from the browser, returns a undefined methodempty?' for nil:NilClass`
When I do have a cookie already set in the browser, and then create a movie, the cookies[:owner] value is different from the #movie.owner code?
cookies[:owner] will either be nil (when it hasn't been set), or a String (when it's been set). The method you're looking for is blank?, instead of empty?
2.1.0 :003 > nil.blank?
=> true
2.1.0 :005 > "i'm not blank".blank?
=> false
2.1.0 :006 > " ".blank?
=> true
As for your second problem: where do you call the save method? Do you have any callback on the Movie model that could rewrite the owner attribute?
You could also use this.
def new
if !cookies[:owner]
cookies[:owner] = SecureRandom.hex
end
#movie = Movie.new
#movie.owner = cookies[:owner]
end

Check if record exists from controller in Rails

In my app a User can create a Business. When they trigger the index action in my BusinessesController I want to check if a Business is related to the current_user.id:
If yes: display the business.
If no: redirect to the new action.
I was trying to use this:
if Business.where(:user_id => current_user.id) == nil
# no business found
end
But it always returns true even when the business doesn't exist...
How can I test if a record exists in my database?
Why your code does not work?
The where method returns an ActiveRecord::Relation object (acts like an array which contains the results of the where), it can be empty but it will never be nil.
Business.where(id: -1)
#=> returns an empty ActiveRecord::Relation ( similar to an array )
Business.where(id: -1).nil? # ( similar to == nil? )
#=> returns false
Business.where(id: -1).empty? # test if the array is empty ( similar to .blank? )
#=> returns true
How to test if at least one record exists?
Option 1: Using .exists?
if Business.exists?(user_id: current_user.id)
# same as Business.where(user_id: current_user.id).exists?
# ...
else
# ...
end
Option 2: Using .present? (or .blank?, the opposite of .present?)
if Business.where(:user_id => current_user.id).present?
# less efficiant than using .exists? (see generated SQL for .exists? vs .present?)
else
# ...
end
Option 3: Variable assignment in the if statement
if business = Business.where(:user_id => current_user.id).first
business.do_some_stuff
else
# do something else
end
This option can be considered a code smell by some linters (Rubocop for example).
Option 3b: Variable assignment
business = Business.where(user_id: current_user.id).first
if business
# ...
else
# ...
end
You can also use .find_by_user_id(current_user.id) instead of .where(...).first
Best option:
If you don't use the Business object(s): Option 1
If you need to use the Business object(s): Option 3
In this case I like to use the exists? method provided by ActiveRecord:
Business.exists? user_id: current_user.id
with 'exists?':
Business.exists? user_id: current_user.id #=> 1 or nil
with 'any?':
Business.where(:user_id => current_user.id).any? #=> true or false
If you use something with .where, be sure to avoid trouble with scopes and better use
.unscoped
Business.unscoped.where(:user_id => current_user.id).any?
ActiveRecord#where will return an ActiveRecord::Relation object (which will never be nil). Try using .empty? on the relation to test if it will return any records.
When you call Business.where(:user_id => current_user.id) you will get an array. This Array may have no objects or one or many objects in it, but it won't be null. Thus the check == nil will never be true.
You can try the following:
if Business.where(:user_id => current_user.id).count == 0
So you check the number of elements in the array and compare them to zero.
or you can try:
if Business.find_by_user_id(current_user.id).nil?
this will return one or nil.
business = Business.where(:user_id => current_user.id).first
if business.nil?
# no business found
else
# business.ceo = "me"
end
I would do it this way if you needed an instance variable of the object to work with:
if #business = Business.where(:user_id => current_user.id).first
#Do stuff
else
#Do stuff
end
Something new to try (:
Assign a variable or return
return unless #business = Business.where(user_id: current_user.id).first
Method would exit at this point if there are no businesses found with current user's ID, or assigns instance variable #business to the first business object.

ActiveRecord - check if value is null, 0 or 1

Rails 3.2.2, Ruby 1.9.2
I'm using MySql and there is column "MyColumn" of TINYINT type. I need to show the status of it on a page. So I created a helper method.
module MyControllerHelper
def result(a)
case a
when false then 'false 0'
when true then 'true 1'
when blank? then 'blank or nil'
end
end
end
The bottom line is that it can also be empty or nil. So it doesn't work as I need. It constantly returns either false 0 or true 1 but never blank or nil even if it should do.
What did I do wrong?
A case uses === for comparison so that's equivalent to:
if false === a
'false 0'
elsif true === a
'true 1'
elsif blank? === a
'blank or nil'
else
nil
end
Rails adds a blank? method to Object that looks like this:
def blank?
respond_to?(:empty?) ? empty? : !self
end
so you can call blank? anywhere, even without a specified receiver: there will always be a self and it will always be an Object. Now you should see that when blank?, while syntactically valid, makes no sense at all: it doesn't call a.blank? and see if a true value came back, it simply checks self.blank? === a for whatever self happens to be.
You're probably better off using an explicit if/else for this:
def result(a)
# false.blank? is true so you don't want a.blank? here.
if(a.nil?)
'nil'
elsif(a)
'true 1'
else
'false 0'
end
end

What's the right way to modify the params passed to controller in rails 3.1.0?

In our Rails 3.1.0 app, we need to modify params passed to rfq controller in create and update. For example, we want to record the current user id under input_by_id. What we did was:
#rfq.input_by_id = session[:user_id]
It worked as expected. Also when need_report field is false, then report_language field should be nil. We decide to add the following line in rfq controller to make sure the nil is passed to report_language when need_report is false:
#rfq.report_language = nil unless params[:need_report]
However this addition causes the rspec case failure (in create/update of the controller) because of the data validation failure. However when we fire up the app, it behaves fine without saving the report_language when need_report is false. I am wondering if the line above is not the right way to use params[:need_report] for #rfq updating.
Thanks so much.
UPDATE:
Controller code:
def create
if has_create_right?
#rfq = Rfq.new(params[:rfq], :as => :roles_new )
#rfq.input_by_id = session[:user_id]
#save sales_id selected
if sales? && member? && !team_lead?
#rfq.sales_id = session[:user_id]
end
#view page may carry the hidden report language even if need_report == false
#rfq.report_language = nil unless params[:need_report]
#save into join table rfqs_standards
params[:rfq][:standard_ids].each do |sid|
#rfq.standards << Standard.find(sid.to_i) if !sid.nil? && sid.to_i > 0
end unless params[:rfq][:standard_ids].nil?
#save into join table rfqs_test_items
params[:rfq][:test_item_ids].each do |tid|
#rfq.test_items << TestItem.find(tid.to_i) if !tid.nil? && tid.to_i > 0
end unless params[:rfq][:test_item_ids].nil?
if #rfq.save!
redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
else
flash.now[:error] = "RFQ not saved!"
render 'new'
end
else
redirect_to URI.escape("/view_handler?index=0&msg=No rights!")
end
end
Test case failed after addition of #rfq.report_language = nil unless params[:need_report]
it "should be successful for corp head" do
session[:corp_head] = true
session[:user_id] = 1
s = Factory(:standard)
rfq = Factory.attributes_for(:rfq, :need_report => true, :report_language => 'EN')
rfq[:standard_ids] = [s.id] # attach standard_id's to mimic the POST'ed form data
get 'create', :rfq => rfq
#response.should redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
response.should render_template('new')
end
the problem ist that you are simply not looking at the right value.
get 'create', :rfq => rfq will result in a params-hash like {:rfq => {...}}
so you need to #rfq.report_language = nil unless params[:rfq][:need_report] == 'true'

Resources