Rails Case or Helper - ruby-on-rails

I have a number of codes which all have different meanings e.g. "cancelled", "confirmed" etc and I store the code in the database. I want to make use of the word forms in a number of different places around my Rails app and wanted to know what people recommend for this to keep things as efficient as possible.
I have considered a case statement in my views but it will be repeated and I want to avoid logic in my views too. So I thought maybe a helper but should it be a case statement within the helper?
This is what I have so far in my relevant helper file:
def status_word(status)
case status
when 1
puts "Cancelled"
when 2
puts "Confirmed"
end
end
and I call it in my view as follows:
<%= status_word(1) %>
but nothing is outputted in my view. Can/should I use "put" here or is there a better way?

Your using puts incorrectly. It's for writing to standard output, not for returning a value from a method. You want return, or just let the value fall off the end of the method:
def status_word(status)
case status
when 1 then "Cancelled"
when 2 then "Confirmed"
end
end
With puts before each string, you're actually returning the return value of puts, which is nil:
irb(main):001:0> puts "what"
what
=> nil

Related

Nested each do loops printing unexpectedly in Ruby

I have two arrays containing strings. I'm trying to iterate through both arrays with nested .each do loops to see if any elements in the first array have a substring of any of the elements in the second array. I'm using .include? within the nested loops to check this. I want the result to be the string printed the number of times it matches an element in partials.
This is the method that isn't working
def orphanCheck(partials, partials1, duplicatesArray)
partials1.each do |i|
partials.each do |j|
if i.include?(j)
duplicatesArray.push(i)
end
end
end
end
I'm using this as a helper method to define partials and partials1
def manipulate(monthEmails, todayEmails, partials, partials1)
monthEmails.each do |i|
email = EmailAddress.new(i.to_s)
partials.push(email.host_name.to_s)
end
todayEmails.each do |j|
todaySignup = j.to_s.slice(11, 100)
partials1.push(todaySignup)
end
end
And then I'm calling the two with the following
manipulate(allUnique, todayEmails, partials, partials1)
orphanCheck(partials, partials1, duplicatesArray)
#puts duplicatesArray
duplicatesArray is printing some strings that shouldn't be matches and it's printing some strings more times than I want. For example, gmail.com isn't in partials at all but me#gmail.com, which is in partials1 once, is being pushed to duplicatesArray three times. If yahoo.com is in partials three times, then I would want me#yahoo.com (from partials1) to be pushed to duplicatesArray three times, for example.
To be sure you could be doing:
partials1.each do |i|
i_ups=i.split('#')[-1]
partials.each do |j|
if i_ups===j
duplicatesArray.push(i)
break
end
end
end
If I understood correctly (partials is only the host part of the email provider and partials1 is the full email address)
A better solution that should give you a correct duplicatesArray would be:
partials1.each do |email_address|
email_host = email_address.split("#").last
duplicatesArray.push(email_address) if partials.include?(email_host)
end

What is the most elegant Ruby expression for comparing and selecting values from a 2D Array?

I have some code that is chugging through a set of Rails Active Record models, and setting an attribute based on a related value from a 2D Array.
I am essentially setting a US State abbreviation code in a table of US States which was previously only storing the full names. A library of state names is being used to derive the abbreviations, and it contains a 2D Array with each sub-array having a full name, and an abbreviation (i.e., [['New York', 'NY']['Pennsylvania', 'PA'][etc]]). I compare the state name from each record in the database to each full text name in this Array, then grab the corresponding sibling Array cell when there is a match.
This code works fine, and produces the correct results, but its frumpy looking and not easily understood without reading many lines:
# For the following code, StatesWithNames is an Active Record model, which is
# having a new column :code added to its table.
# Sates::USA represents a 2D Array as: [['StateName', 'NY']], and is used to
# populate the codes for StatesWithNames.
# A comparison is made between StatesWithNames.name and the text name found in
# States::USA, and if there is a match, the abbreviation from States::USA is
# used
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
States::USA.each do |s|
if s[0] == named_state.name
named_state.update_column(:code, s[1])
break
end
end
end
end
end
What is the most Ruby style way of expressing assignments with logic like this? I experimented with a few different procs / blocks, but arrived at even cludgier expressions, or incorrect results. Is there a more simple way to express this in fewer lines and/or if-end conditionals?
Yea, there is a few ifs and checks, that are not needed.
Since it is Rails even though it does not state so in question's tags, you might want to use find_each, which is one of the most efficient way to iterate over a AR collection:
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update_column(:code, s[1]) if s[0] == named_state.name
end
end
Also be aware, that update_column bypasses any validations, and if you wish to keep your objects valid, stick to update!.
And last thing - wrap it all in transaction, so if anything goes wrong all the way - it would rollback any changes.
StatesWithNames.transaction do
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update!(:code, s[1]) if s[0] == named_state.name
end
end
end
You might use a different data structure for this.
With your existing 2D array, you can call to_h on it to get a Hash where
a = [['California', 'CA'], ['Oregon', 'OR']].to_h
=> { 'California' => 'CA', 'Oregon' => 'OR' }
Then in your code you can do
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
abbreviation = state_hash[named_state.name]
if !abbreviation.nil?
named_state.update_column(:code, abbreviation)
end
end
end
end
the first thing you want to do is convert the lookup from an array of arrays to a hash.
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.select{|state| state.code.blank?}.each do |named_state|
named_state.update_column(:code, state_hash[named_state.name]) if state_hash[named_state.name]
end
end

Fill array with nils for when nothing returned from query in ruby (RoR)

I have a model called foo with a date field.
On my index view, I am showing a typical "weekly view" for a specified week. To put the data in my view, I loop through each day of the specified week and query the data one day at time. I do this so that I can make sure to put a NIL on the correct day.
foos_controller.rb
for day in 0..6
foo = Foo.this_date(#date+day.days).first
#foos[day] = foo
end
index.html.haml
- for day in 0..6
%li
- if #foos[day].nil?
Create a new foo?
- else
Display a foo information here
Obviously, there's a lot of things wrong here.
I should find someone smart member to tell me how to write a good query so that I only have to do it once.
I should not have any if/else in my view
My goal here is to either show the content if the it is there for a particular day or show a "create new" link if not.
thanks for the help in advance!!
First, I have no idea what this_date actually does, but I'll assume it's retrieving a record with a specific date from your datastore. Instead of doing 7 queries, you can condense this into one using a date range:
Foo.where(date: (#date..(#date + 6.days)))
You can tack on a .group_by(&:date) to return something similar to the hash you are manually constructing, but using the actual dates as keys instead of the date offset.
To iterate over the dates in the view, I would recommend using Hash#fetch, which allows you to define a default return when a key is not present, e.g:
hash = { :a => 1, :b => 2 }
hash.fetch(:a){ Object.new } #=> 1
hash.fetch(:c){ Object.new } # #<Object:...>
The question now is what object to substitute for nil. If you want to avoid using conditionals here, I'd recommend going with the NullObject pattern (you could involve presenters as well but that might be a bit overkill for your situation). The idea here is that you would create a new class to substitute for a missing foo, and then simply define a method called to_partial_path on it that will tell Rails how to render it:
class NullFoo
def to_partial_path
"null_foos/null_foo"
end
end
You'll need to create partials at both app/views/foos/_foo.html.erb and app/views/null_foos/_null_foo.html.erb that define what to render in each case. Then, in your view, you can simply iterate thusly:
<% (#date..(#date + 6.days)).each do |date| %>
<%= render #foos.fetch(date){ NullDate.new } %>
<% end %>
Is this appropriate for your situation? Maybe it's also a bit overkill, but in general, I think it's a good idea to get in the habit of avoid nil checks whenever possible. Another benefit of the NullObject is that you can hang all sorts of behavior on it that handle these situations all throughout your app.

Rails3 - Multiple queries or single query in controller action

Hopefully a simple question around a rails best-practice.
Let's keep this super simple; say I have a task model that has an ID, description and status.
In my controller I have an index action to return all tasks
def index
#tasks = Task.all
end
My question is, in my view, suppose I want to display the tasks in separate HTML tables according to their status.
What is the best practice?
a) Query the database multiple times in the index action, ie
def index
#draft_tasks = Task.where(status: "Draft")
#approved_tasks = Task.where(status: "Approved")
#closed_tasks = Task.where(status: "Closed")
end
b) Query the database once, and filter in the contoller action
def index
tasks = Task.all
#draft_tasks = tasks.#somethinghere
#approved_tasks = tasks.#somethinghere
#closed_tasks = tasks.#somethinghere
end
c) Filter in the view
<% #tasks.each do |k, v| %>
<% some if statement searching for the status I want %>
# Some code to output the table
<%end%>
<%end%>
or
d) Something else?
The generally accepted best practices here are to keep controller methods thin and to keep logic out of the view. So with that in mind, one possible way to do this would be:
# model
class Task
scope :drafts, where(:status => "Draft")
scope :approved, where(:status => "Approved")
scope :closed, where(:status => "Closed")
end
# controller
def index
#draft_tasks = Task.drafts
#approved_tasks = Task.approved
#closed_tasks = Task.closed
end
This will make 3 queries to the database, which could become a performance concern down the road, but if that does happen, you can optimize it at the model level (e.g. by defining class methods drafts, approved, and closed where the first one called prefetches everything). It's less elegant though, so don't prematurely optimize.
This is a loaded question with no one best practice in my opinion. Given the case you have stated (display a table for each status) I would use the following thought process:
I would generally avoid case A when you're just dealing with one Model type. I try to limit the number of database queries when possible
Case B is what I would probably use if the view needs to display different markup depending on the status of a task.
I would usually tend towards case C if the markup is the same for each status. You can use the group_by function for this:
When the amount of information on your page starts to get larger and more complicated, you can start looking at extracting some logic out of the controller and into another object (common terms for this object would be a presenter or decorator). This can make testing some of your presentation logic easier by separating it from the controller and keeping your controllers 'thin'. But for the case you've given, I'd stick with option b or c.
In the simple case where the number of tasks is limited, I would do only a single query to retrieve them, and then separate them as follows:
tasks = Task.all
#draft_tasks = tasks.select { |x| x.status == 'Draft' }
#approved_tasks = tasks.select { |x| x.status == 'Approved' }
#closed_tasks = tasks.select { |x| x.status == 'Closed' }
Furthermore, depending on the bendability of your requirements, I would even render them in a single table with a clear visual marker what the state is (e.g. background-colour or icons). Then there would not even be a reason to separate the tasks beforehand (but I can imagine this would break your UI completely).
None of the above is valid once the number of tasks becomes larger, and you will need to apply pagination, and you need to display three different tables (one for each state).
In that case you will need to use the three separate queries as answered by #Ben.
Now UI-wise, I am not sure how you can paginate over three different sets of data at once. So instead I would use a single table showing all the states, and offer the option to filter on the status. In that case at least it is clear for the user what pagination will mean.
Just my two cents, hope this helps.
option a) seems better just because database can cache the query for you and stuff, so it should be faster.

Ruby syntax: break out from 'each.. do..' block

I am developing a Ruby on Rails app. My question is more about Ruby syntax.
I have a model class with a class method self.check:
class Cars < ActiveRecord::Base
...
def self.check(name)
self.all.each do |car|
#if result is true, break out from the each block, and return the car how to...
result = SOME_CONDITION_MEET?(car) #not related with database
end
puts "outside the each block."
end
end
I would like to stop/break out from the each block once the result is true (that's break the each block if car.name is the same as the name parameter once) AND return the car which cause the true result. How to break out in Ruby code?
You can break with the break keyword. For example
[1,2,3].each do |i|
puts i
break
end
will output 1. Or if you want to directly return the value, use return.
Since you updated the question, here the code:
class Car < ActiveRecord::Base
# …
def self.check(name)
self.all.each do |car|
return car if some_condition_met?(car)
end
puts "outside the each block."
end
end
Though you can also use Array#detect or Array#any? for that purpose.
I provide a bad sample code. I am not directly find or check something
from database. I just need a way to break out from the "each" block if
some condition meets once and return that 'car' which cause the true
result.
Then what you need is:
def check(cars, car_name)
cars.detect { |car| car.name == car_name }
end
If you wanted just to know if there was any car with that name then you'd use Enumerable#any?. As a rule of thumb, use Enumerable#each only to do side effects, not perform logic.
you can use include? method.
def self.check(name)
cars.include? name
end
include? returns true if name is present in the cars array else it returns false.
You can use break but what your are trying to do could be done much easier, like this:
def self.check(name)
return false if self.find_by_name(name).nil?
return true
end
This uses the database. You are trying to use Ruby at a place the database can deal with it better.
You can also use break conditional:
break if (car.name == name)
I had to do this exact same thing and I was drawing a blank. So despite this being a very old question, here's my answer:
Note: This answer assumes you don't want to return the item as it exists within the array, but instead do some processing on the item and return the result of that instead. That's how I originally read the question, I realise now that was incorrect - though this approach can be easily modified for that effect (break item insead of break output)
Since returning from blocks is dodgy (nobody likes it, and I think the rules are about to change which makes it even more fraught) this is a much nicer option:
collection.inject(nil) do |_acc, item|
output = expensive_operation(item)
break output if output
end
Note that there are lots of variants; for example, if you don't want an incidental variable, and don't mind starting a second loop in some circumstances, you can invert it like this:
collection.inject(nil) do |acc, item|
break acc if acc
expensive_operation(item)
end

Resources