Ruby: return value or nil - ruby-on-rails

I have this method in Rails and it trips up when passed in parameter, date_string, is nil. Specifically when GetFileInfo -d returns nil.
What's a Rails-y way to handle this?
def calling_method
...
m = MyModel.create(creation_date: cdate_for(path))
...
end
def cdate_for(path)
local_datetime_for(`GetFileInfo -d "#{path}"`.chomp!)
end
def local_datetime_for(date_string)
t = Time.strptime(date_string, '%m/%d/%Y %H:%M:%S')
DateTime.parse(t.to_s)
end
It's OK to return nil from this, assuming Model.create(creation_date: return_value_here) can handle nil values.
Edit: added some other methods to illustrate call chain.

You can use .blank?:
def local_datetime_for(date_string)
return nil if date_string.blank?
t = Time.strptime(date_string, '%m/%d/%Y %H:%M:%S')
DateTime.parse(t.to_s)
end
Some use cases for .blank?:
1.9.3p448 :042 > false.blank?
=> true
1.9.3p448 :043 > true.blank?
=> false
1.9.3p448 :044 > [].blank?
=> true
1.9.3p448 :045 > ''.blank?
=> true
1.9.3p448 :046 > '1'.blank?
=> false
1.9.3p448 :047 > nil.blank?
=> true
(For the record, .present? is the exact opposite of .blank?)

I think idiomatic Ruby calls for this:
def local_datetime_for(date_string)
unless date_string.blank?
t = Time.strptime(date_string, '%m/%d/%Y %H:%M:%S')
DateTime.parse(t.to_s)
end
end
Or if you want to be fancy:
def local_datetime_for(date_string)
DateTime.parse(Time.strptime(date_string, '%m/%d/%Y %H:%M:%S').to_s) unless date_string.blank?
end
Using return early in the method is very Ruby too. I just have trouble getting used to it coming from a Java background where I avoided early returns like the plague.

Use File.stat instead of calling out to the system for GetFileInfo. Then handle any exceptions with the stat in Ruby that might have caused a nil result.
def cdate_for(path)
File.stat(path).ctime.to_datetime
end

You need a condition to check if the date_string is nil or not. I would use a simple If statement,
def local_datetime_for(date_string)
if date_string === nil
t = Time.strptime(date_string, '%m/%d/%Y %H:%M:%S')
t = DateTime.parse(t.to_s)
else
t = 'Unknown'
end
return t
end

You can check the date_string value immediately. Returning immediately is the best way to express your intent – and use the nil? method to express what you're looking for in a Ruby-ish way:
def local_datetime_for(date_string)
return if date_string.nil?
t = Time.strptime(date_string, '%m/%d/%Y %H:%M:%S')
DateTime.parse(t.to_s)
end
Expressing the return case in a single "guard clause" is good practice – it gets the edge cases out of the way as quickly as possible, rather than leaving them hanging around like a bad smell, hovering over the meat of the method.

Related

How do I find and replace 'nil' values of Ruby hash with "None" or 0?

I'm trying to drill down to each value in an iteration of an array nested hash and replace all nil values with something like 'None' or 0. Please see my code that is clearly not working. I need to fix this before I pass it to my Views in Rails for iteration and rendering:
My controller:
def show
results = Record.get_record(params[:trans_uuid])
if !results.empty?
record = results.map { |res| res.attributes.symbolize_keys }
#record = Record.replace_nil(record) # this calls method in Model
else
flash[:error] = 'No record found'
end
end
My model:
def self.replace_nil(record)
record.each do |r|
r.values == nil ? "None" : r.values
end
end
record looks like this when passed to Model method self.replace_nil(record:
[{:id=>1, :time_inserted=>Wed, 03 Apr 2019 15:41:06 UTC +00:00, :time_modified=>nil, :request_state=>"NY", :trans_uuid=>"fe27813c-561c-11e9-9284-0282b642e944", :sent_to_state=>-1, :completed=>-1, :record_found=>-1, :retry_flag=>-1, :chargeable=>-1, :note=>"", :bridge_resultcode=>"xxxx", :bridge_charges=>-1}]
each won't "persist" the value you're yielding within the block. Try map instead.
def self.replace_nil(record)
record.map do |r|
r.values.nil? ? "None" : r.values
end
end
In fact, there's a method for that; transform_values:
record.transform_values do |value|
value.nil? ? 'None' : value
end
I realized that using Rails you can use just presence and the or operator:
record.transform_values do |value|
value.presence || 'None'
end

Rails/Ruby incorrectly showing variable not defined

In debugging console, while app running (using binding.pry to interrupt it), I can see that my variable Rails.configuration.hardcoded_current_user_key is set:
pry(#<TasksController>)> Rails.configuration.hardcoded_current_user_key
=> "dev"
But it doesn't appear to be defined:
pry(#<TasksController>)> defined?(Rails.configuration.hardcoded_current_user_key)
=> nil
Yet it works fine to store and test its value:
pry(#<TasksController>)> tempVar = Rails.configuration.hardcoded_current_user_key
=> "dev"
pry(#<TasksController>)> defined?(tempVar)
=> "local-variable"
What is going on?
This is because Rails config implements respond_to? but not respond_to_missing?, and defined? only recognizes respond_to_missing?:
class X
def respond_to?(name, include_all = false)
name == :another_secret || super
end
private
def method_missing(name, *args, &block)
case name
when :super_secret
'Bingo!'
when :another_secret
'Nope.'
else
super
end
end
def respond_to_missing?(name, include_all = false)
name == :super_secret || super
end
end
x = X.new
puts x.super_secret # => Bingo!
p defined?(x.super_secret) # => "method"
puts x.another_secret # => Nope.
p defined?(x.another_secret) # => nil
It's recommended to implement respond_to_missing? along with method_missing, I too wonder why Rails did it that way.
You shouldn't be using defined? on anything but the "stub" of that, or in other words, merely this:
defined?(Rails)
Anything beyond that is highly unusual to see, and I'm not even sure it's valid.
defined? is not a method, but a construct that tests if the following thing is defined as a variable, constant or method, among other things. It won't evaluate your code, it will just test it as-is. This means method calls don't happen, and as such, can't be chained.
If you want to test that something is assigned, then you should use this:
Rails.configuration.hardcoded_current_user_key.nil?

user.update :last_search_at => Time.current Always Returns False

When I call test_function , puts user.update :last_search_at => Time.current is always returning false although the third puts value is correct: 2016-06-10 20:40:33 UTC . user.save never works and first puts always returns nothing.
def test_function
puts user.last_search_at
puts user.update :last_search_at => Time.current
puts user.last_search_at
user.save
end
I would appreciate hearing your thoughts. Thanks for reading!
The main reason why the update may fail is because the record is invalid.
Print out the list of errors after the update
p user.update :last_search_at => Time.current
p user.error.full_messages
and make sure the record is valid.

How can I test if a variable is defined?

Here is the pseudo-code of what I want to do:
if #current_user is defined then puts #current_user.name
Use the operator defined? then.
x = 10
defined? x # => "local-variable"
defined? y # => nil
#x = 10
defined? #x # => "instance-variable"
!!defined? x # => true
!!defined? y # => false
write your code as below:
puts #current_user.name if !!defined?(#current_user)
Do you really need to know whether the variable is defined or is it enough to know whether it contains a valid User object?
Instance variables will never raise a NameError, even when they are not defined. They just evaluate to nil, so you can just check for that:
puts #current_user.name unless #current_user.nil?
Since your question is tagged ruby-on-rails, I'll assume that you have ActiveSupport loaded anyway, so you can also use the Object#try extension method:
puts #current_user.try(:name)
puts #current_user.name if instance_variable_defined?(:#current_user)

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.

Resources