comparing params in rails - ruby-on-rails

I am doing
if params[:type] = "Type A"
# do something
end
if params[:type] = "Type B"
# do something
end
But I think that is wrong. I should be using ==
However that gives me error:
You have nil object when you didn't expect it
What is the best way to do this in rails?
All I am doing is getting a radio button variable from a form and doing logic based on its value (either Type A or Type B)

Preamble
class Hash
def try(arg)
self[arg] rescue nil
end
end
Your way
if params.try(:type) == "Type A"
# do
elsif params.try(:type) == "Type B"
# do
end
DRY
case params.try(:type)
when "Type A"
# do
when "Type B"
# do
else
# default do
end

You're sure it should be params[:type]? First, check your logs to see what is inside params before you access action in controller.
To check multiple choices you can use switch construct:
case params[:type]
when "Type A"
# do sth
when "Type B"
# do sth
else # not A nor B, can be nil
# do nothing
end
And if you need to deeper inside params then you can use if/else:
if params[:type] && params[:type][:value] == "sth"
# do sth
elsif params[:type] && params[:type][:value] == "..."
# do sth
end
And check where you get your error from, because in Ruby you can easily compare nil and String, so it's not because of using == in your example.

Related

Ruby comparison with an OR operator - strange behaviour

Below code should be a good example of strange Ruby behaviour when it comes to the OR operator:
def search_by_name_active
if request.path == (name_search_registrants_path \
|| new_registrant_path)
'active'
end
end
And the specs:
describe "#search_by_name_active" do
it "should return active if current page is new_registrant" do
allow(helper.request).to receive(:path).and_return(new_registrant_path)
expect(helper.search_by_name_active).to eq('active')
end
end
Which gives me an error:
Failure/Error: expect(helper.search_by_name_active).to eq('active')
expected: "active"
got: nil
If I remove the brackets:
def search_by_name_active
if request.path == name_search_registrants_path \
|| new_registrant_path
'active'
end
end
The first spec will passed but not the below one:
it "should return nil if current page is not search_by_name" do
allow(helper.request).to receive(:path).and_return(id_search_registrants_path)
expect(helper.search_by_name_active).to be_nil
end
Failure/Error: expect(helper.search_by_name_active).to be_nil
expected: nil
got: "active"
WTF?! Is there any other way to write this logical equation besides an additional if like below?
def search_by_name_active
if request.path == name_search_registrants_path
'active'
elsif request.path == new_registrant_path
'active'
end
end
This behaviour is expected in all programming languages, not just ruby. To simplify your example a little:
x == (a || b)
...is not equivalent to:
(x == a) || (x == b)
The first expression is evaluating (a || b) before comparing it to x. So you're only comparing x to one of the values, not both of them.
The generic way to write this in all programming languages would to instead use the second code sample above. Or in other words, using your specific example:
if request.path == name_search_registrants_path \
|| request.path == new_registrant_path
Or, there are a couple of ruby-specific ways we can shorten this code:
# Works in any ruby code
if [name_search_registrants_path, new_registrant_path].include?(request.path)
# Works in any rails code
if request.path.in? [name_search_registrants_path, new_registrant_path]
The second example is rails-specific, because it's using this extension to the core ruby language.

debug Model validation in Rails

I am new to RoR and I am trying to debug a custom validation in my Model. I've tried the following in my code, as well as puts self.input_values.inspect and Rails.logger.info self.input_values.inspect but no outcome in the logs. Am I address wrong debugging? How can I check the value of self.input_values?
validate :i_is_i
def i_is_i
unless self.cust_record == 'number' && self.input_values.all? do |value|
'#################'
Rails.logger.debug self.input_values.inspect
'#################'
value.match(/^\d*$/)
end
errors.add(:input_values, "only numbers!")
throw(:abort)
end
end
They might not be getting printed because the condition is failing and it doesn't go inside block. Do:
validate :i_is_i
def i_is_i
p "CUST_RECORD >>>>>>>>>>>>>>>>> #{self.cust_record}"
p "INPUT_VALUES >>>>>>>>>>>>>>>>> #{self.input_values.all?}"
unless self.cust_record == 'number' && self.input_values.all? do |value|
# Stuff you wanna do when it goes inside
end
end

facing issues about array in rails

I am implementing a program in rails where there is a form and after submitting the form it will check if there is any record with duplicate value for a specific field in database. My database table is students. So my corresponding model name is Student. I am writing this code (what I have just discussed) in my controller.
But I am facing the following error. I am using some arrays for internal operations. When I wrote that particular function in ruby only(not rails) then it was working fine. Moreover I am also facing error due to the use of "length".
My error is:
NoMethodError in StudentsController#create
undefined method `[]' for nil:NilClass
My controller code is:
class StudentsController < ApplicationController
def new
#student=Student.new
#students=Student.all
end
def create
#student=Student.new(u_params)
ret_val=string_check
if ret_val==1
#student.save
redirect_to new_student_path , :notice => "Inserted!!!"
else
redirect_to new_student_path , :notice => "Match,Not inserted!!!"
end
end
def u_params
params.require(:student).permit(:id ,:firstname, :lastname)
end
def u_params_second
params.require(:student).permit(:firstname)
end
def string_check
count =0;
#temp1=:firstname
temp1=params[:firstname]
supplied_val=temp1
puts "Entered in string_check method"
for i in 46..100
temp2=Student.find_by(id:i)
table_val=temp2.firstname
size1=supplied_val.to_s.length
size2=table_val.to_s.length
arr1=Array.new
arr2=Array.new
# arr1[i] ||= []
# arr2[i] ||= []
for i in 0..size1
arr1.push(supplied_val[i])
end
for i in 0..size2
arr2.push(table_val[i])
end
for i in 0..size1
if arr1[i]=="#" || arr1[i]=="#" || arr1[i]=="{" || arr1[i]=="}" || arr1[i]=="(" || arr1[i]==")" || arr1[i]=="[" || arr1[i]=="]" || arr1[i]=="." || arr1[i]==";" || arr1[i]=="," || arr1[i]=="%" || arr1[i]=="&" || arr1[i]=="*" || arr1[i]=="!" || arr1[i]=="?" || arr1[i]=="$" || arr1[i]=="^" || arr1[i]==":" || arr1[i]=="-" || arr1[i]=="/"
count=count+1
# puts count
arr1[i]=""
end
end
# puts arr1
puts arr1.join
final1=arr1.join
for i in 0..size2
if arr2[i]=="#" || arr2[i]=="#" || arr2[i]=="{" || arr2[i]=="}" || arr2[i]=="(" || arr2[i]==")" || arr2[i]=="[" || arr2[i]=="]" || arr2[i]=="." || arr2[i]==";" || arr2[i]=="," || arr2[i]=="%" || arr2[i]=="&" || arr2[i]=="*" || arr2[i]=="!" || arr2[i]=="?" || arr2[i]=="$" || arr2[i]=="^" || arr2[i]==":" || arr2[i]=="-" || arr2[i]=="/"
count=count+1
# puts count
arr2[i]=""
end
end
# puts arr2
puts arr2.join
final2=arr2.join
if final1==final2
flag=0
else
flag=1
end
return flag
end
end
end
The routes.rb file is:
Rails.application.routes.draw do
resources :students
end
My error is: NoMethodError in StudentsController#create
undefined method `[]' for nil:NilClass
It simply means that you are trying to access something as an array that is actually a nil object, and not an array.
To get rid of this error, you can a technique called short-circuit in Ruby.
Let's say your following piece of code is producing the said error:
arr1[i]
You can use an if condition like this:
if arr1
arr1[i]
end
Or use short-circuit technique like this:
arr1 && arr1[i]
If you sure that the relevant code snippet was working for ruby and it's not for rails, the problem is most likely due to variable i used at inner and outer loops both. In any case, this needs to be fixed first or else it will result in unexpected behaviour only.
Outer Loops:
for i in 46..100
Inner Loops:
for i in 0..size1
for i in 0..size2
...
Keep i for outer loop and change the inner loop iterator to j
Hope it Helps : )
Adding to the answers of #harish and #arslan, there may be a case where, temp2=Student.find_by(id:i) may fail because there may not be a student with that id.
So, temp2 may return nil at that time.
for i in 0..size2
arr2.push(table_val[i]) // this may get failed
end
Then arr2.push will not work because table_val[i] is nil, so there are chances of undefined method [] for nil class.

return result back to view

i am trying to keep all my logic out of views, and have come up with the following piece of code, the though im having is that it isnt returning the actual score value, it just returns if it was a Win, lost or tie
def find_result(schedule)
return "not required" if schedule.event != '1' or schedule.time >= Time.now
if schedule.for.nil? or schedule.against.nil?
"Not Entered"
else
tie = '<b>T</b> '
tie << schedule.for.to_i
tie << ' - '
tie << schedule.against.to_i
win = '<b>W</b> '
win << schedule.for.to_i
win << ' - '
win << schedule.against.to_i
return raw tie if schedule.for.to_i == schedule.against.to_i
schedule.for.to_i > schedule.against.to_i ? (raw win) : "Lost"
end
end
Don't use << with an integer. See the docs:
http://www.ruby-doc.org/core-1.9.3/String.html#method-i-3C-3C
It's probably turning your win/loss numbers into characters that aren't showing up in the HTML.
Use a formatter or something, or perhaps just to_s rather than to_i when appending the numbers.
Example using string format (untested):
def find_result(schedule)
return "not required" if schedule.event != '1' or schedule.time >= Time.now
if schedule.for.nil? or schedule.against.nil?
"Not Entered"
elsif schedule.for.to_i < schedule.against.to_i
"Lost"
else
raw "<b>%s</b> %d - %d" % [
schedule.for.to_i == schedule.against.to_i ? 'T' : 'W',
schedule.against.to_i,
schedule.for.to_i
]
end
Edit: Refactor
Keeping logic out of the views is good, but it would be even more appropriate to
move some of this to the model, namely the result of the schedule (not entered,
win, loss, tie)
In the example I'll make a simple inner class which encapsulates that logic, which
the Schedule makes use of to know its own result. You could do this any number of ways
though (e.g. a module versus a class, or methods directly on Schedule)
I'll then demonstrate how you might use the new schedule in your helper using the logic provided, or simply querying for the result itself and using it as a key for a translation lookup (I18n).
Note this is untested and a little bit pseudo-codey (I'm not using any I18n library in particular, just guessing at methods and translation formatting). But it should work with some tweaking, or at least give you an idea of another way of doing things.
class Schedule
# The schedule jus instantiates a result object when you ask for one.
# For convenience the result's to_s is it's value, e.g. "win"
def result
Result.new(self.for, self.against)
end
# delegate methods querying the result
delegate :win?, :loss?, :tie?, :not_entered?, :to => :result
class Result
Values = %(win loss tie not_entered)
Win = Values[0]
Loss = Values[1]
Tie = Values[2]
NotEntered = Values[3]
attr_reader :for, :against
def initialize(_for, against)
#for = _for
#against = against
end
def value
return NotEntered unless [#for, #against].all?
case v = #for - #against
when v.zero? then Tie
when v > 0 then Win
else Loss
end
end
alias :to_s :value
def not_entered?; self.value == NotEntered end
def win?; self.value == Win end
def loss?; self.value == Loss end
def tie?; self.value == Tie end
end
end
# then in your helper, something like
def find_result(schedule)
# you'd want to refactor this requirement part too
return "not required" if schedule.event != '1' or schedule.time >= Time.now
# Now you could do it essentially the way you had, with ifs or a
# case statement or what have you, but the logic for the result is kept
# where it belongs, on the class.
if schedule.not_entered?
"Not Entered"
elsif schedule.loss?
"Loss"
else
prefix = schedule.win? ? "W" : "T"
raw "<b>%s</b> %d - %d" % [prefix, schedule.for, schedule.against]
end
# OR you could use some kind of translation library using the `value`
# returned by the result. Something like:
key = ["schedule", schedule.outcome.value].join(".")
raw I18n.translate(key, {:for => schedule.for, :against => schedule.against})
end
# if you used the latter, it would lookup the translation in some other place,
# e.g. some config JSON, which might look like this (more or less, and
# depending on the lib you use):
{
"schedule": {
"win": "<b>W</b> {{for}} - {{against}}",
"tie": "<b>T</b> {{for}} - {{against}}",
"loss": "Loss",
"not_entered": "Not Entered"
}
}
# The translation has a few advantages. It would allow you to sub in other
# languages, but also, it conveniently keeps all of the app's text in one
# place, if you stick to using it.

Ruby on Rails: Execute Logic Based on Selected Menu

I have a class that I use to contain select menu options for property types. It works fine. However, I need to be able to verify the selection and perform specific logic based on the selected option. This needs to happen in my Ruby code and in JavaScript.
Here is the class in question:
class PropertyTypes
def self.[](id)
##types[id]
end
def self.options_for_select
##for_select
end
private
##types = {
1 => "Residential",
2 => "Commercial",
3 => "Land",
4 => "Multi-Family",
5 => "Retail",
6 => "Shopping Center",
7 => "Industrial",
8 => "Self Storage",
9 => "Office",
10 => "Hospitality"
}
##for_select = ##types.each_pair.map{|id, display_name| [display_name, id]}
end
What is the best way to verify the selection? I need to perform specific logic and display user interface elements based on each type of property type.
Since I am storing the id, I would be verifying that the id is a particular property type. Something like:
PropertyTypes.isResidential?(id)
Then this method would look like this:
def self.isResidential?(id)
##types[id] == "Residential"
end
But now I am duplicating the string "Residential".
For JavaScript, I assume I would make an ajax call back to the model to keep the verification code DRY, but this seems like over kill.
Do I need to manually create a verification method for each property type or can I use define_method?
This seems so basic yet I am confused and burned out on this problem.
Thanks
===
Here's my solution:
class << self
##types.values.each do |v|
# need to remove any spaces or hashes from the found property type
v = v.downcase().gsub(/\W+/, '')
define_method "is_#{v}?", do |i|
type_name = ##types[i]
return false if type_name == nil #in case a bogus index is passed in
type_name = type_name.downcase().gsub(/\W+/, '')
type_name == v
end
end
end
It sounds like you can benefit from some Ruby meta-programming. Try googling "ruby method_missing". You can probably do something quick & dirty along the lines of:
class PropertyTypes
def method_missing(meth, *args, &block)
if meth.to_s =~ /^is_(.+)\?$/
##types[args.first] == $1
else
super
end
end
end
On the ruby side you could also use something like this to define dynamically these methods:
class << self
##types.values.each do |v|
define_method "is_#{v}?", do |i|
##types[i] == v
end
end
end

Resources