Rails 3.2 CRUD : .where with 'or' conditional - ruby

With ruby on rails, I want to do something like:
#tasks = Task.where(:def => true || :house_id => current_user.house_id)
What is the most efficient/clean way to do this?

You can do it like this:
Task.where("def = ? or house_id = ?", true, current_user.house_id)
The general case is:
Model.where("column = ? or other_column = ?", value, other_value)
You can also leverage Arel:
t = Task.arel_table
#tasks = Task.where(
t[:def].eq(true).
or(t[:house_id].eq(current_user.house_id))
)

Related

Dynamic variable names in Ruby

#baseball_games = 0
#basketball_games = 0
#football_games = 0
Game.all.each do |game|
instance_variable_set("##{game.sport.name.downcase}_games", instance_variable_get("##{game.sport.name.downcase}_games") + 1)
end
Is there a better way to do this, than calling the get method inside the set method? I really am just trying to += the dynamic variable...
Building upon #Santosh's answer, you could do this more generally:
#games = Sport.all.map { |sport| [sport.name.to_sym, sport.games.count] }.to_h
Another solution, without the loop (Assuming the relation is sport has_many games)
#games = {
:baseball => Sport.find_by_name('baseball').games.count,
:basketball => Sport.find_by_name('basketball').games.count,
:football => Sport.find_by_name('football').games.count
}
#games = {:baseball => 0, :basketball => 0, :football => 0 }
Game.all.each do |game|
#games[game.sport.name.downcase.to_sym] = #games[game.sport.name.downcase.to_sym] + 1
end

What is a more readable way to test for one of three strings?

Is there a more readable way to test if delivery_status is one of three strings?
if ["partial", "successful", "unsuccessful"].include? delivery_status
Here's what I'd really like, but it doesn't work:
if delivery_status == ("partial" or "successful" or "unsuccessful")
While I would not advise this, you can do it anyway:
def String
def is_one_of?(array)
array.include?(self)
end
end
And then:
if delivery_status.is_one_of?([...])
But there is a much better solution: use case (if possible in your situation):
case delivery_status
when 'partial', 'successful', 'unsuccessful'
#stuff happens here
when ... #other conditions
end
if %w[partial successful unsuccessful].include? delivery_status
It's not intuitive, but using the Regexp engine can speed these tests up:
STATES = ["partial", "successful", "unsuccessful"]
regex = /\b(?:#{ Regexp.union(STATES).source })\b/i
=> /\b(?:partial|successful|unsuccessful)\b/i
delivery_status = 'this is partial'
!!delivery_status[regex]
=> true
delivery_status = 'that was successful'
!!delivery_status[regex]
=> true
delivery_status = 'Yoda says, "unsuccessful that was not."'
!!delivery_status[regex]
=> true
delivery_status = 'foo bar'
!!delivery_status[regex]
=> false
If I'm not searching a string for the word, I'll use a hash for a lookup:
STATES = %w[partial successful unsuccessful].each_with_object({}) { |s, h| h[s] = true }
=> {"partial"=>true, "successful"=>true, "unsuccessful"=>true}
STATES['partial']
=> true
STATES['foo']
=> nil
Or use:
!!STATES['foo']
=> false
If you want a value besides true/nil/false:
STATES = %w[partial successful unsuccessful].each_with_index.with_object({}) { |(s, i), h| h[s] = i }
=> {"partial"=>0, "successful"=>1, "unsuccessful"=>2}
That'll give you 0, 1, 2 or nil.
I ended up doing something similar to #Linuxios's suggestion
class String
def is_one_of(*these)
these.include? self
end
def is_not_one_of(*these)
these.include? self ? false : true
end
end
This allows me to write:
if delivery_status.is_one_of "partial", "successful", "unsuccessful"

If Statement in ruby on rails

I have a task to list students' details. I want to perform searching based on name and city. If cityId==0 and name='', then I want to list all my student details. How can I do this? I did this in a wrong way. The controller is:
if(Student.where(params[:cityId])==0)
studentcount = Student.count()
#students = Student.limit(params[:jtPageSize]).offset(params[:jtStartIndex]).order(params[:jtSorting])
#jtable = {'Result' => 'OK','Records' => #students.map(&:attributes), :TotalRecordCount => studentcount}
else
studentcount = Student.where("name LIKE ? AND city = ?", "%#{params[:name]}%", params[:cityId]).count()
#students = Student.where("name LIKE ? AND city = ?", "%#{params[:name]}%", params[:cityId]).limit(params[:jtPageSize]).offset(params[:jtStartIndex]).order(params[:jtSorting])
#jtable = {'Result' => 'OK','Records' => #students.map(&:attributes), :TotalRecordCount => studentcount}
Your condition should look like this:
if( Student.where(:cityId => params[:cityId]).count == 0 )
The if-statement you have tests a ActiveRecord::Relation and an Integer for equality which will never be true
if User.where(city_id: params[:cityId]).empty?

In Ruby, how to conditionally add several members to an object?

I would like to create a hash with several members added conditionally.
The simple approach is:
var a = {}
a['b'] = 5 if condition_b
a['c'] = 5 if condition_c
a['d'] = 5 if condition_d
Now, I would like to write a more idiomatic code. I am trying:
a = {
b => (condition_b? 5 : null),
c => (condition_c? 5 : null),
d => (condition_d? 5 : null)
}
But now, a.length equals 3 whatever conditions are met. This is not the desired result.
Is there a handy solution?
May be not exactly what you want but this can help
array = ['b','c','d'] ##array of values you want as keys
a ={}
array.each do |val|
a[val] = 5 if send("condition_"+val.to_s) #call method condition_#{key_name}
end
If the conditions are not related you can use your own hash and you can
a = {
b => (condition_b? 5 : nil),
c => (condition_c? 5 : nil),
d => (condition_d? 5 : nil)
}
a.values.compact.size
to get length of values other then nil
How about you only add to the hash if the condition is met, like this:
a = {}
a.merge!({'b'=>5}) if condition_b
a.merge!({'c'=>5}) if condition_c
In the second way, you're always going to have the three keys; the conditions only determine the particular values. So if you want to use that syntax, you will need to define a custom method that only counts the keys if they are not nil (also, it's nil in Ruby, not null). Something like:
def non_nil_length(hash)
i = 0
hash.each_pair do |k,v|
if !v.nil?
i += 1
end
end
i
end
There's probably a better way to do that, but there's the quick and dirty.

Why do I get "The error occurred while evaluating nil.<=>" when using sort_by?

This is the code:
xml = REXML::Document.new(data)
#contacts = Array.new
xml.elements.each('//entry') do |entry|
person = {}
person['name'] = entry.elements['title'].text
gd_email = entry.elements['gd:email']
person['email'] = gd_email.attributes['address'] if gd_email
#contacts << person
end
#contacts.sort_by { |k| k['name'] } if #contacts[0].size > 0
the error:
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.<=>
Try using:
person['name'] = entry.elements['title'].text || ''
instead of:
person['name'] = entry.elements['title'].text
Shouldn't the last line be
#contacts.sort_by { |k| k['name'] } if #contacts.size > 0
not #contacts[0].size ?
Also, try adding a #contacts.compact! before sorting to ensure you have no nil values in the array.
I think you can streamline your code a bit:
#contacts = Array.new
xml = REXML::Document.new(data)
xml.elements.each('//entry') do |entry|
gd_email = entry.elements['gd:email']
#contacts << {
'name' => entry.elements['title'].text,
'email' => (gd_email) ? gd_email.attributes['address'] : ''
}
end
#contacts.sort_by! { |k| k['name'] }
I don't have samples of your XML to test it, but it looks like it should work.
If the element['title'] is null you'll get the error you are seeing so you'll want to either skip those elements or use a default value for the name field, like "unknown".

Resources