Quick Tip: Rails 2.1 Time Zones

September 13th, 2008

My last quick tip involved setting your time zone in Ubuntu Hardy, so now, how do you set your time zone in a Ruby on Rails application? Rails 2.1 makes it much easier to manage time zone settings than it was previously.

Add the following to your environment configuration file:

config/environment.rb

config.time_zone = 'Arizona'

Replace Arizona with your own time zone. You can find a list of valid values by running any of the following rake tasks:

rake time:zones:all
rake time:zones:local
rake time:zones:us

Your data will still be stored in UTC time, but it will be converted into the specified time zone when it is type cast on retrieval.

Update: If you set both your server’s time zone and your applications time zone then you may see some incorrect times. I believe this is because you’ll be storing local times and the Rails app will be trying to convert them to local – a double conversion. So be careful.

References

I’ve been pretty enamored with SliceHost recently. They make it very easy to setup a slice, configure it and get your product deployed quickly (I’m down to 30 min). I hadn’t noticed until just recently that I’ve never set a time zone on any slice I’ve configured. So, here’s how you do it, simple and easy.

  1. SSH into your slice
  2. Run this command: sudo dpkg-reconfigure tzdata
  3. Select your geographic area and location

That’s it, hope it helps!

If you want your own slice, consider using my Slicehost referral link. Thanks!

Quick Tip: Form Partials

September 1st, 2008

Partials are a great way to keep your view code separated logically. Prior to Rails 2.1 if you wanted to reuse a form partial in, for example, a new and edit view, then you needed to pass the form into the partial somehow.

Given the following form partial:

views/users/_form.html.erb

<div>
  <%= form.label :name -%>
  <%= form.text_field :name -%>
</div>

Pre-Rails 2.1

You might consider passing the form in as a local, like so.

views/users/new.html.erb or views/users/edit.html.erb

<% form_for @user do |form| -%>
  <%= render :partial => 'form', :locals => { :form => form } -%>
<% end -%>

Rails 2.1

There’s now a shortcut for this common method of rendering a form partial.

views/users/new.html.erb or views/users/edit.html.erb

<% form_for @user do |form| -%>
  <%= render :partial => form -%>
<% end -%>

Nice! Cleans things up a bit.

Quick Tip: SSH Backspace

August 26th, 2008

I just ran across this informative post by Jonathan Tron that solved an annoyance I’ve had for a while: backspace not doing what I want when I use SSH.

Change your Terminal Preferences

  1. In Terminal, select Preferences | Settings | Advanced.
  2. Select ‘Delete sends Ctrl-H’

You may need to do this again if you change your terminal style.

Specify a setting for nano

  1. On your server open ~/.nanorc
  2. Add ‘set rebinddelete’

And now you should have backspace working correctly for at least a few things…

Quick Tip: Route Associations

August 26th, 2008

Are you used to writing your routes like this?

map.resources :notes do |notes|
    notes.resource  :author
    notes.resources :comments
    notes.resources :attachments
  end

Don’t fret, there may be hope for you yet. For these simple routes you can use the has_one or has_many route association options.

  • has_one – use it for a singleton resource
  • has_many – use it for plural resources

Refactored routes

map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]

Give it a try!

Keep in mind I said simple. If you’re doing something more complex they might not be the best choice.

Additional Resources

It’s really easy to create a many-to-many relationship that can be assigned through checkboxes. Check it out!

Let’s say you have Users and Groups. A User can belong to a Group and a Group can have many Users – we call this a Membership, like so (migrations omitted for brevity):

app/models/user.rb

class User < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy
  has_many :groups, :through => :memberships
end

app/models/group.rb

class Group < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy
  has_many :users, :through => :memberships
end

app/models/membership.rb

class Membership < ActiveRecord::Base
  belongs_to :group
  belongs_to :user
end

We can now assign groups to members in a relatively easy manner with no extra work needed in the models. Behold!

app/views/users/edit.html.erb

<h1>User <%= @user.id -%></h1>

<h2>Group Memberships</h2>
<% form_for @user do -%>
  <% Group.all.each do |group| -%>
    <div>
      <%= check_box_tag :group_ids, group.id, @user.groups.include?(group), :name => 'user[group_ids][]' -%>
      <%= label_tag :group_ids, group.id -%>
    </div>
  <% end -%>
  <%= submit_tag -%>
<% end -%>

Errr… something like that. Anyway, the important thing to note is the use of group_ids. The values will get submitted as group_ids, a member of the User. Where did that come from? We don’t have an attribute or method on the model for it, so where’d it come from? Well, seems that it is auto-generated for you to allow something like I just showed.

When this form is submitted, any checked Groups will be associated through Memberships to the User by way of the magic *_ids= method. Should work the other way too with user_ids checkboxes on a group. No extra code needed. Awesome, right?

Bonus: If you uncheck all the checkboxes, then nothing gets posted, doh! So make sure to merge a default value with your parameters like this to ensure the *_ids= method gets called:

app/controllers/users_controller.rb

@user.attributes = {'group_ids' => []}.merge(params[:user] || {})

Super Bonus: When you’re defaulting the group_ids in the controller make sure to use the key as a string, not a symbol. Or if you do use a symbol then make it a Hash with_indifferent_access.

Super Monkey Ball: A monkey encased in a ball who collects bananas.

If you have changed the SSH port number on your server, then you need to let Capistrano know how to connect. Luckily, it’s pretty easy.

Add the following to your deployment file, replacing 8888 with your port number.

config/deploy.rb

ssh_options[:port] = 8888

This will apply that port number to connections made by Capistrano. If you need to specify the port for each server (app, web, db) then tack it on to the end of their declarations.

role :app, "65.74.169.199:8030" 
role :web, "65.74.169.199:8031" 
role :db,  "65.74.169.199:8032", :primary => true

I haven’t verified that one, but it supposedly works. Happy deployments!

Quick Tip: SSH Config

August 22nd, 2008

It’s easy to create shortcuts for all those servers you need to SSH into. There should be a file in your .ssh directory called config. Simply add a few lines to this file and you can refer to those servers by their short name.

Example (~/.ssh/config)

Host foo
  Hostname some.long.hostname.you.cannot.remember.com
  User myuser
  Port 1234

Now you can use your new host when you SSH.

ssh foo

If you want to get even fancier, read the man page for additional options.

man ssh_config

Setup as many as you need and free yourself from SSH hostname hell!