If condition and Short circuit evaluation in rails - DRY - ruby-on-rails

I m doing an app and I need to add multiple condition if. I want to know if an solution exist to DRY.
I m tried something like this but that dont work, it's like https://en.m.wikipedia.org/wiki/Short-circuit_evaluation
<% #camping.situations.each do |situation| %>
<%if situation.plage == "oui" || situation.etang == "oui" || situation.lac == "oui" %>
Do that
<%else%>
Do this
<%end%>
<%end%>
For my app to test more than 30 conditions so i dont want to repeat myself with multiple IF.
Example : Plage = oui / etang = Non / lac = oui
I want to display in my view :
Do that
Do this
Do that
Do you have any suggestions ?
EDIT
I m sorry i think my message wasn't clear. So for each entries I have 3 possible value 1) OUI 2) NON 3) EMPTY. User add the value of his object.
So in my view i display like this
<% #camping.caracteristiquetests.each do |caracteristiquetest| %>
<%if caracteristiquetest.animaux =="oui"%>animaux<%else%><s>animaux</s><%end%>
<%if caracteristiquetest.barbecue=="oui"%>barbecue<%else%><s>barbecue</s><%end%>
<%if caracteristiquetest.handicap =="oui"%>handicap<%else%><s>handicap</s><%end%>
<%if caracteristiquetest.piscine=="oui"%>piscine<%else%><s>piscine</s><%end%>
<%if caracteristiquetest.jeux =="oui"%>jeux<%else%><s>jeux</s><%end%>
<%if caracteristiquetest.loisir=="oui"%>loisir<%else%><s>loisir</s><%end%>
<%end%>
<!--Affiche les parametres de la table situation-->
<% #camping.situations.each do |situation| %>
<%if situation.plage =="oui"%>plage<%else%><s>plage</s><%end%>
<%if situation.etang =="oui"%>etang<%else%><s>etang</s><%end%>
<%if situation.montagne =="oui"%>montagne<%else%><s>montagne</s><%end%>
<%if situation.riviere =="oui"%>riviere<%else%><s>riviere</s><%end%>
<%if situation.foret=="oui"%>foret<%else%><s>foret</s><%end%>
<%end%>
If value is different to "oui" my view show Strikethrough.
I have more entries but it's always the same condition. As you can see i have lot of "IF". So i just wanna know if it's possible to DRY this ?

You could do it as follows.
The code situation.members.any?{|m| situation.send(m) == 'oui'} enumerates the members (attributes) of your situation object and compares the value of them each by doing a send of the membername against the string 'oui', if any of them contains it the whole expression is true.
The ternary statement ? evaluates this and puts the "Do that" if true and the "Do this" else.
situation = Struct.new(:plage, :etang, :lac)
situations = []
situations << situation.new('non','oui','non')
situations << situation.new('non','non','non')
situations.each do |situation|
puts situation.members.any?{|m| situation.send(m) == 'oui'} ? "Do that" : "Do this"
end
# gives
# Do that
# Do this
If I understand your last edit correct, this would become
situations.each do |situation|
situation.members.each{|member| puts situation.send(member) == 'oui' ? "Do that" : "Do this"}
end
You really should make your question more clear next time.
Based on your last edit, my last attempt, you just need to go one level deeper.
We just give you the methods how to do something, the rest is up to you.
More Dry than this it won't become..
camping = Struct.new(:situation, :caracteristiquetest)
campings = []
situation = Struct.new(:plage, :etang, :lac, :montagne, :riviere, :foret)
caracteristiquetest = Struct.new(:animaux, :barbecue, :handicap, :piscine, :jeux, :loisir)
campings <<
camping.new(situation.new('non','oui','non','non','oui','non'),
caracteristiquetest.new('non','oui','non','non','oui','non'))
campings <<
camping.new(situation.new('oui','oui','non','non','oui','non'),
caracteristiquetest.new('non','oui','non','non','non','non'))
campings.each do |camping|
camping.members.each do |camping_attr|
camping.send(camping_attr).members.each{|member|
puts camping.send(camping_attr).send(member) == 'oui' ? member : "<s>#{member}</s>"
}
end
end
Which gives
<s>plage</s>
etang
<s>lac</s>
<s>montagne</s>
riviere
<s>foret</s>
<s>animaux</s>
barbecue
<s>handicap</s>
<s>piscine</s>
jeux
<s>loisir</s>
plage
etang
<s>lac</s>
<s>montagne</s>
riviere
<s>foret</s>
<s>animaux</s>
barbecue
<s>handicap</s>
<s>piscine</s>
<s>jeux</s>
<s>loisir</s>

Related

How to use variable from link

I have a link which reads
Collection
and another which reads
Wantlist
They link to the same view but produce different lists.
On that view page I want to be able to alter the text depending on if it is a Collection or Wantlist page. Something like this:
<% if :status == 'Got' %>collection<% elsif :status == 'Want' %>wantlist<% end %>
Obviously that doesn't work but after much experimentation I can't work out how to query which status was passed in the link. Is this possible?
You should be doing it like this
<% if params[:status] == 'Got' %>Collection
<% elsif params[:status] == 'Want' %>Wantlist
<% end %>
The parameters you pass to link_to get put into the params hash. Here, params[:status] is how you'd access it, either in the view or - better - in the controller.
link_to_if might be alternate solution for your question.
link_to_if(params[:status] == 'got', "Collection", user_collections_path(#user)) do
link_to "Wantlist" user_collections_path(#user)
end

update_attribute using where clause in ruby

In my communication table I have some columns:
id | UserID | CommunicationMode | CommunicationDetail | Private
1 | 1 | Phone | 123456789 | 1
2 | 1 | Email | abc#abc.com | 1
And I want to update column value using where clause using loop like below:
create
#user_communication=Communication.where(:UserID => current_user.id)
if !#user_communication.blank?
#user_communication.each do |c|
if params[:ChkBx_Phone].to_i == 1
c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "1")
elsif params[:ChkBx_Phone].to_i == 0
c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "0")
end
if params[:ChkBx_Email].to_i == 1
c.where("CommuicationMode == 'Email'").update_attribute( :Private, "1")
elsif params[:ChkBx_Email].to_i == 0
c.where("CommunicationMode == 'Email'").update_attribute( :Private, "0")
end
end
end
end
I want to check above that if Phone checkbox is checked then it updates Private column with value 1 else 0 where CommunicationMode is Phone and for email I want to check that if Email checkbox is checked then it updates Private column with value 1 else 0 where CommunicationMode is Email
And below is Phone and Email checkboxes:
<table>
<% #user_communication.each do |c| %>
<tr>
<td>
<% if c.CommunicationMode == "Phone" and c.Private.to_s == "1" %>
<input type="checkbox" name="ChkBx_Phone"
id="ChkBx_Phone" value="1" checked = "checked">
<%= label(:lb_Phone, "Phone") %>
<% elsif c.CommunicationMode == "Phone" and c.Private.to_s == "0" %>
<%= check_box_tag 'ChkBx_Phone' %>
<%= label(:lb_Phone, "Phone") %>
<% end %>
</td>
</tr>
<tr>
<td>
<% if c.CommunicationMode == "Email" and c.Private.to_s == "1" %>
<input type="checkbox" name="ChkBx_Email"
id="ChkBx_Email" value="1" checked = "checked">
<%= label(:lb_Email, "Email") %>
<% elsif c.CommunicationMode == "Email" and c.Private.to_s == "0" %>
<%= check_box_tag 'ChkBx_Email' %>
<%= label(:lb_Email, "Email") %>
<% end %>
</td>
</tr>
<% end %>
</table>
But I am getting an error below:
undefined method `where' for #<Communication:0x4bc5490>
But when I am using below code:
create
#user_communication=Communication.where(:UserID => current_user.id)
if !#user_communication.blank?
#user_communication.each do |c|
if params[:ChkBx_Phone].to_i == 1
puts "Hassan2"
c.update_attribute( :Private, "1")
elsif params[:ChkBx_Phone].to_i == 0
puts "Ali2"
c.update_attribute( :Private, "0")
end
end
end
end
Its working fine but it update both Private column value of Phone and Email and I have checked only Phone checkbox.
Kindly suggest me, waiting for your reply.
Thanks.
As Kingston said, when you iterate through the #user_communication collection, the c variable in the block is in fact a concrete Communication object, not an ActiveRecord::Relation object, which is what contains the .where query methods.
Additionally, there are a couple of other problems I have noticed. Some, I cannot directly help you solve because I don't know how your system works, but I will point them out for you and try to suggest an alternative, and hopefully you can figure out the best way to fix them in the context of your system's requirement.
In regards to the create method, there are two approaches that will have the same end result, but one of them I would consider the "naïve approach". I'm going to show the naïve approach only to give you a direct alternative/answer to the first create method you posted, and so you can see the difference and figure out the best solution:
Naïve Approach:
def create
#user_communication=Communication.where(:UserID => current_user.id)
# Transaction so we don't execute X number of individual update statements
Communication.transaction do
#user_communication.each do |c|
if c.CommunicationMode == "Phone"
# Note: Its not necessary to use the ternary operator if you're not
# comfortable with it, however it can save you a few lines of code.
c.update(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )
elsif c.CommunicationMode == "Email"
c.update(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )
end
end # end communication loop
end # end transaction
end
This will iterate through the the Communication objects for that user id, and update each one based on its CommunicationMode value, setting it to a 1 or a 0, depending on the value of the two checkboxes. What you will see in your log are several individual UPDATE statements for each Communication object you have in the collection. Normally, the database will execute the UPDATE statement immediately, since the updates are wrapped in their own transactions, and consequently, this becomes rather costly over time if you have a large number of records. So to avoid that problem, as you can see I've wrapped the entire operation in a transaction, and consequently, the updates are only committed at the end; this is much more efficient (you can do some experiments to verify this yourself; you should be able to see a noticeable time difference with and without the transaction wrapper).
And now, the "potentially" better approach:
The "Potentially" better / less naïve approach
... other method code
Communication.where(UserID: current_user.id, CommunicationMode: "Phone").update_all(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )
Communication.where(UserID: current_user.id, CommunicationMode: "Email").update_all(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )
This will execute only two UPDATE statements (which you can also wrap in a transaction if you like). This should execute even quicker than the above approach, and saves you even more lines of code.
Note that both this and the naïve approach can be moved to a method in the Communication model, to keep heavy model-related operations out of the controller.
Another issue of note
In your view code, you appear to be iteration over a collection #user_communication, within which you're creating a checkbox for each object:
<input type="checkbox" name="ChkBx_Email" id="ChkBx_Email" value="1" checked = "checked">
Consequently, if you have five objects in that collection, you'll see a list of five checkboxes. Because of the way you've named this input, only one of the values is ever being sent (I believe it is typically the "last" input in the DOM). In other words, if you click "on" the first checkbox, but leave the last one "off", the value of the checkbox will be "off". I have a strong feeling this is probably not how you want your system to behave. Perhaps you will want to place those checkboxes outside of the loop (above), since they don't appear to have anything to do with the Communication objects themselves.
Additionally, an unchecked checkbox is never sent to the server. The line params[:ChkBx_Phone].to_i == 1 will crash if you unchecked that checkbox because params[:ChkBx_Phone] is nil, and you'd be invoking to_i on a nil object. This answer shows you an alternative for this solution. In your case, you will want to make sure of the Rails helper tags, such as check_box_tag, and hidden_field_tag, like so:
<%=hidden_field_tag 'ChkBx_Phone', '0'%>
<%=check_box_tag 'ChkBx_Phone', '1', true %>
As a result, if the checkbox is unchecked, because of the existence of the identically named hidden field input, the server will always receive a value for this parameter.
In your code
#user_communication.each do |c|
# Your logic
end
you looping #user_communication then c variable will contain Communication object not Active record relation,then you are doing c.where(),c contain Communication object. But .where Active record relation.so it is throwing error.

RAILS or in if statement

I'm not sure how to use the OR in a view IF statement.
This doesn't work:
<% if current_user.employee.emptype.typename == "ADMIN" or "TECH" %>
Thanks!
Write something like this :
<% if current_user.employee.emptype.typename == "ADMIN" || current_user.employee.emptype.typename == "TECH" %>
Or better
<% if ['TECH', 'ADMIN'].include?(current_user.employee.emptype.typename) %>
Be careful with OR and AND keywords, they don't have same operators priority than && and ||
You can do this one of two ways. I prefer the first method.
<% if current_user.employee.emptype.typename.in?(["ADMIN", "TECH"]) %>
...or...
<% if current_user.employee.emptype.typename == "ADMIN" || current_user.employee.emptype.typename == "TECH" %>
That won't work, and it has nothing to do with views. In Ruby, all strings evaluate to true in if statements.
You have to check both values against the type name, like so:
if current_user.employee.emptype.type_name == "ADMIN" || current_user.employee.emptype.type_name == "TECH"
or check if it's contained within an array of valid types:
if ["ADMIN", "TECH"].include?(current_user.employee.emptype.type_name)
Please also see this article for a comparison of || and or in Ruby.

Add a selected class to links depending on id

My question is very similar to this one : Changing Current Tab in Rails
I am trying to add a 'selected' class to my link regarding the id of my portfolios controller’s show action
Here is what I've tried:
<ul>
<% for portfolio in #portfolios %>
<li class="<%= controller.class == PortfoliosController and controller.action_name == 'show' and controller.params[:id] == portfolio.id ? 'selected' : '' %>"><%= link_to portfolio.name, portfolio %></li>
<% end %>
</ul>
But it seems that the following:
controller.params[:id] == portfolio.id
doesn't match correctly and I don't understand why..
Thanks for your help!
Beware: params are always strings.
So :
controller.params[:id].to_i == portfolio.id
SideNote: extract this kind of logic in helpers, it's much cleaner.
You might be comparing string and numerical values, which in Ruby are not considered equivalent. A conversion of one of or the other might help. In fact, if you roll this up in a helper method, it might make it a lot easier to follow:
def class_for_porfolio_entry(portfolio)
controller.class == PortfoliosController and
controller.action_name == 'show' and
controller.params[:id].to_i == portfolio.id ?
'selected' :
''
end
You've got a whole lot going on there, so you might want to look at ways of reducing the complexity, for instance using params instead:
def class_for_porfolio_entry(portfolio)
params[:controller] == 'portfolios' and
params[:action] == 'show' and
params[:id].to_i == portfolio.id ?
'selected' :
''
end
This could be simplified further if you had a boolean flag set in your controller that is later used as required, avoiding hard-coding something like this:
def class_for_porfolio_entry(portfolio)
if (#show_selected_portfolio and #portfolio.id == portfolio.id)
'selected'
else
''
end
end
This presumes you have an instance variable #portfolio, which is usually the case in any controller's show method, and that you will set #show_selected_portfolio to true in any controller method where this logic applies.

A blank? version for database fields which has 0 oder NULL?

in my database I have a field which holds foreign keys. Sometimes the values are NULL or 0.
I know the helper blank?. Is there something similar to enable if there is a number set in the field? Because blank doesn't work here.
the code for the view is something like this
<%= #b.author unless #b.author_id.blank? %>
you could write your own helper
def identified? author
author.id.blank? or author.id == 0
end
You could try something like:
<% if #b.author_id == 0 %>
#display something here
<% else %>
#display something else
<% end %>
in your view.

Resources