How to find a random ActiveRecord object

July 23rd, 2010
Station.find(:first, :offset => (rand Station.count))

Title case for ruby strings

June 15th, 2010

I don’t know why the code snippets out there for turning ruby strings into Title Case are so complicated. Here’s one I hacked out based off of Justin French’s string extensions.

It’s probably slower than the others, or missing some exceptions, but it’s good enough for my current purpose.

class String
  def titlecase
    downcase.split.map {|w| capitalization_exceptions.include?(w) ? w : w.capitalize}.join(" ").upfirst
  end
 
  def upfirst
    self[0,1].capitalize + self[1,length-1]
  end
 
  private
    def capitalization_exceptions
      [ 
        'of','a','the','and','an','or','nor','but','if','then','else','when','up','at','from','by','on',
        'off','for','in','out','over','to'
      ]
    end	
end

I love Ruby

June 8th, 2010
>> Dec[4] - 14.weeks
=> Sat, 28 Aug 2010

How to set up a postgresql user for rails (on Ubuntu)

May 30th, 2010

This took a little while to figure out, and existing documentation wasn’t super helpful for me, so here are some notes.

First off, you create new postgres users with the createuser command, but every time I ran it I was getting an error like this:

createuser: could not connect to database postgres: could not connect to server: No such file or directory
	Is the server running locally and accepting
	connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

What got me over that hump was installing postgresql-8.3 instead of 8.4, which is the default you get on Ubuntu when you install the postgres package. This is what I wanted anyway, since Heroku (my host) uses 8.3:

sudo /etc/init.d/postgresql-8.4 stop
sudo apt-get remove postgresql
sudo apt-get install postgresql-8.3

If you want to check if your postgresql server is running, run ps ax | grep post. You should get something like this:

 4417 ?        S      0:00 /usr/lib/postgresql/8.3/bin/postgres -D /var/lib/postgresql/8.3/main -c config_file=/etc/postgresql/8.3/main/postgresql.conf
 4420 ?        Ss     0:00 postgres: writer process                                                                                                    
 4421 ?        Ss     0:00 postgres: wal writer process                                                                                                
 4422 ?        Ss     0:00 postgres: autovacuum launcher process                                                                                       
 4423 ?        Ss     0:00 postgres: stats collector process                                                                                           
 4482 pts/4    D+     0:00 grep post

Now you can create your user:

sudo su postgres
createuser -A -d --pwprompt the_username

And then you should be able to create your databases with rake db:create.

If you are getting errors like this:

rake db:create
(in /path/to/your/app)
FATAL:  Ident authentication failed for user "your_username"
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/postgresql_adapter.rb:968:in `initialize'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/postgresql_adapter.rb:968:in `connect'
...

… it is because Postgres expects there will be a UNIX user that corresponds to each postgres user. When it talks about “Ident” it’s talking about the UNIX authentication system. You could just create a UNIX user with the same credentials as your postgres user, but I took a different route.

As suggested by Felix Sun, I edited my pg_hba.conf (which on Ubuntu is in /etc/postgresql/8.3/main). I didn’t do exactly what he did though, I just added another line as follows:

local   all         postgres                          ident sameuser
local   all         all                                  md5

As far as I understand, that just means that any user connecting locally on any database is OK as long as they authenticate with a password.

I restarted postgres (/etc/init.d/postgresql-8.3 restart) and ran my rake db:create; rake db:migrate and I was in business.

My startup

May 29th, 2010

I don’t know how many people subscribe to this blog, since it’s basically just a dump of random snippets of code. But if you do and you’re curious what I’ve been referring whenever I’ve mentioned “my site” over the last year, I just launched it a couple of weeks ago, so you all can see it.

It’s called SproutRobot, and it tells you when to plant your vegetable garden and sends you seeds! It’s still quite a mess, but it does something useful, and it will hopefully only get better. At some point you just have to take the plunge.

Look forward to future posts about optimizing activerecord-driven sites for speed, I’m sure. :)

Rails forms for has_many relations

May 29th, 2010

This is pretty cool. Say you have a model with a has_many relation, and you want to edit the model and all it’s children on one page. Rails can take care of all of this, automatically, with the standard controller. You just need this in your parent model:

class User < ActiveRecord::Base
  has_many :posts
  accepts_nested_attributes_for :posts
end

And you need a form like this:

<% form_for(@user) do |f| %>
  <%= f.label_for :blog_name %>
  <%= f.text_field :blog_name %>
 
  <% f.fields_for :posts do |p| %>
    <%= p.label_for :title %>
    <%= p.text_field :title %>
    ...
  <% end %>
 
  <%= f.submit %>
<% end %>

And that’s it! You can use all the craziness that the form helpers do (grouping radio buttons, converting to values booleans, etc) and it all works out.

It’s exciting. I feel like I’m finally getting into the really good stuff with Rails.

Setting the default background color for transparent PNGs

May 7th, 2010

I’ve been using .png images on my site, which are great for putting on top of gradient backgrounds. But unfortunately IE6 just displays a flat background for them.

Probably the best thing to do would be to switch to transparent .gif images with carefully chosen background colors that will blend reasonably on the anticipated background color. But that’s a lot of work.

Instead, I’m just setting the background color in the .pngs to something close to the gradient background. You can still see the box, but whatever. If you’re using IE6 you obviously don’t care if the web sites you visit look funky.

The way to do this is just to create an extra layer in The GIMP below your other layers, fill it with the background color you want in IE6, and then set the transparency to 0%. Won’t affect modern browsers.

IE6 text invisible until selected and deselected

May 7th, 2010

I’m embarking on the painful journey of making my site passable usable in Internet Explorer. Not fun.

First bizarre bug: sometimes text is simple not displayed, until you select it and then deselect it. And then it’s there. Nice work, Microsoft.

I was able to fix it by changing the nesting of my divs and form elements.  I changed this:

<div>
  <form>
  ...
  </form>
</div>

… to this:

<form>
  <div>
  ...
  </div>
</form>

Lots to look forward to.

Failed search terms: ie6 text hidden/invisible unless I select it, deselect

Custom error pages in Rails (with Exceptional support)

April 19th, 2010

This one took way longer than I would’ve expected. I just wanted to replace those ugly “Internal Server Error” pages with nice ones that include my feedback form.

The easy way to do this in Rails is just to replace the files in /public (404.html, etc) with nicer ones. But I wanted to use my existing templates and forms and such, so I needed to dig a little deeper.

There’s a ton of stuff all over the place on the web about this, and I tried a bunch of stuff to no avail. But what ended up working well for me is basically doing what Tanel Suurhans suggests.

The one thing that is messed up about those instructions is that you need to set config.action_controller.consider_all_requests_local to FALSE in your development.rb for testing, and then back to true when you’re done messing around with the error pages. Tanel has it backwards I think.

The other thing is, if you’re using Exceptional instead of HopToad, your render_error method needs to look more like this:

def render_error(exception)
  log_error(exception)
  Exceptional::handle(exception)
  render :template => "/error/500.html.erb", :status => 500
end

Redirecting to a secure subdomain with Authlogic

April 19th, 2010

In my application, I wanted to redirect my users from my domain to https://secure.mydomain.com for checkout, so their credit card info would be transmitted securely.

Unfortunately, if I just do a simple redirect, the user gets logged out, because Authlogic (and maybe the cookies themselves?) only work on a per-domain basis.

There are various ways to do this, including having Authlogic authenticate for your whole domain, and not just the one subdomain, but the solution I chose is to use Authlogic’s single use tokens. This just means, I get a token that can sign the user in one time, and I pass the user with that token to my secure domain.

Here’s the relevant bits of my controller:

class CheckoutController < ApplicationController
  before_filter :require_user, :except => [:with_authentication]
 
  def index
    if RAILS_ENV == 'production' && !request.ssl?
      domain = "https://secure.mydomain.com"
      token = Authlogic::Random.friendly_token
      current_user.update_attributes(:single_access_token => token)
      redirect_to "#{domain}/checkout/with_authentication?user_credentials=#{token}"
    else
  end
 
  def with_authentication
    user = User.find_by_single_access_token(params[:user_credentials])
    session = UserSession.new(user)
    session.save
    redirect_to '/checkout'
  end
end

I also had to add the single_use_token field to my users table:

script/generate migration add_single_access_token_to_users single_access_token:string
rake db:migrate

What happens now is if a user goes to /checkout and they’re not using SSL, they get redirected to something like http://secure.mydomain.com/checkout/with_authentication?user_credentials=fh3f83ahfa92ha, which authenticates them and creates a new session on the secure subdomain. Then it redirects them to /checkout on that domain.

The one thing that wasn’t very well explained on The Google is how you get the single use token in the first place. The docs explain how to use it, but now how to create it. As you see above, you just have to generate a random one with Authlogic::Random.friendly_token and then stick it in your user model.

Seems to work ok.

One of the other issues is that this totally screws up your Cucumber features. Cucumber basically expects you to stay on one domain. There are some good suggestions on the Rspec mailing list for getting around this, but I ultimately chose the hacky, ugly solution of just selectively redirecting when we’re in the production environment. It didn’t seem worth it to do it the right way… especially given that the “right” ways aren’t all that much prettier.

Keywords: login to multiple subdomains, cucumber ssl redirect, https