Rails: Conditional statement with && and || - ruby-on-rails

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.

Related

Use instance variable in "delete_if" method in ruby on rails

I am using
#registers.delete_if { |r| r.location_id != #location_id && #location_id.present?}
to filter the registers so that only the ones with the chosen location_id are kept, if anything was chosen at least. The thing is that using the #location_id.present? works just fine but checking if the id's are the same doesn't. If I check hardcoded with a number like this: r.location_id != 5 it does work.
Try it like this :
#registers.delete_if { |r| r.location_id != #location_id.to_i && #location_id.present?}
But I think to do it better you should do it like :
#registers.delete_if { |r| r.location_id != #location_id.to_i if #location_id.present?}

How to use multiple #select blocks in Ruby?

Let's say I have a list of records like:
transactions = Transaction.all
And I have the following instance methods #currency, #geo, #industry. I want to select records which has the following criteria:
Select all transactions that has field currency which equals to #currency unless #currency is nil and in this case we'll ignore the condition (currency would mean all currencies when it's nil)
Select all transactions that has field geo which equals to #geo unless #geo is nil.
Select all transactions that has field industry which equals to #industry unless #industry is nil.
I tried multiple #select but with no luck something like:
transactions.select{ |i| (i.currency == #currency) unless #currency.nil? }.
.select{ |i| (i.geo == #geo) unless #geo.nil? }.
.select{ |i| (i.industry == #industry) unless #industry.nil? }
The problem with your example is the unless #currency.nil? will return nil (which is falsey) if #currency is nil, which is the opposite of what you intended.
You should use || instead:
transactions.select{ |i| (i.currency == #currency) || #currency.nil? }.
select{ |i| (i.geo == #geo) || #geo.nil? }.
select{ |i| (i.industry == #industry) || #industry.nil? }
In this case, if #currency is nil, the first condition will return true, and all elements will pass the select box to the next one...
Another option would be to run the select block only is the parameter is not nil. In this case, you'd like to break the line into separate blocks:
transactions.select!{ |i| (i.currency == #currency) } unless #currency.nil?
transactions.select!{ |i| (i.geo == #geo) } unless #geo.nil?
transactions.select!{ |i| (i.industry == #industry) } unless #industry.nil?
transactions.select do |t|
(#currency.nil? || t.currency == #currency) &&
(#geo.nil? || t.geo == #geo) &&
(#industry.nil? || t.industry == #industry)
end
this should do the job.
Or, if you are into dynamics:
[:currency, :geo, :industry].all? do |field|
(ivar = instance_variable_get("##{field}")).nil? || t.send(field) == ivar
end
Use AR/SQL instead of Ruby processing when possible:
transactions.where(currency: #currency, geo: #geo, industry: #industry)
Multiple use of select is superfluous in this situation. You can use && and || logical operators:
transactions.select do |transaction|
(#currency.nil? || transaction.currency == #currency) &&
(#geo.nil? || transaction.geo == #geo) &&
(#industry.nil? || transaction.industry == #industry)
end

ruby - refactoring if else statement

I've tried reading some tutorials on refactoring and I am struggling with conditionals. I don't want to use a ternary operator but maybe this should be extracted in a method? Or is there a smart way to use map?
detail.stated = if value[:stated].blank?
nil
elsif value[:stated] == "Incomplete"
nil
elsif value[:is_ratio] == "true"
value[:stated] == "true"
else
apply_currency_increment_for_save(value[:stated])
end
If you move this logic into a method, it can be made a lot cleaner thanks to early return (and keyword arguments):
def stated?(stated:, is_ratio: nil, **)
return if stated.blank? || stated == "Incomplete"
return stated == "true" if is_ratio == "true"
apply_currency_increment_for_save(stated)
end
Then...
detail.stated = stated?(value)
stated = value[:stated]
detail.stated = case
when stated.blank? || stated == "Incomplete"
nil
when value[:is_ratio] == "true"
value[:stated] == "true"
else
apply_currency_increment_for_save stated
end
What's happening: when case is used without an expression, it becomes the civilized equivalent of an if ... elsif ... else ... fi.
You can use its result, too, just like with if...end.
Move the code into apply_currency_increment_for_save
and do:
def apply_currency_increment_for_save(value)
return if value.nil? || value == "Incomplete"
return "true" if value == "true"
# rest of the code. Or move into another function if its too complex
end
The logic is encapsulated and it takes 2 lines only
I like #Jordan's suggestion. However, it seems the call is incomplete -- the 'is_ratio' parameter is also selected from value but not supplied.
Just for the sake of argument I'll suggest that you could go one step further and provide a class that is very narrowly focused on evaluating a "stated" value. This might seem extreme but it fits with the notion of single responsibility (the responsibility is evaluating "value" for stated -- while the 'detail' object might be focused on something else and merely makes use of the evaluation).
It'd look something like this:
class StatedEvaluator
attr_reader :value, :is_ratio
def initialize(value = {})
#value = ActiveSupport::StringInquirer.new(value.fetch(:stated, ''))
#is_ratio = ActiveSupport::StringInquirer.new(value.fetch(:is_ratio, ''))
end
def stated
return nil if value.blank? || value.Incomplete?
return value.true? if is_ratio.true?
apply_currency_increment_for_save(value)
end
end
detail.stated = StatedEvaluator.new(value).stated
Note that this makes use of Rails' StringInquirer class.

Two conditions in a ruby block

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.

Check if not nil and not empty in Rails shortcut?

I have a show page for my Users and each attribute should only be visible on that page, if it is not nil and not an empty string. Below I have my controller and it is quite annoying having to write the same line of code #user.city != nil && #user.city != "" for every variable. I am not too familiar with creating my own methods, but can I somehow create a shortcut to do something like this: #city = check_attr(#user.city)? Or is there a better way to shorten this procedure?
users_controller.rb
def show
#city = #user.city != nil && #user.city != ""
#state = #user.state != nil && #user.state != ""
#bio = #user.bio != nil && #user.bio != ""
#contact = #user.contact != nil && #user.contact != ""
#twitter = #user.twitter != nil && #user.twitter != ""
#mail = #user.mail != nil && #user.mail != ""
end
There's a method that does this for you:
def show
#city = #user.city.present?
end
The present? method tests for not-nil plus has content. Empty strings, strings consisting of spaces or tabs, are considered not present.
Since this pattern is so common there's even a shortcut in ActiveRecord:
def show
#city = #user.city?
end
This is roughly equivalent.
As a note, testing vs nil is almost always redundant. There are only two logically false values in Ruby: nil and false. Unless it's possible for a variable to be literal false, this would be sufficient:
if (variable)
# ...
end
This is preferable to the usual if (!variable.nil?) or if (variable != nil) stuff that shows up occasionally. Ruby tends to wards a more reductionist type of expression.
One reason you'd want to compare vs. nil is if you have a tri-state variable that can be true, false or nil and you need to distinguish between the last two states.
You can use .present? which comes included with ActiveSupport.
#city = #user.city.present?
# etc ...
You could even write it like this
def show
%w(city state bio contact twitter mail).each do |attr|
instance_variable_set "##{attr}", #user[attr].present?
end
end
It's worth noting that if you want to test if something is blank, you can use .blank? (this is the opposite of .present?)
Also, don't use foo == nil. Use foo.nil? instead.

Resources