simple rails code optimisation - ruby-on-rails

<% if defined?(#club.name) && !(#club.name).nil? %>
<%= #club.name %>
<% else %>
clubs:
<% end %>
first line looks ugly. any help is appreciated.

Perhaps:
<% if #club && #club.name %>
<%= #club.name %>
<% else %>
clubs:
<% end %>
Since #club will presumably either be set to a Club object or be set to nil, we don't really need to explicitly check whether or not the #club has a name method. Instead, we can just test that #club is set at all, and a truthiness test will suffice for that.
Similarly, we don't need an explicit nil check on #club.name. All values other than false and nil are considered "truthy," and, since a club's name presumably can't be set to false, simply testing its truthiness is equivalent to explicitly checking if it is nil.
Violet pointed out in the comments that this is actually a fairly common idiom in the Rails world, so a shortcut actually exists. In a Rails environment, all objects, including nil, have a method named try. try invokes the method name passed to it as an argument, and either returns that method's return value, or nil if that method is not defined.
So, for example:
no_name = Club.new
no_name.try(:name) # => nil
so = Club.new :name => 'StackOverflow'
so.try(:name) # => "StackOverflow"
nil.try(:name) # => nil
As such, the following is the exact equivalent to the first code block:
<% if #club.try(:name) %>
<%= #club.name %>
<% else %>
clubs:
<% end %>
Much prettier :)

<%= #club.name || "clubs:" %>
how about that?

Related

Ruby on Rails: Store array value in variable and use as string

I'm in the process of refactoring some code. I'm trying to use arrays in my view as part of a for loop that makes columns in a table.
I have defined the arrays in my controller:
subjects_controller.rb
def index
...
#CRFS_TO_VIEW = [Baseline, TreatmentCompletion]
#CRF_PATH = {Baseline => 'baseline_path', TreatmentCompletion => tc_path}
end
So my goal; as the function iterates over #CRFS_TO_VIEW, the correct path is selected from #CRF_PATH and appended to the link_to function.
indext.html.erb
<% #CRFS_TO_VIEW.each do |crf| %>
<% path = #CRF_PATH[crf] %>
<%= link_to "edit", path(crf.where(subject_id: sub.subject_id).first %>
<% end %>
I also tried :
<%= link_to "edit", #CRF_PATH[crf](crf.where(subject_id: sub.subject_id).first %>
Which didn't work. I feel I must be getting close, any help or insight would be greatly appreciated.
Thanks.
A few things:
a. You should save yourself some time and loop through the dictionary instead of the array:
<% #CRF_PATH.each do |crf, path| %>
...
<% end %>
b. You are getting a string from the loop - you can invoke the equivalent method with send:
<%= send(path, ...) %>
c. You can simplify your retrieval of the objects using:
crf.find_by(subject_id: sub.subject_id)
That said - this seems like a pretty bad way of doing things. I'd recommend instead adding a view helper:
def crf_path(crf)
case crf
when Baseline then baseline_path(crf)
...
end
With something like this you could use (notice changed the find_by to find_by! for safety as well):
<% #CRFS_TO_VIEW.each do |crf| %>
<%= link_to "edit", crf_path(crf.find_by!(subject_id: sub.subject_id) %>
<% end %>
Finally instance variables should NOT be named upper case. If you want to use a constant define it as a constant (otherwise use lower case names).

Is there a way to get the console to ignore certain errors?

Say I have an instance variable #n, and I'm calling <%= #n.title %> in my view.
If #n equals a valid record, then this will print normally. But if #n is blank or invalid, then the entire page will show an error message, because of this one little line.
Is there a way to get #n.title to just print nil if #n is nil or invalid?
I'm looking for a way to do this without conditional statements. For example, if I wanted to print
<%= #v1.title %>,<%= #v2.title %>,<%= #v3.title %>,<%= #v4.title %>,
if I wanted to use conditionals to print without errors, it would require 12 lines of code:
<% if #v1 %>
<%= #v1.title %>,
<% end %>
<% if #v2 %>
<%= #v2.title %>,
<% end %>
<% elsif #v3 %>
<%= #v3.title %>,
<% end %>
<% elsif #v4 %>
<%= #v4.title %>,
<% end %>
It seems a shame to use 12 lines on this. It would be nice to be able to accomplish the error-handling right when printing.
You can totally do this easily with the try() method. I use it all the time.
<%= #n.try( :title ) %>
That will return nil if #n is nil or if the title method doesn't exist on #n.
You can also chain them together like this:
#n.try( :title ).try( :to_s )
Or even use it on a hash:
#n.try( :[], 'name' ) # Which is the same as #n['name']
See http://api.rubyonrails.org/classes/Object.html#method-i-try
EDIT (Jan 11, 2016)
You can now use the "safe navigation operator" as of Ruby 2.3.0.
#n&.title&.to_s
As well as the Array#dig and Hash#dig methods introduced in Ruby 2.3.0.
hash = { 'name' => 'bob' }
hash.dig( 'name' ) # Which is the safe way to do hash['name']
You can add some logic to your view that differentiates between development (where some errors can be ignored) and production environments (where errors should cause your app to fail in an obvious and ugly manner). Ruby's nil has a "falsey" nature, so you can use that concept to your benefit as well.
<% if Rails.env.development? %>
<% if #n %>
<%= #n.title %>
<% else %>
<%= nil %>
<% end %>
<% else %>
<%= #n.title %>
<% end %>

Test ownership in a helper

I have this block in my views:
<% video.members.each do |p| %>
<% if p.id == current_user.id %>
<%= "paid" %>
<% end %>
<% end %>
Basically I'm trying to work out if a member has paid for a video based on whether the id's match.
Maybe this a really bad way of doing it, which case I'd be happy to try and different method.
Assuming it is an ok way of checking this, how could I write a similar statement but as a helper method? I've tried, but it seems you can't write the same logic in helpers as the block just spits out the full array and not the id, meaning it doesn't work.
You should do this instead:
<% if video.members.exists?(id: current_user.id) %>
<%= 'Paid' %>
<% end %>
This will generate a single query to test if the video has been paid by the current_user ;-)
In a helper:
# application_helper.rb
def display_paid_or_not(video)
return '' if video.blank? # similar to .nil?
video.members.exists?(id: current_user.id) ? 'Paid' : ''
end
# in view
<%= display_paid_or_not(video) %>
Hope this helps!

show a text instead of NULL value

3.5 and want to show a text instead of NULL value in my category_id field
<% if e.categories_id == "NULL" %>
<%= e.categories_id ="Don't have value" %>
<% else %>
<%= e.categories_id ="Has value" %>
<% end %>
Someone can help me please?
I have tried
<% if e.categories_id = NULL %>
<% if e.categories_id == NULL %>
<% if e.categories_id = "NULL" %>
<% if e.categories_id == "NULL" %>
<%= e.categories_id.nil? ? "Don't have value" : "Has value" %>
or even better:
<%= e.categories_id ? "Has value" : "Don't have value" %>
<%= e.categories_id || "There's no categories_id" %>
But seriously, there's so much that's bad about this question. Firstly, you should know enough Ruby to know that it uses nil not NULL. A foreign key called categories_id suggest you have a model called Categories. Model names should be singular.
<% if e.categories_id.blank? %>
<p>"Don't have value" </p> (or whatever you want displayed)
<% else %>
<%= e.categories_id %> (should display the value of e.categories_id)
<% end %>
I prefer the .blank? method that Rails uses to check for both .empty? and .nil? Plus using an if else is more readable than the Terinary operator ? :
You can do this if you only want to display "Don't have value" if that is the case:
<%= e.categories_id || "Don't have value" %>
What you could do was to set the value:
<% e.categories_id ||= "Don't have value" %>
<%= e.categories_id %>
If you want to change the value of the data, you should not do that in the view. You should do all the data-manipulation in the controllers or even better, the model (fat model, skinny controller).
There is one other issue you are having: In Ruby null is called nil. So a test for nil would look like this:
if whatever == nil
#Do anything
end
What is nice about Ruby is that nil is considered as false in a statement, so you can omit the == nil so you get:
if whatever
#Do anything
end

Rails 'params' variable

In reference to this
I've created a question in a webform like this:
<div class="form_row">
<label for="features[]">Features:</label>
<% [ 'scenarios', 'role_profiles', 'private_messages', 'polls' ].each do |feature| %>
<br><%= check_box_tag 'features[]', feature,
(params[:features] || {}).include?(feature) %>
<% end %>
</div>
So if scenarios and private_messages gets checked and I print out params[:features] I will get:
scenariosprivate_messages
I was wondering how would I be able to obtain scenarios and private_messages separately from params. Is the mapping params[:features] = "scenariosprivate_messages" or is it really params[features] = ["scenarios", "private_messages"] ? If it's the latter how can I loop through them?
I write in my view:
<%= params[:features].each {|param|
param.capitalize
} %>
and I still just get scenariosprivate_messages printed.
Try this instead:
<% params[:features].each do |param| %>
<%= param.capitalize %>
<% end %>
The problem with your original solution is that you're printing out the result of the block, which is the array itself, rather than printing out each element of the array.
You shouldn't be using params in your views. You're best off assigning params[:features] to an instance variable in your controller and then iterating over that in your view.
But to answer your question, you're putting the equals sign for output in the wrong place. You want to output each element of the array individually instead of outputting the result of the loop.
You must use humanize:
<% params[:features].each do |param| %>
<%= param.humanize %>
<% end %>
According to this blog post you should be able to access them individually as params[:features]['scenarios'] etc. Looping should just work like with all other arrays -- eg
params[:features].each { |param|
# do something with param
}

Resources