Setting a value to nil? - ruby-on-rails

I've run into a problem in Ruby that I can't seem to find an answer online for.
I have a few forms, and on one of them, I'm trying to set the value of age to nil if nothing is entered.
This is the relevant section for what I have right now:
puts "What is #{name}'s estimated age?"
age = gets.to_i
def set_nils
age = nil if age.blank?
end
I've tried a few different ideas, but nothing seems to change my final output (just printing age back) at all.

The problem is that if you enter nothing as your input and convert this \n to an integer, the value becomes 0 because:
irb(main):008:0> '\n'.to_i
=> 0
Therefore, you should change to this:
puts "What is #{name}'s estimated age?"
input = gets.to_i
#age = input > 0 ? input : nil
def set_nils
#age ||= nil
end
Output with valid age (> 0):
irb(main):001:0> name = "Tim"
=> "Tim"
irb(main):002:0> puts "What is #{name}'s estimated age?"
What is Tim's estimated age?
=> nil
irb(main):003:0> input = gets.to_i
8
=> 8
irb(main):004:0> #age = input > 0 ? input : nil
=> 8
irb(main):005:0>
irb(main):006:0* def set_nils
irb(main):007:1> #age ||= nil
irb(main):008:1> end
=> nil
irb(main):009:0> set_nils
=> 8
Output with invalid age (< 0):
irb(main):017:0> name = "Tim"
=> "Tim"
irb(main):018:0> puts "What is #{name}'s estimated age?"
What is Tim's estimated age?
=> nil
irb(main):019:0> input = gets.to_i
-1
=> -1
irb(main):020:0> #age = input > 0 ? input : nil
=> nil
irb(main):021:0>
irb(main):022:0* def set_nils
irb(main):023:1> #age ||= nil
irb(main):024:1> end
=> nil
irb(main):025:0> set_nils
=> nil

Related

Ruby rails object is nil, yet controller logic acting as if object is not nil

I have an an object Search with a method listings that returns an array of hashes if there are search results, otherwise it returns an empty array. In the event there is any empty array, I need to skip some code and go straight to the show page. I've tried object.nil? object.empty? object.present? all with the same outcome....the object which is supposed to be nil is treated as non-nil.
Controller code:
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.last.present?
if #results.last[0] == "p" || #results.last[0] == "s" || #results.last[0] == "d"
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end
end
show action results in
undefined method `[]' for nil:NilClass
Extracted source (around line #21):
19 p "#results.pop is either p, s, or d"
20 #sort_column = #results.pop
21 #grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
22 end
23 end
24 end
but, the server interface verifies that #results.last is nil:
>> #results
=> []
>> #results.last
=> nil
>> #results.last.present?
=> false
>> #results.last[0]
NoMethodError: undefined method `[]' for nil:NilClass
from /Users/tomb/Projects/schoolsparrow/app/controllers/searches_controller.rb:21:in `show'
>>
I'm at a loss as to how to logic is getting past the results.last.present? when results.last is nil.
If you're testing to see whether or not your array has any values in it, try:
#results.any?
An empty array is not nil, which is why your checks are failing.
EDIT:
A concise explanation of nil v. empty v. blank in Ruby on Rails
Why don't you check your condition on #results.present? and not #results.last.present?.
#results.last would throw a NoMethodError if #result is nil
To check if an array has elements use .any? or its opposite twin .empty?:
irb(main):006:0> [].empty?
=> true
irb(main):007:0> [].any?
=> false
.present? and .presence work on arrays but they are really more idiomatically correct for hashes like the params.
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.any? && ['p', 's', 'd'].include?(#results.last[0])
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end

Does there exist a gem to parse human numbers?

There is a helper #number_to_human to print large numbers, but is there an opposite helper to parse large numbers, similar to strtotime()?
No specific search results. Ruby Toolbox is dead.
A bonus would be to accept a locale, to handle , and ..
I would like to parse things like
$1m
$15 million
999 thousand
$999k
$111 M
1,234,567.89
€987.654,00
$1.1 billion
I found something and customized it.
def human_to_number(human)
return human unless human.is_a? String
return human if human.blank? # leave '' as is
human.downcase!
if human.index('k') || human.index('thousand')
multiplier = 1000
elsif human.index('m')
multiplier = 1_000_000
elsif human.index('b')
multiplier = 1_000_000_000
elsif human.index('t')
multiplier = 1_000_000_000_000
else
multiplier = 1
end
number = human.gsub(/[^0-9\.]/,'').to_f
number = number * multiplier
end
irb(main):003:0> d.human_to_number '$1.2 million'
=> 1200000.0
irb(main):004:0> d.human_to_number '$1.2 billion'
=> 1200000000.0
irb(main):005:0> d.human_to_number '$1.2k'
=> 1200.0
irb(main):006:0> d.human_to_number '1.2k'
=> 1200.0
irb(main):007:0> d.human_to_number '555.66k'
=> 555660.0

Inspect in Ruby

When I use this code:
x = []
x << 1
if (x[0].inspect == 1)
puts "OLLAAA"
end
It outputs nothing, but it should because x[0].inspect is == to 1. But if I change == to != I get output "OLLAAA", or when if is changed to unless. Any ideas?
Fixnum#inspect is an alias for to_s - you need to compare against "1"
Why are you so sure x[0].inspect == 1? Object::inspect returns a string.
$ irb
2.0.0-p247 :001 > x = []
=> []
2.0.0-p247 :002 > x << 1
=> [1]
2.0.0-p247 :003 > x[0].inspect == 1
=> false
If you change == to !=, obviously you flip the logic, so it prints. The exact same thing is happening for if to unless.
Because of below :
x[0].inspect # => "1"
x[0].inspect gives string instance,which is not equal to Fixnum instance 1. Thus x[0].inspect == 1 evaluates to false,and you didn't get the output. Now when you use !=,hope you got the point why you did get the output.
Fixnum#inpect call actually does Fixnum#to_s. See below :
12.method(:inspect) # => #<Method: Fixnum#to_s>
as mentioned above, inspect return a string. You can solve your problem with
if (x[0].inspect.to_i == 1)
puts "Hey I am an integer :)"
end

Rails Fixnum Error

I have a simple query that Rails seems to be interpreting as a fixnum, but I'm not sure why. My code looks like this:
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
#user_with_points.each_with_index do |user_with_point, index|
When I add puts #user_with_points, it shows:
#<Point:0x6360138>
#<Point:0x6322f38>
However, I'm receiving this error this error:
NoMethodError: undefined method 'each' for 75:Fixnum
adding Entire Code
def self.update_overall_rank_and_points
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
rank = 0
points = 0
#user_with_points.each_with_index do |user_with_point, index|
#user = User.find(user_with_point.user_id)
if user_with_point.points != points
points = user_with_point.points
rank += 1
end
#user.rank = rank
#user.points = user_with_point.points
#user.save
end
end
Your query is returning a scalar value which the sum of points as an integer. The total of your query happens to be 75, hence the error. Therefore you can't do an each against it since it's not an enumeration.
Try:
#user_with_points = Point.sum(:points, :group => :user_id, :order => 'sum(points)')
#user_with_points.each do |user_id, points|
#...
user = User.find(user_id)
if user.points != points
puts "not equal!"
end
end

How to properly handle changed attributes in a Rails before_save hook?

I have a model that looks like this:
class StopWord < ActiveRecord::Base
UPDATE_KEYWORDS_BATCH_SIZE = 1000
before_save :update_keywords
def update_keywords
offset = 0
max_id = ((max_kw = Keyword.first(:order => 'id DESC')) and max_kw.id) || 0
while offset <= max_id
begin
conditions = ['id >= ? AND id < ? AND language = ? AND keyword RLIKE ?',
offset, offset + UPDATE_KEYWORDS_BATCH_SIZE, language]
# Clear keywords that matched the old stop word
if #changed_attributes and (old_stop_word = #changed_attributes['stop_word']) and not #new_record
Keyword.update_all 'stopword = 0', conditions + [old_stop_word]
end
Keyword.update_all 'stopword = 1', conditions + [stop_word]
rescue Exception => e
logger.error "Skipping batch of #{UPDATE_KEYWORDS_BATCH_SIZE} keywords at offset #{offset}"
logger.error "#{e.message}: #{e.backtrace.join "\n "}"
ensure
offset += UPDATE_KEYWORDS_BATCH_SIZE
end
end
end
end
This works just fine, as the unit tests show:
class KeywordStopWordTest < ActiveSupport::TestCase
def test_stop_word_applied_on_create
kw = Factory.create :keyword, :keyword => 'foo bar baz', :language => 'en'
assert !kw.stopword, 'keyword is not a stop word by default'
sw = Factory.create :stop_word, :stop_word => kw.keyword.split(' ')[1], :language => kw.language
kw.reload
assert kw.stopword, 'keyword is a stop word'
end
def test_stop_word_applied_on_save
kw = Factory.create :keyword, :keyword => 'foo bar baz', :language => 'en', :stopword => true
sw = Factory.create :keyword_stop_word, :stop_word => kw.keyword.split(' ')[1], :language => kw.language
sw.stop_word = 'blah'
sw.save
kw.reload
assert !kw.stopword, 'keyword is not a stop word'
end
end
But mucking with the #changed_attributes instance variable just feels wrong. Is there a standard Rails-y way to get the old value of an attribute that is being modified on a save?
Update: Thanks to Douglas F Shearer and Simone Carletti (who apparently prefers Murphy's to Guinness), I have a cleaner solution:
def update_keywords
offset = 0
max_id = ((max_kw = Keyword.first(:order => 'id DESC')) and max_kw.id) || 0
while offset <= max_id
begin
conditions = ['id >= ? AND id < ? AND language = ? AND keyword RLIKE ?',
offset, offset + UPDATE_KEYWORDS_BATCH_SIZE, language]
# Clear keywords that matched the old stop word
if stop_word_changed? and not #new_record
Keyword.update_all 'stopword = 0', conditions + [stop_word_was]
end
Keyword.update_all 'stopword = 1', conditions + [stop_word]
rescue StandardError => e
logger.error "Skipping batch of #{UPDATE_KEYWORDS_BATCH_SIZE} keywords at offset #{offset}"
logger.error "#{e.message}: #{e.backtrace.join "\n "}"
ensure
offset += UPDATE_KEYWORDS_BATCH_SIZE
end
end
end
Thanks, guys!
You want ActiveModel::Dirty.
Examples:
person = Person.find_by_name('Uncle Bob')
person.changed? # => false
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_was # => 'Uncle Bob'
person.name_change # => ['Uncle Bob', 'Bob']
Full documentation: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
You're using the right feature but the wrong API.
You should #changes and #changed?.
See this article and the official API.
Two additional notes about your code:
Never rescue Exception directly when you actually want to rescue execution errors. This is Java-style. You should rescue StandardError instead because lower errors are normally compilation error or system error.
You don't need the begin block in this case.
def update_keywords
...
rescue => e
...
ensure
...
end

Resources