A Fresh Cup is Mike Gunderloy's software development weblog, covering Ruby on Rails and whatever else I find interesting in the universe of software. I'm a full-time Rails and iOS developer.

Navigation

A Fresh Cup

Notes on Rails and other development

Sunday
Oct262008

Double Shot #320

Rails 2.2RC1 is out. Here's the official announcement. And here are the Ruby on Rails 2.2 Release Notes - of which I am rather fond, because I wrote the bulk of them.

Some other Rails 2.2 stuff:

  • Ruby on Rails 2.2 - The Rails Envy guys want to sell you PDF and videocast guides to the new features.

  • What's New in Edge Rails: Rails 2.2 Released - Summary of Features</a> and What's New in Edge Rails: Even Better Conditional GET Support - Coverage from Ryan Daigle.


  • And more general links:

  • "raise NoMethodError" raises NoMethodError. Raise it with
    NoMethodError.new instead.
    - My first commit to the core Rails code. Hopefully not the last.

  • Good Homes Wanted - A batch of Rails and Ruby projects looking for new maintainers, including some fairly prominent ones.

  • SIPr - SIP stack written in Ruby. If only I had time to play with VOIP stuff...

  • REST: Some tips and implementing "Forgot your password?" - Some ideas about RESTful design.

  • Thinking Sphinx - New PDF from PeepCode.

  • GitHub Rebase #1 - Nick Quaranto is starting a new series to look at activity over on GitHub.

  • How to Spot a Help Vampire - Wisdom from #rubyonrails.
  • Saturday
    Oct252008

    Rails 2.1.2 and 2.2RC1: Update Your RubyGems

    There's a mismatch between the most recent Rails releases (2.1.2 and 2.2RC1) and older versions of RubyGems. Judging by what I've seen in various discussion fora, this is well on the way to being a FAQ. It's made more fun by the facts that
  • The error message doesn't implicate RubyGems

  • You may think you're up-to-date on RubyGems when you're not

  • If you're affected, you'll see this message when you try to run script/generate in a Rails application:

    [sourcecode language='ruby']
    undefined method empty?' for /_generator$/:Regexp
    [/sourcecode]

    If you see this, don't panic. It just means you need a newer RubyGems release. I'm not sure how current you need to be, but 1.1.1 is definitely too old. As I write this, 1.3.0 is current.

    Now, in theory, to update to the latest version of RubyGems, you just need to run

    [sourcecode language='ruby']
    sudo gem update --system
    [/sourcecode]

    But - depending on what version of RubyGems you have installed, running that command may lie and tell you that you have nothing to update. If you're currently on RubyGems 1.1 or 1.2, you need to run a different set of commands to update RubyGems:

    [sourcecode language='ruby']
    sudo gem install rubygems-update
    sudo update_rubygems
    [/sourcecode]

    You can check your current RubyGems version with

    [sourcecode language='ruby']
    gem -v
    [/sourcecode]
    If it reports 1.3.0 or later, you should be good to use the recent Rails releases.
    Friday
    Oct242008

    Rails 2.2 Change: Private Methods on Association Proxies are Private

    There's a small change in Active Record behavior that's going to bite some existing applications under Rails 2.2: private methods on an association proxy are actually private. To illustrate, this code would work in Rails 2.1:

    user.rb:

    [sourcecode language='ruby']
    class User < ActiveRecord::Base
    has_one :account
    end
    [/sourcecode]

    account.rb:

    [sourcecode language='ruby']
    class Account < ActiveRecord::Base
    belongs_to :user
    private
    def secret_code
    "ABCDE"
    end
    end
    [/sourcecode]

    Controller code:

    [sourcecode language='ruby']
    @u = User.first
    @code = u.account.secret_code
    [/sourcecode]

    With Rails 2.1, you'll actually get the secret code in @code, even though it comes from a private method of the account object. Rails 2.2 is stricter about this: that same code will fail with a NoMethodError. If you want to access a private method across an association proxy now, you'll need to use @code = u.account.send("secret_code").

    Directly accessing private methods on an Active Record instance itself was already blocked in Rails 2.1, but Rails 2.2 updates that failure to give back a NoMethodError as well.
    Friday
    Oct242008

    Double Shot #319

  • Learn the Language, Then the Framework - Good advice, though I'm afraid I've built much of my own career on ignoring good advice.

  • Path Finder 5.0 - Supposed to be a really good Finder replacement, and the new version is just out. I'm contemplating trying it, at least.

  • Rails 2.1.2: Security, other fixes - Worth upgrading if you're on the 2.1 branch. The 2.2 release candidate is just around the corner as well.

  • Thread safety for your Rails - Yes, it's there. No, it's not magic pixie dust. Pratik Naik explains.
  • Thursday
    Oct232008

    Better Controllers with finder_filter and from_param

    I've been nibbling away at how to write the code in Rails controllers recently, and I'm at a point where I'm fairly happy with what I'm doing. The two design requirements for me were:

    1. Write as little code as possible

    2. Get decent SEO-friendly URLs


    Rails already supports friendly URLs, sort of, with to_param. Consider the canonical Post model from a blogging application:

    [sourcecode language='ruby']
    class Post < ActiveRecord::Base
    def to_param
    "#{id}-#{title}".gsub(" ","-").downcase.gsub(/[^a-z0-9-]/,"").gsub(/(-)*$/,"")
    end
    end
    [/sourcecode]

    Armed with that definition, it's possible to write a controller that works with URLs like /blogs/6-my-latest-blog-entry:

    [sourcecode language='ruby']
    class PostsController < ApplicationController

    def index
    @posts = Post.find(:all)
    end

    def show
    @post = Post.find(params[:id])
    end

    def new
    @post = Post.new
    end

    def edit
    @post = Post.find(params[:id])
    end

    def create
    @post = Post.new(params[:post])
    if @post.save
    flash[:notice] = 'Post was successfully created.'
    redirect_to(@post)
    else
    render :action => "new"
    end
    end

    def update
    @post = Post.find(params[:id])

    if @post.update_attributes(params[:post])
    flash[:notice] = 'Post was successfully updated.'
    redirect_to(@post)
    else
    render :action => "edit"
    end
    end

    def destroy
    @post = Post.find(params[:id])
    @post.destroy
    redirect_to(posts_url)
    end
    end
    [/sourcecode]

    OK, works fine. But there are a couple of things that annoy me. First is repeating the code to turn things into a slug in every model (though, with Inflector.paramterize coming in 2.2, this is going to get easier). Second is having to include a numeric ID at the start of the parameter. Third is those repeated calls to find(params[:id]).

    Enter a couple of plug-ins: finder_filter, which gets rid of some of the duplication, and from_param, which handles the URLs a bit better. With those installed, I can rewrite the code this way:

    [sourcecode language='ruby']
    class Post < ActiveRecord::Base
    def to_param
    title.urlize
    end
    end

    class PostsController < ApplicationController
    finder_filter :only => [:show, :edit, :update, :destroy]

    def index
    @posts = Post.find(:all)
    end

    def show
    end

    def new
    @post = Post.new
    end

    def edit
    end

    def create
    @post = Post.new(params[:post])
    if @post.save
    flash[:notice] = 'Post was successfully created.'
    redirect_to(@post)
    else
    render :action => "new"
    end
    end

    def update
    if @post.update_attributes(params[:post])
    flash[:notice] = 'Post was successfully updated.'
    redirect_to(@post)
    else
    render :action => "edit"
    end
    end

    def destroy
    @post.destroy
    redirect_to(posts_url)
    end
    end
    [/sourcecode]

    Perfect? Naw, not really. But to my eyes, better. And getting better a little bit at a time is good enough for me.

    The originals of those two plugins are not mine: finder_filter comes from Matthew Bass, and from_param comes from Michael Bleigh. But I've tweaked them both to work better together, which is why I linked to my forks of the projects.