Two conditions in a ruby block - ruby-on-rails

I am trying to do something like this:
products = products.select { |product|
product.quantity > 0 || (
product.has_attribute?(:permit_negative_quantity) &&
product.permit_negative_quantity == true)
)
}
I am trying to leave only products association that has positive quantity, or, if the quantity is not positive, has the attribute permit_negative_quantity and it is set totrue.
This block keeps rejecting the products with a negative quantity. Am I missing something in the syntax? Is there a better way to do it?

It seems higher priority of && is causing this, try following:
products = products.select { |product| (product.quantity > 0 || (product.has_attribute?(:permit_negative_quantity) && product.permit_negative_quantity == true)) }

Ok finally i saw what i made wrong.
It was the :trueat the end of the condition which have to be truesince the table column is a boolean.

Related

Rails: Conditional statement with && and ||

I currently have this:
<% if h.ab_template.AB_filterParent == 0 && h.status != 'geo_enabled' %>
What I'd like to do is say whether if h.ab_template.AB_filterParent == 0 || nil. How would I do that?
I tried this but it wasn't working:
<% if (h.ab_template.AB_filterParent == 0 || nil) && h.status != 'geo_enabled' %>
Would love to know what I've mistyped or implemented incorrectly!
Cheers
If you are sure that h.ab_template.AB_filterParent will always be a number (can be wrapped by quotes), then you can try following
<% if h.ab_template.AB_filterParent.to_i == 0 && h.status != 'geo_enabled' %>
else, if there is a possibility that h.ab_template.AB_filterParent can be something like "abc", "0asd" etc then try:
<% if (h.ab_template.AB_filterParent.nil? || h.ab_template.AB_filterParent == 0) && h.status != 'geo_enabled' %>
Generally nil and zero shouldn't mean the same thing. Try to eliminate the possibility of AB_filterParent being nil before you hit this code, by assigning a default value of zero in your migration table. I don't know how your model is so I can't even show an example of how to do it.
The main problem of using to_i == 0 is that it only works if AB_filterParent is either an integer or nil.
0.5.to_i == 0
"asdasd".to_i == 0
So it's odd.
Another way is to have it initialized in an action or other model method or even after_create callback.

Filtering by value calculated in instance method

I have a House model that has a price attribute which is calculated using an instance method.
Let's say that the price is constantly changing due to market rates, inflation, etc. Thus we need a price_today method to get the current price.
How can I create a named scope (or something) to filter the houses using a maximum price and minimum price?
I tried doing something like this, but I feel it's a little hacky...
def index
#houses = # get houses from DB
if not params[:max_price].nil?
#houses.keep_if { |h| h.price_today <= params[:max_price].to_f }
end
if not params[:min_price].nil?
#houses.keep_if { |h| h.price_today >= params[:min_price].to_f }
end
end
A named scope (or a where clause) would definitely be faster that querying and filtering the instantiated objects.
However, I'm afraid that if price_today does not exist in the DB you won't be able to use them.
You can improve your code, though.
The conditions can be made simpler, and you can use a single call to keep_if.
max = params[:max_price].presence
min = params[:min_price].presence
if max || min
#houses.keep_if do |house|
a = max ? (house.price_today <= max.to_f) : true
b = min ? (house.price_today >= min.to_f) : true
a && b
end
end
If you want, instead of keep_if you can use select.
Another variation:
{ max_price: ->(price, max) { price <= max },
min_price: ->(price, min) { price >= min } }.each { |prop, cond|
#houses.select! { |h| cond.(h.price_today, params[prop].to_f) } unless params[prop].nil?
}
In case of lambda for particular property you can easily add another conditions=)

Filter an array with many parameters "like an SQL query"

I need to filter objects in array.
It works with one parameters
#usersc = #usersb.select { |user| user.need_appartment? }
but i would like use more parameters than in SQL/ActiveRecord :
(need_bedrooms_min >= :nb_bedrooms_min) AND (budget_amount BETWEEN :budget_min AND :budget_max) AND ((need_surface_min BETWEEN :surface_min AND :surface_max) OR (need_surface_max BETWEEN :surface_min AND :surface_max))"+req,{nb_bedrooms_min: params[:nb_bedrooms_min], budget_min: params[:budget_min], budget_max: params[:budget_max],surface_min: params[:surface_min], surface_max: params[:surface_max]}).paginate(:page => params[:page])
I dont find the solution... Anyone can help me ?
F.
select does exactly what you need with as many parameters as you might want:
#usersb.select do |user|
user.need_bedrooms_min >= params[:nb_bedrooms_min].to_i &&
(params[:budget_min].to_i..params[:budget_max].to_i).include? user.budget_amount &&
((params[:surface_min].to_i..params[:surface_max].to_i).include? user.need_surface_min ||
(params[:surface_min].to_i..params[:surface_max].to_i).include? user.need_surface_max)
end
Or, more cleanly:
class User
def needs_apartment?(params)
budget_min, budget_max, surface_min, surface_max, nb_bedrooms_min =
%w{budget_min budget_max surface_min surface_max nb_bedrooms_min}.map{|k| params[k.to_sym].to_i}
budget_range = budget_min..budget_max
surface_range = surface_min..surface_max
need_bedrooms_min >= nb_bedrooms_min &&
budget_range.include? budget_amount &&
(surface_range.include?(need_surface_min) || surface_range.include?(need_surface_max))
end
end
#usersb.select{|user| user.needs_apartment?(params)}

Rails Search and Category Dropdown (No Method Error)

I have the following in my events controller:
def index
#event = Event.search(params[:search]).events_by_category(params[:cat]).order(...).paginate(...)
end
And in my events model, I have the following class method:
def self.events_by_category(cat)
if cat == 0
all
elsif cat && cat != 0
where('category = ?', cat)
else
scoped
end
end
And in my view I have a standard search box for the search and dropdown for the category selection. The category options_for_select has an array that includes ["All Categories", 0] in it.
My question is: Why does this return no results instead of all results when All Categories is selected in the dropdown. And, when I change the array to ["All Categories", "ALL"] and the if statement to if cat == "ALL" it returns Undefined method 'order'?
I think it has something to do with stringing the search and events_by_category together in the controller, but searches and categories work just fine in conjunction when it's not All Categories being selected...
Any help would be GREATLY appreciated!
To answer your second question first: the problem is that your cat == "ALL" condition returns Event.all, which is a Ruby Array, not an ActiveRecord::Relation. order is an activerecord method not an array method, that's why you're getting the error Undefined method 'order'.
If you want to return all results for the category ALL, then change all to scoped (which will return a scope with no conditions on it). That will be chainable, so that you can call it with order and paginate.
As to your first question, params[:cat] is a string so you should be checking whether cat == "0" not cat == 0. I think that should solve the problem.
Your conditional, by the way, is a bit convoluted: you're testing if cat is 0, then checking that it is not 0 in the else statement, but you already know that it is not 0. I'd suggest simplifying your method code to this:
def self.events_by_category(cat)
(cat && cat != "0") ? where('category = ?', cat) : scoped
end
This says: if the category is present and not "0" (i.e. not the category "all results"), then return results for that category, if not return all results.
def self.events_by_category(cat)
if cat == "0"
all
elsif cat && cat != "0"
where('category = ?', cat)
else
scoped
end
end
The above will work.

Rails - IF Block with complex statements, how to optimize w/o reducing Readability?

in ruby/rails3, I need to do some heavy text parsing to find a certain string. Right now I'm doing something like the following:
extract_type1 = body.scan(/(stuff)/m).size
extract_type2 = body.scan(/(stuff)/m).size
extract_type3 = body.scan(/(stuff)/m).size
extract_type4 = body.scan(/(stuff)/m).size
extract_type5 = body.scan(/(stuff)/m).size
if extract_type1 > 0
elsif extract_type2 > 0
elsif extract_type3 > 0
elsif extract_type4 > 0
elsif extract_type5 > 0
The problem here is that I keep needing to add extract types based on the app. And that results in a lot of processing when the case occurs that extract_type1 >0 and the rest aren't needed.
But at the same time, it's nice and clean to have the extract logic separated from the if block as that would be busy messy and hard to read.
Any thoughts on how to optimize this while not compromising readability?
Thanks
what about storing all your "keywords" you are searching for in an array and iterate over it like:
stuff = ["stuff1", "stuff2"]
stuff.each do |c_stuff|
if body.scan(/(#{Regexp.escape(c_stuff)})/m).size > 0
# do something
# break the loop
break
end
end
Edit: If you need the index of the element, you can use each_with_index do |c_stuff, c_index|
Lazy evaluation might work for you; just convert your extract_X variables to lambdas so that the values are computed on use:
extract_type1 = lambda { body.scan(/(stuff)/m).size }
extract_type2 = lambda { body.scan(/(stuff)/m).size }
extract_type3 = lambda { body.scan(/(stuff)/m).size }
extract_type4 = lambda { body.scan(/(stuff)/m).size }
extract_type5 = lambda { body.scan(/(stuff)/m).size }
if extract_type1.call > 0
elsif extract_type2.call > 0
elsif extract_type3.call > 0
elsif extract_type4.call > 0
elsif extract_type5.call > 0
If you're using the extract_X values more than once then you can add memoization to the lambdas so that the values are computed on first access and then cached so that subsequent accesses would just use the value that was already computed.

Resources