Proper usage of `rescue` (or `try`) - ruby-on-rails

When I have a model attribute that may or may not exist and I need to chain some methods after it, I add present?. For example:
Car.last.passengers.present? and
Car.last.passengers.each do { |passenger| puts passenger.name }
Would it be better to instead use rescue in such cases? For example
Car.last.passengers.each { |passenger| puts passenger.name } rescue "No passengers in the car!"
EDIT:
Thanks for all the responses. I should have asked a more general question, "What is the best way to handle a potentially nil result in the middle of a chain of methods?".
Let me make my example more general for clarity. If I call:
Car.last.driver.pocket_contents
but the last instance of Car has no driver, I'd be calling pocket_contents on nil. As per one of the commenters below, should I be using try, and if so, could you show me how it would be used succinctly in this case?

There is absolutely no reason to use rescue here. Using exception handling mechanisms for flow-control is widely viewed as an abuse of exceptions and generally considered a bad practice.
There is also probably no reason to use x.present? && x.each, as (if this is an ActiveRecord association) it will never return a falsy "non-present" value. It will always return an array-like object representing 0 or more items, which you can safely invoke each on. If this is not a ActiveRecord association, you should change your code to follow this convention and return an empty array instead of nil.
In the general case (assuming ActiveSupport::CoreExtensions is available), you can use try. Assuming passengers is a method that may return nil, your .present? checks should be try invocations:
Car.last.passengers.try(:each) { ... }
This can be chained to artibtrary lengths; both of these are equivalent:
a && a.b && a.b.c
a.try(:b).try(:c)
Note that this will not work if your method isn't returning nil, but instead returning a "blank" value.
If try is not available, your currently solution is a widely used practice, except that you should be using && instead of and - These are not equivalent operators in Ruby.
Car.last.passengers && Car.last.passengers.each { ... }
If you want to save characters, you can use || instead of && to supply a default value before you resort to the dirty rescue trick you're currently considering:
(Car.last.passengers || []).each { ... }
If this is an ActiveRecord association, there are several idiomatic Rails solutions, the best of which is to move your "puts" (which I'm assuming in reality is rendering a series of HTML elements) into its own partial, called _passenger.html.erb. Then, you can render the entire collection:
= render Car.last.passengers
If you want to handle rendering an empty set differently, you should depend on render's behavior of returning false when an empty collection is rendered:
= render Car.last.passengers || render 'no_cars'
This way, your markup for displaying a "There are no passengers" message to the user is stored in a _no_cars.html.erb partial and rendered cleanly with a single line.

You don't want to rescue an exception that should occur. If it is expected behavior that the attribute may not exist or the object may be nil then you may want to use either the try or try! methods.
This will try to call the method/attribute if your object is not nil. The try method will quietly evaluate to nil if somewhere along the chain it can not complete. The try! method will raise a NoMethodError if you try to call a method that doesn't exist on a non nil object.
http://apidock.com/rails/v4.2.1/Object/try%21
http://apidock.com/rails/Object/try

I can't tell where the code in your question lives, but I'll assume you are trying to list passenger names in a view. If you make passengers a collection in the controller:
#passengers = #car.passengers.all
Your _passenger.html.erb partial might read:
<%= passenger.name %>
You can render the collection in your view pass in a message if the collection is empty:
<%= render(#passengers) || "No passengers in the car!" %>
More about this here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Related

How to check if a variable has a map available in rails? In other words, if a variable is mappable?

I am trying to deal with a legacy method which accepts a variable and returns the variable.amount.to_s. But now with some changes, the variable can also be an active record relation.
I basically want to be able to do something like this:
def method(variable)
if variable has map?
variable.map { |v| v.amount.to_f }.reduce(:+)
variable.to_s
else
variable.amount.to_s
end
end
but I am unable to figure out what that if condition should be ?
You can test for the method directly with respond_to?.
if variable.respond_to?(:map)
Or you can check that it is Enumerable which provides a large suite of methods used for iteration, including map.
if variable.is_a?(Enumerable)
The advantage of Enumerable is it tells you more about the object, and you can be more sure that its map is the map you expect and not just some method that happens to be named map. The disadvantage is it will miss anything which is not Enumerable but does implement an applicable map; I can't think of a case where that should happen.

Streamline Ruby: update based on object attribute only if object is not nil

if SOMETHING
charge = Object (this object has a method ID)
end
DiffObject.update_attributes(specific_attribute: charge.id)
But obviously, if the SOMETHING did not evaluate to true then I don't need to update anything, or in my case, I think it might be easier to just run an update_attributes with specific_attribute:nil
So what I have right now is this:
DiffObject.update_attributes(specific_attribute: ((defined? charge) == nil ? nil : charge.id)))
But something tells me there's a MUCH better way of doing it
ActiveSupport's core extension provides a handy method try on almost every object.
try(*a, &b)
Invokes the public method whose name goes as first argument just like public_send does, except that if the receiver does not respond to it the call returns nil rather than raising an exception.
For example, you can use change.try(:id). If change is nil or something that doesn't respond to id, it returns nil, otherwise it returns the ID.
See RailsGuide and Rails API doc for details.
When you don't know whether charge is nil, you can use try to safely access the id. It will return the id if charge is present, and just return nil if the charge is nil, without an error.
DiffObject.update_attributes(specific_attribute: charge.try(:id))
The methods update_attributes, update, save etc will only save the record if it has actually changed, so you don't need to check that yourself. Just save it and let ActiveRecord figure out the rest.

Rails difference in object created from a .find(:id) and .where() methods

What is the difference in the objects created with these 2 methods:
tec = Technique.find(6)
tec2 = Technique.where(:korean => 'Jok Sul')
The data returned for each is exactly the same, yet the first object will respond perfectly to an inherited method like update_attributes while the second object will give an error of method not found.
When I do tec.class and tec2.class one is an ActiveRecord::Relation and the other doesn't give me a class at all, it just prints out the content of the object.
Maybe when you use the .where method you get an array, even if there is only one match and therefore you always have to issue the .each method to get at the contents? But that makes it hard to deal with when you want to update records, etc.
Can someone clarify this for me? Specifically, how to deal with matches found through the .where method.
Thanks.
Try:
tec2 = Technique.where(:korean => 'Jok Sul').first
Good question.
tec_scope = Technique.where(:korean => 'Jok Sul') # create an internal query
Remember, here only the query is created, it is not executed. You can programmatically build on top of this query if you so wished. The scope (or query if you so wish) will be executed in 2 ways. "Implicit" or "Explicit". Implicit way of running the query happens for example in the console, which invokes a method on the scope which automatically runs the query for you. This wont happen in your controllers unless you run it explicitly for .e.g
tec_scope.all # returns array
tec_scope.first # retuns one element
Scopes are just adding where clauses/predicates to your query. It's query building and delaying the execution till it is needed.
However,
tec_objects = Technique.find(6) # explicitly runs a query and returns one object (in this case)
This will explicitly run the query there and then. It is a question of the timing of execution of the query.
The difference is subtle but very important.
This hasnt got anything to do with whether you get one result or an array.
Technique.find([4,5]) # will return an array
Technique.find(4) # will return one object
Technique.where(:some_key => "some value").all # will return an array
Technique.where(:id => 5).first # will return one object
The difference is in timing of the execution of the query. Don't let the console fool you into believing there is no difference. Console is implicitly firing the query for you :)
The find(6) returns a single object, because you're specifying the object ID in the database, which is guaranteed to be unique by convention.
The where call returns a collection, which may be only 1 item long, but it still returns a collection, not a single object.
You can reveal this difference. Using your example code, if you call tec.class vs. tec2.class I think you'll find that they aren't the same class of object, as you expect.
That is, the methods available to a collection of objects is different than the methods available on an instance of that object.

Trouble on finding a class object in a array of classes

I am using Ruby on Rails 3.0.7 and I would like to understand how to handle the following code in order to retrieve a class objects with a specified id.
In my view file I have:
#records = Users.all # This returns an array (class)
In another file, a partial template, I would like to retrieve, for example, the user with id 1, but if I make this:
#records.find(1)
I get an enumerator (class) of all records:
<Enumerator: [<Users id: 1, ... ] >
How can I find the user with id 1 (or other ids) "a là Ruby on Rails Way"?
UPDATE
I use #records = Users.all in a view file because I aim to minimize calls to the database since I need to iterate almost over all records and check them existence. If I do for example:
some_hash.each { |key, value|
put User.find(value)
}
and I go in the log file, I will see a lot of database requests.
Even though this is probably quite slow, and I suspect there are some less than optimal designs in the app you're working on (not judging, we've all been there), Array#index seems to be what you're looking for:
#records[#records.index{|user| user.id == 1}]
Edit
Although if you need to do something for every user, and you need to access them by id quickly, I'd probably do something like this in your controller. Even if it's not really faster, it's much more readable (to me anyways):
#users_hash = {}
User.all.each{|user| #users_hash[user.id] = user}
Then in your views you can do:
#users_hash[id].username
Use User.scoped instead of User.all. #all will immediately query the database and return an array, whereas #scoped will return an ActiveRecord::Relation object which you can chain further queries. In this case, the database won't be hit until you try and somehow inspect or enumerate the result
Actually you're mistaken. #records.find(1) is returning an object of the class Enumerator (which is not the same as the class Enumerator itself).
The problem here is that, as you've noted, #records is an Array, not an ActiveRecord object, and Array#find (inherited from Enumerable#find--which, when not given a block, returns an object of class Enumerable) is not the same method as ActiveRecord::Base#find (i.e. User#find).
What you should do is, in your controller, pick out the one user record you want:
#user = User.find 1
...and then use #user directly in your template. Generally you should avoid doing ActiveRecord lookups (e.g. find) in your templates. That kind of logic should happen in your controller.
Last time for such case I ended up doing like this:
#assignments = Assignment.find_by_sql(' ... ')
#assignments.find(id: 1).first

Could I improve this method with duck typing?

Hopefully I haven't misunderstood the meaning of "duck typing", but from what I've read, it means that I should write code based on how an object responds to methods rather than what type/class it is.
Here's the code:
def convert_hash(hash)
if hash.keys.all? { |k| k.is_a?(Integer) }
return hash
elsif hash.keys.all? { |k| k.is_a?(Property) }
new_hash = {}
hash.each_pair {|k,v| new_hash[k.id] = v}
return new_hash
else
raise "Custom attribute keys should be ID's or Property objects"
end
end
What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object. I don't particularly enjoy having to iterate through the hash keys twice with all? to determine if I need to grab the ID's out.
Of course, I'll accept any other suggestions to improve this code as well :)
How you write this method should depend on whether you expect an exception to be thrown during the course of normal program execution. If you want a readable exception message because an end-user might see it, then throwing one manually makes sense. Otherwise, I'd just do something like this:
def convert(hash)
new_hash = {}
hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v }
return new_hash
end
This will accomplish exactly the same thing, and you'll still get an exception if an array key doesn't have an id field. Even better, this uses a little more duck typing because now anything that has an id field will be acceptable, which is better than explicitly checking for something being a Property. This makes your code more flexible, especially when unit testing.
We still have an explicit check for integer objects, but this kind of occasional special case is usually acceptable, especially when checking for built-in data types.
Duck typing is really just a nuanced version of polymorphism. In a statically typed language like Java you'd have to create an explicit interface that told the compiler all of the methods that a particular variable can accept. With a dynamic language like Ruby the interfaces still exist in an abstract sense, they're just implicit.
The problem is the fact that you're accepting two different data structures into one method. The way to make duck typing work is to require that all the objects that get passed to your method obey the same contract (i.e. it's always a hash of Integers to [Foo] objects.) The process of converting a hash with Property keys into the correct structure should be the job of the client code. That can be done very easily with a simple wrapper class or a conversion function consisting of just the body of your elseif clause.
Bottom line it's up to the guy calling the method to make sure his parameters all quack the way your method expects them to quack. If they don't, he's the one who need's to figure out how to make his turkey quack like a duck, not you.
What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object.
You should probably check for that when you're creating/inserting into the hash. You could try something like this:
h = {}
def h.put obj
self[obj.id]=obj
end
or maybe
h = {}
def h.[]= key, value
raise "hell" unless key == value.id
super
end

Resources