When using a date, what can I do with a nil value? - ruby-on-rails

So I've got an object in my database with a date field, except sometimes it will be nil. Is there a way in the view I can show this as a string value. Something like TBA maybe?

<%= #event.date || "TBA" %> should do it.
In response to your comments, yes, you could do this in the model but it's a bad idea. Why?
First of all, it is about presentation of the data so for that reason it belongs in the view.
Secondly, it could break things. If you did it in the model, #event.date would sometimes return a date and sometimes a string. What would happen if you called #event.date.hour and date was "TBA"? You'd get an error. The only fix would be to check for it everywhere, which would be horrible.
If you really are going to be doing it a lot you could create a helper method in application_helper.rb that could look something like this:
def date_or_tba(date)
date || "TBA"
end
So you could then write in your view:
<%= date_or_tba #event.date %>
Which isn't much less typing but would have the not inconsiderable advantage of restricting the use of the string "TBA" to only one place - which means if you ever need to change it (for I18n purposes for example) - it's really easy.

Related

Is there a way to store a variable while in a view in Rails?

As stated above. Is there a way to store a variable in a view in rails for later use without setting it in the controller function for that view?
ex.
<% bagel = #bagels.active_bagel %>
...
<%= bagel.delicious? %>
This can be achieve by this
#app/views/xyz/
#1. make a hidden html input field
<input type ="hidden" id = "you_id" value = "<%= yourvariable %>"
#if u want to access that variable in every view write your code in app/helper/application_helper
#otherwise write in the specific helper something this kind of code
def yourvariable
#your code
return somthing which you want in view
end
and from that html tag access the value
If you are inside of the same view template (instead of being in another template) then sure - go ahead and set variables just as in your example. We do this all the time.
...but if you go to another template to use this variable... then it should be set in the calling scope, not be a magic variable that is used in multiple templates, if you see what I mean :)
it's possible to do it, but it's not considered good practice. If you can do it in the controller, then you probably should.
In your case... I don't think there should be a problem with you calling active_bagel more than once on the #bagels variable.
If you are trying to do this because you think it might plausibly be slow someday.... I'd recommend waiting until it is actually slow... then figuring out how to optimise for speed then. If it's for some other purpose... then tell us what that is and we'll figure out a better way.
You can use data-attributes.
tag("div", class: :bagel, :data => {bagel_id: 3})
Then use jQuery.data() to retrieve the value.

Better ways to return HTML from a method in rails?

this is my first post here so be nice ;-)
I am currently learning the ropes of RoR (and general programming concepts) and I'm not sure if how I am returning some data from a method is 'the right way' (or maybe the 'rails way') to do so.
I have a form that a user can enter a value into and my 'app' will poll the requested data from an external web service.
In my view, I have a table to contain said data and in one cell I've included a call to the following method:
extract of view:
<tr>
<td>Subscriber</td>
<%= is_subscribed?(#search_result.expiry) %>
</tr>
So, I'm calling this little is_subscribed? method (which I've stored in a helper) as per below:
def is_subscribed?(sub_date)
if sub_date >= Date.today
return '<td class="text-success">Yes</td>'.html_safe
else
return '<td class="bg-danger">No</td>'.html_safe
end
end
Depending on the result of the comparison, I return some HTML with one class and a value or some HTML with another class.
The above does work and the class is applied correctly in the resulting HTML. What I would like to know is whether there a simpler way to do this, is this way bad practise? I am also curious how would someone else handle this sort of task.
Thanks!
It's fine how you've done it. Here's a variant:
def subscribed_td(sub_date)
sub_date >= Date.today ?
content_tag(:td, 'Yes', class: 'text-success') :
content_tag(:td, 'No', class: 'bg-danger')
end
The main difference is simply the function name is more accurate imo, as I'd expect a function called is_subscribed? to return a boolean. It's also using content_tag to be a bit more concise, and a ternary instead of the if-then (which is not to everyone's taste). You could try to be fancy here with just a single content_tag expression and then use ternaries inside it to vary the arguments, but that's OTT DRY-ness imo.

Avoiding nil in Rails views

I'm sure this has been asked already, but I can't find the answer.
I have a Project model, which has a belongs_to relationship with my Client model. A client has a name, but a project doesn't necessarily have a client.
In my view, I've got code like this:
<%=h project.client && project.client.name %>
because if the project doesn't have a client then trying to access project.client.name causes a NoMethodError (nil doesn't have a method called name).
The question is, is it acceptable to have this kind of nil checking in the view, or should I be looking for another way around it?
Just use
project.client.try(:name)
I think its perfectly acceptable - this is view logic, you are more or less deciding whether or not to show portions of your view, based on whether there is data.
I run into this all the time, and yes it's annoying. Even when there is supposed to never be a nil, dirty data that I inherited sometimes triggers it.
Your solution is one way of handling it. You could also add a method to Project called client_name that displays the client name if it exists, but then you are linking the models together more than some people recommend.
def client_name
client && client.name
end
You could also make a helper method to do it, but you can end up writing a lot of them. :)
As mentioned by Skilldrick below, this is also useful to add a default string:
def client_name
client ? client.name : "no client"
end
You can use delegate in your Project class, so this way you will respect the Law of demeter which says that you should "talk only to your immediate friends".
project.rb
class Project
delegate :name, to: :client, prefix: true, allow_nil: true
end
So this way the project object will know where to ask about the client's name:
#You can now call
project.client_name
See more about delegate in the Rails documentation.
my hacky solution is to yield a block and rescue the error. Many would say using rescue as logic is very bad form. Just don't use this where you would actually need to know when something is nil and shouldn't be.
In application_helper.rb:
def none_on_fail
begin
return yield
rescue
return "(none entered)"
end
end
Then in the view:
<%= none_on_fail { project.client.name } %>
Then methods can be chained as deep as needed and it can be used on any method BUT it will cover up other potential problems with models/relationships/methods if they exist. I would equate it to taking out a splinter with a flamethrower. Very effective with painful consequences if used improperly.
I think these checks can usually be eliminated with a bit of thought. This has the benefit of keeping your view code cleaner, and more importantly, keeping logic out of the view layer, which is a best practice. Some templating engines don't allow any logic in the view.
There are at least a couple of scenarios. Let's say you have a show action that depends on an instance variable. I'd say if the record is not found the controller should not render the html, by redirecting or something else. If you have a loop in the view for an array, use #array.each do |a| end so that it doesn't evaluate if the array is empty. If you truly want an application default in the view, try loading it from a config file, e.g. #page_title || #{#APP_CONFIG['page_title']} (see Railscasts #85). Remember you may want to change these strings later, for example translating the UI.
Those are a couple scenarios where presence checks and usage of try can be avoided. I'd try to avoid them if possible. If you can't avoid them, I'd put the conditional checks in a view helper and add a helper unit test for it to verify (and document) both code paths.

Rails: Flatten array in parameter

I'm trying to flatten an array for my form.
def update
#tour = Tour.find(params[:id])
params[:tour][:hotel_ids][0] = params[:tour][:hotel_ids][0].split(',')
...
This results in:
"hotel_ids"=>[["1","2"]]
Naturally I want it to be
"hotel_ids"=>["1","2"]
My Form:
<%= text_field_tag 'tour[hotel_ids][]', nil %>
Hope anyone can help with this.
EDIT
I've gotten it to work, somehow. This might be a bad way to do it though:
I changed the text_field that get's the array from jquery to:
<%= text_field_tag 'tour[h_ids][]', nil %>
then in my controller I did:
params[:tour][:hotel_ids] = params[:tour][:h_ids][0].split(',')
And this works, I had to add h_ids to attr_accessor though. And it will probably be a big WTF for anyone reading the coder later... but is this acceptable?
This is ruby!
params[:tour][:hotel_ids][0].flatten!
should do the trick!
ps: the '!' is important here, as it causes the 'flatten' to be saved to the calling object.
pps: for those ruby-related questions I strongly suggest experimenting with the irb or script/console. You can take your object and ask for
object.inspect
object.methods
object.class
This is really useful when debugging and discovering what ruby can do for you.
Simply use <%= text_field_tag 'tour[hotel_ids]', nil %> here, and then split like you do in example.
What really happens in your example is that Rails get param(-s) tour[hotel_ids][] in request and it thinks: "ok, so params[:tour][:hotel_ids] is an array, so I'll just push every value with this name as next values to this array", and you get exactly this behavior, you have one element in params[:tour][:hotel_ids] array, which is your value ("1,2"). If you don't need (or don't want) to assign multiple values to same param then don't create array (don't add [] at the end of the name)
Edit:
You can also go easy way (if you only want answer to posted question, not solution to problem why you have now what you expect) and just change your line in controller to:
params[:tour][:hotel_ids] = params[:tour][:hotel_ids][0].split(',')
#split returns array and in your example you assigned this new array to first position of another array. That's why you had array-in-array.

rails if object.nil? then magic '' in views?

This is one of those things, that maybe so simple I'll never find it because everyone else already knows it.
I've got objects I have to check for nil in my views so I don't dereference a nil:
<%= if tax_payment.user; tax_payment.user.name; end %>
Or I could do this variant:
<%= tax_payment.user ? tax_payment.user.name : '' %>
So this is ok ... for most languages. But I feel like there must be some bit of shiny ruby or railness I'm still missing if this is the best I can do.
What about:
<%= tax_payment.user.name if tax_payment.user %>
You can also try the new Object.try syntax, pardon the pun.
This is in the shiny new Rails 2.3:
tax_payment.try(:user).try(:name)
The Ruby community has put an incredible amount of attention to automating this idiom. These are the solutions I know of:
try in Ruby on Rails
Another try
andand
A safer andand
Kernel::ergo
send-with-default
maybe
_?
if-not-nil
turtles!
method_ in Groovy style
do-or-do-not
The most well-known is probably the try method in Rails. However, it has received some criticism.
In any case, I think Ben's solution is perfectly sufficient.
I've always preferred this approach:
model:
class TaxPayment < ActiveRecord::Base
belongs_to :user
delegate :name, :to=>:user, :prefix=>true, :allow_nil=>true
end
view:
<%= tax_payment.user_name %>
http://apidock.com/rails/Module/delegate
For a little more comprehensive solution, you could check out the Introduce Null Object Refactoring. The basic mechanics of this refactoring is that instead of checking for nil in the client code you instead make sure that the provider never produces a nil in the first place, by introducing a context-specific null object and returning that.
So, return an empty string, an empty array, an empty hash or a special empty customer or empty user or something instead of just nil and then you will never need to check for nil in the first place.
So, in your case you would have something like
class NullUser < User
def name
return ''
end
end
However, in Ruby there is actually another, quite elegant, way of implementing the Introduce Null Object Refactoring: you don't actually need to introduce a Null Object, because nil is already an object! So, you could monkey-patch nil to behave as a NullUser – however, all the usual warnings and pitfalls regarding monkey-patching apply even more strongly in this case, since making nil silently swallow NoMethodErrors or something like that can totally mess up your debugging experience and make it really hard to track down cases where there is a nil that shouldn't be there (as opposed to a nil that serves as a Null Object).
I just do
<%= tax_payment.user.name rescue '' %>
Another option, which makes sense occasionally...
If tax_payment.user returns nil, nil.to_s (an empty string) is printed, which is harmless. If there is a user, it will print the user's name.
You could write a helper method which looks like this:
def print_if_present(var)
var ? var : ""
end
And then use it like this (in the view):
<%= print_if_present(your_var) %>
If the var is nil, it just prints nothing without raising an exception.

Resources