Nooby Help with created_at, updated_at, and if/then


#1

Hola. I’m super-new to actually doing things in rails and with ruby, so
pardon me if this is an easy question to everyone else.

I’m making a web app, and at this time am designing the news/blog
portion of it. I have a controller (posts) and a model (post) designed
to coordinate this. I started out with a basic scaffold and went from
there. At this point, I’m trying to get rails to print “Updated by
author on 1/1/07 at 3:45PM” if the updated_at time is different than the
created_at time. Here’s my code:

<% for post in @posts.reverse %>

<%= link_to post.title, {:action => 'show', :id => post}, {:class => 'posttitle'} %> <%= link_to 'Edit',{ :action => 'edit', :id => post }, { :class => 'postedit' } %> <%= link_to(image_tag('destroypost.png'), {:action => 'destroy', :id => post}, :confirm => 'Are you sure?', :method => :post)%>

<%= textilize(post.body) %>
Written by <%= post.author %> on <%= post.created_at.strftime("%B %d, %Y at %I:%M%p") %>
<% end %>

However, that string never shows up, and even weirder is that in the
default scaffold code, the updated_at and created_at values are the same
for every row. For the record, the default scaffold code is this:

<% for column in Post.content_columns %> <% end %>

<% for post in @posts %>

<% for column in Post.content_columns %> <% end %> <% end %>
<%= column.human_name %>
<%=h post.send(column.name) %><%= link_to 'Show', :action => 'show', :id => post %> <%= link_to 'Edit', :action => 'edit', :id => post %> <%= link_to 'Destroy', { :action => 'destroy', :id => post }, :confirm => 'Are you sure?', :method => :post %>

Any insight? Tips?


#2

Okay, I’m dumb. I copied that after I deleted the defective code. But
could anyone show me the best way to do that comparison?

If created_at isn’t equal to updated_at, print “Updated by author on
1/1/07 at 3:45PM”


#3

Some might debate this, but I find the best way is to factor the
whole thing into a helper or a model. E.g.:

my model.rb

def timestamp_string
updated_at > created_at ?
“Updated by #{author} at #{updated_at.to_formatted_s(:short)}” :
“Created by #{author} at #{created_at.to_formatted_s(:short)}” :
end

my view.rhtml

<%= post.timestamp_string %>

This should clean up your view. If you feel this couples your model
too tightly with the display of the data, then create a helper instead.

Does this help?


#4

Steve R. wrote:

def timestamp_string
updated_at > created_at ?
“Updated by #{author} at #{updated_at.to_formatted_s(:short)}” :
“Created by #{author} at #{created_at.to_formatted_s(:short)}” :
end

Yes, that should be good for cleaning things up, but I could still use
some interpretation of this.

How does the:
‘updated_at > created_at ?’
line translate?

If updated_at is greater than created_at, then execute this block. Is
that right?

Does this take into account that I’d rather not have that “Updated
by…” string in there if updated_at and created_at are the same or do I
need to change this to:

def timestamp_string
updated_at > created_at ?
“Updated by #{author} at #{updated_at.to_formatted_s(:short)}”
“Created by #{author} at #{created_at.to_formatted_s(:short)}”
else
“Created by #{author} at #{created_at.to_formatted_s(:short)}”
end


#5

def timestamp_string
updated_at > created_at ?
“Updated by #{author} at #{updated_at.to_formatted_s(:short)}” :
“Created by #{author} at #{created_at.to_formatted_s(:short)}” :
end

I’ve not seen the one-line if-then-else statement form like:
? <action_to_do_if_true> : <action_to_do_if_false>

expanded into MORE than one line. I just tried it in irb. It works –
SORT OF – but it’s hard to follow. These both work:

true ? 1 : 2

true ?
1 :
2

… generating an output of “1”. But you can’t leave anything out. So
these generate an error:

true ? 1

true ?
1

… as do:

true ? 1 :

true ?
1 :

Besides, as Ben notes, they’re not clear. So indeed, do it the easy
way:
def timestamp_string
if updated_at > created_at
“Updated by #{author} at
#{updated_at.to_formatted_s(:short)}”
“Created by #{author} at
#{created_at.to_formatted_s(:short)}”
else
“Created by #{author} at
#{created_at.to_formatted_s(:short)}”
end
end

But this won’t work either, because methods return only the LAST
assignment statement, unless you include an explicit "return
". So regardless of the values of updated_at and
created_at, the above would return ONLY the line starting with
“Created by”. (Try it in irb.)

Let’s try again, eliminating the duplicate assignment while we’re at
it. I added an end-of-line character so the strings wouldn’t just run
together. And because you can’t use the “+=” method unless a variable
has been initialized as a string, number, array, or something similar,
you wind up with something like:

def timestamp_string
s = “”
if updated_at > created_at
s = “Updated by #{author} at
#{updated_at.to_formatted_s(:short)}\n”
end
s += “Created by #{author} at
#{created_at.to_formatted_s(:short)}”
end

If you REALLY wanted, you could shorten this to:

def timestamp_string
updated_at > created_at ? s = “Updated by #{author} at
#{updated_at.to_formatted_s(:short)}\n” : s = “”
s += “Created by #{author} at
#{created_at.to_formatted_s(:short)}”
end

—Jim Gagne—


#6

Did I inadvertently stick a colon at the end of that? Oops. The
ternary operator, ?, is equivalent to an if/else. So:

updated_at > created_at ? stmt_a : stmt_b

is equivalent to:

if updated_at > created_at
stmt_a
else
stmt_b
end

It all depends on what you’re used to.


#7

That’s why I used the ternary operator. It seems obvious to me that
the last evaluation will depend on the result of the conditional.

irb(main):001:0> a = 1
=> 1
irb(main):002:0> b = 2
=> 2
irb(main):003:0> a > b ? ‘a is greater than b’ : ‘a is not greater
than b’
=> “a is not greater than b”
irb(main):004:0> a = 2
=> 2
irb(main):005:0> b = 1
=> 1
irb(main):006:0> a > b ? ‘a is greater than b’ : ‘a is not greater
than b’
=> “a is greater than b”
irb(main):007:0>


#8

Thanks Everyone. Very Helpful :slight_smile: