Synthesis : Scott Becker

RubyBrigade.org – A Rails Rumble Success

Just wanted to mention our Rails Rumble project, RubyBrigade.org. Jason Perry, James Seaman and I
worked through the weekend to build RubyBrigade.org – a geographically aware database of Ruby User groups.

Big thanks to James for the killer hand drawn illustrations and interface. Big thanks to Jason & Katie for letting us take over their house for the weekend.

Features:

More Screenshots

View a Brigade

Edit a Brigade

Delete a Brigade

404 Message

If you like what you see, vote for us!

Comments Off on RubyBrigade.org – A Rails Rumble Success

Are you SURE?! (How to confirm HTTP methods in Rails)

In Scott Raymond’s excellent book Ajax on Rails I came across a cool (non-ajax related) pattern for insuring a request’s method is POST and showing a confirmation form if not.

This is really useful in situations such as confirmation links in emails, where a form can’t be displayed and javascript won’t work, yet we don’t want a destructive action occurring through a GET request.

Here’s an example:

def unsubscribe
  if request.post?
    @user = User.find(params[:id])
    @user.update_attributes :subscribed => false
    redirect_to home_url
  else
    render :inline => %Q(
      <% form_tag do %>
        <%= submit_tag 'Confirm' %>
      <% end %>
    )
  end
end

One other piece of code you’ll need to get this to work, assumming you’re using RESTful routes, in config/routes.rb

map.resources :users, :member => {:subscribe => :any, :unsubscribe => :any} 

This states that we have a couple extra actions in our controller, and we’re not going to mandate a specific HTTP method to get there. This way we can handle it within the action if the HTTP method is wrong.

Now if your user comes to your page from a straight GET request, he’ll get prompted to confirm the big destructive action he’s about to commit.

(In a real app we’d make this form look a bit nicer.)

Wouldn’t it be nice if we could re-use this pattern, without writing out the inline form code every time? Seems generic enough.

DRY it up!

We could abstract that out, and also support any HTTP method we want. Lets follow Rails / RESTful conventions , and require PUT for updating a model. Using some handy ruby block syntax, we could write something like, say:

def unsubscribe
  confirm_unless :put do
    @user = User.find(params[:id])
    @user.update_attribute :subscribed, false
    flash[:notice] = "User is now unsubscribed"
    redirect_to users_url
  end
end

Much better. But how?! Keep reading.

Get one for yourself!

To get the confirm_unless method for yourself, slap the following code into app/controllers/application.rb:

def confirm_unless(method)
  if request.method == method
    yield
  else
    render :inline => %Q(
      <% form_tag({}, :method => :#{method}) do %>
        <%= submit_tag "Confirm" %>
      <% end %>
    )
  end
end

We could take it one step further and make it a before_filter, but I’ll leave that for a possible future post.

Comments Off on Are you SURE?! (How to confirm HTTP methods in Rails)

How to generate CSV files in Rails

A while back someone posted on rubyonrails-talk asking how to export to CSV from Rails. I posted a solution, and people seemed to dig it, so I’ll share it again here.

Use FasterCSV

Get the FasterCSV gem. Why? It’s faster, and easier to use. Once you’ve got it, require it in environment.rb. Here’s an abbreviated version of my working controller method. Copy/paste/modify. And you’re done!

def export_to_csv 
  @users = User.find(:all) 
  csv_string = FasterCSV.generate do |csv| 
    # header row 
    csv << ["id", "first_name", "last_name"] 

    # data rows 
    @users.each do |user| 
      csv << [user.id, user.first_name, user.last_name] 
    end 
  end 

  # send it to the browsah
  send_data csv_string, 
            :type => 'text/csv; charset=iso-8859-1; header=present', 
            :disposition => "attachment; filename=users.csv" 
end

HTML or CSV, have it your way

Now, if we wanted to get all clever about it, we could go further and serve both html and CSV data with only one action, using respond_to. This also requires that you add a RESTful route (map.resources :users) to your routes.rb file:

def index 
  @users = User.find(:all) 
  respond_to do |wants| 
    wants.html 
    wants.csv do 
      csv_string = FasterCSV.generate do |csv| 
        # header row 
        csv << ["id", "first_name", "last_name"] 
        # data rows 
        @users.each do |user| 
          csv << [user.id, user.first_name, user.last_name] 
        end 
      end 
      # send it to the browsah
      send_data csv_string, 
                :type => 'text/csv; charset=iso-8859-1; header=present', 
                :disposition => "attachment; filename=users.csv" 
    end 
  end 
end

Now if the user requests:

/users

she’ll get HTML. If she requests:

/users.csv

You get the point.

Comments Off on How to generate CSV files in Rails

More RejectConf Videos

From the ever-helpful Dr. Nic.

Comments Off on More RejectConf Videos

RejectConf2007 Videos

Found a couple videos from RejectConf, which I missed out on while at RailsConf. And since there was 4 parallel tracks at RailsConf, the most anyone could see is 25% of it. If anyone knows links to more videos from RailsConf07 or RejectConf07, I’d love to know.

Comments Off on RejectConf2007 Videos

A List Apart’s Web Design Survey

A List Apart is conducting their annual web designer survey:

Comments Off on A List Apart’s Web Design Survey

New version of AssetPackager

Thanks to Dan Kubb for alerting me to a new version of JSMin, the library used by AssetPackager to compress javascript.

With the release of the latest jQuery 1.1.1, it triggered a bug in how jsmin was treating characters within a regexp.

AssetPackager now has the latest version.

Also, for those using Prototype, the v1.5 release that comes with Rails 1.2.1 has a missing semi-colon on line 846. This of course breaks when compressed. To fix it, this line should have a semi-colon at the end. This:

if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='

Should be:

if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=';

Update: Michael Schuerig has posted this to the rails trac so hopefully this will be fixed soon. In the meantime, it’s pretty simple to make the change yourself.

Comments Off on New version of AssetPackager

Hash – to_query

Look what just appeared in Rails trunk – Hash#to_query – turn a hash of values into a form-encoded query string. I’ve wanted this on occasion in the past. Check it out:

Simple Conversion

{:a => 10}.to_query

Becomes this:

a=10

Nested Conversion

{:person => {:name => 'Nicholas', :login => 'seckar'}}.to_query

Becomes this:

person[name]=Nicholas&person[login]=seckar

Multiple Nested

{:person => {:id => 10}, :account => {:person => {:id => 20}}}.to_query

Becomes this:

account[person][id]=20&person[id]=10

Array Values

{:person => {:id => [10, 20]}}.to_query

Becomes this:

person[id][]=10&person[id][]=20

You can do the same thing in Prototype:

$H({ action: 'ship', order_id: 123, fees: ['f1', 'f2'], 'label': 'a demo' }).toQueryString();

Becomes this:

'action=ship&order_id=123&fees=f1&fees=f2&label=a%20demo'

It doesn’t look like Prototype handles the nested conversions yet though.

Comments Off on Hash – to_query

Asset Packager is in a book!

My plugin AssetPackager has been featured in Scott Raymond’s new book Ajax on Rails !

Thanks Scott! Everyone go buy a copy. AssetPackager has a full section in Chapter 9 – Performance. Nice.

Comments Off on Asset Packager is in a book!

New Plugin: MySQL Tasks

I wrote some convenience rake tasks to automate creation and backup of MySQL databases, and I use it all the time, so I figured I’d plugin-afy it to make it simple to use in all my projects, and share it with the world as well! Here’s the repository

MySQL Tasks

Some rake tasks to automate common database tasks (create/destroy & backup/restore).

Install

./script/plugin install http://sbecker.net/shared/plugins/mysql_tasks

Components

rake db:mysql:create           # Create database (using database.yml config)
rake db:mysql:destroy          # Destroy database (using database.yml config)
rake db:mysql:backup           # Dump schema and data to an SQL file (/db/backup_YYYY_MM_DD.sql)
rake db:mysql:restore          # Load schema and data from an SQL file (/db/restore.sql)

Specifying RAILS_ENV works if you want to perform operations on test or production databases.

Comments Off on New Plugin: MySQL Tasks