Rails

Posted on November 07, 2008 to Rails, haml, sass, and textmate  

First cd to your operating system's /Library folder (*not* ~/Library):

cd /Library/Application Support/TextMate/Bundles

Install the Haml bundle with:

sudo svn co http://macromates.com/svn/Bundles/trunk/Bundles/Ruby%20Haml.tmbundle

Install the Sass bundle with:

sudo git clone git://github.com/aussiegeek/ruby-sass-tmbundle.git "Ruby Sass.tmbundle"

Note: cloning the sass bundle without an additional directory parameter will not work. TextMate expects bundles to have the format "bundlename.tmbundle".

Posted on October 20, 2008 to Rails and git  

This article covers this topic in depth.

I’ve switched to using git submodules to manage external plugins. Recently I forked the rbet library and added it to a project via git submodules:

git submodule add git://github.com/sbraford/rbet.git vendor/rbet

But wait, I have new changes in my rbet library that I’d like to pull into my app:

git submodule update

No dice.

Git submodules are tied to the specific revision of the external repository at the time of checkout. Once more, with enthusiasm!

Git submodules are tied to the specific revision of the external repository at the time of checkout.

To update rbet to the latest version in this example, I had to do a:

cd vendor/rbet
git remote update
git merge origin/master

Then to make sure these changes get reflected in my main app:

cd ../..
git add .
git commit -a -m “updated rbet to latest”
git push

And presto, we have successfully ninja-unicorned our git submodule.

Posted on October 10, 2008 to Rails and crud  

Some rails projects have components that are little more than glorified CRUD interfaces to a database.

In this particular case, I’m working on a backend administration tool, bolting onto some legacy data.

Parts of the app need to be custom, with a nice GUI using in place editing, etc. But other parts are just a dumb listing of the data, with editing ability.

ActiveRecord::Base has a class method ‘columns’ which returns all of the metadata for the class’ database columns.

Here are a few helpers that you can put in a file, e.g. ‘extensions.rb’ in your config/initializers directory (Rails 2.1):

module ColumnHelperExtensions
  
  def field_columns
    columns.select {|c| [:string, :integer].include?(c.type) && !c.primary && !c.name.include?('id') }
  end

  def date_columns
    columns.select {|c| :date == c.type }
  end

  def boolean_columns
    columns.select {|c| :boolean == c.type }
  end

end

class ActiveRecord::Base
  class << self
    include ColumnHelperExtensions
  end
end

Your controllers will behave just as normal CRUD (e.g. scaffolded-style) rails controllers.

Here is an example edit.html.erb:

<h2>Editing <%= @foo.name %></h2>

<% form_for @foo do |f| %>
  <%= error_messages_for 'foo' %>
  <table class="standard_form">
    <%= render :partial => 'shared/generic_form', :locals => {:f => f, :klass => SparqUser} %>
  </table>
  <%= submit_tag "Update Foo &raquo;" %>
<% end %>

Then in ‘shared/_generic_form.html.erb’ we tie it all together:

<% klass.field_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.text_field(field.name) %></td>
  </tr>
<% end %>

<% klass.date_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.date_select(field.name) %></td>
  </tr>
<% end %>

<% klass.boolean_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.check_box(field.name) %></td>
  </tr>
<% end %>

When facing the task of maintaining views for a table with 20+ columns, I find this approach, at the very least, a good way to bootstrap a CRUD interface without lots of code bloat.

Note: for this to be effective, your columns have to be named at least somewhat sensibly. If you have a column titled ‘event_date’, this gets labeled ‘Event date’. Obviously a column named ‘trlf_widget_gzy’ will not make much sense to end users when humanized.

Posted on October 09, 2008 to Rails and ruby  

In several apps I've worked on, we've wanted a way to create a unique string. For example, users cannot have duplicate email addresses. So when creating many users via the Factory pattern, email addresses inevitably end up being something like "barney#{rand(1000)}@foo.org".

This always leads to problems. UUIDs can be handy but then you have a 30 character long email address.

Why not just a global, app-level incrementor? (utilizing the Singleton pattern)

It will always output a unique integer, starting with the number 1. Multiple objects can use this, so long as they do not require "random" integers to be in a row, but rather simply unique.

Enough talk. Here's the code:


module Incrementor

def self.next
@counter ||= Incrementor::Counter.new
@counter.next
end

class Counter
def initialize
@i = 0
end
def next
@i += 1
end
end

end

In rails, you can throw that as 'incrementor.rb' in your config/initializers folder.

Usage:


=> Incrementor.next
=> 1
=> Incrementor.next
=> 2
...

Posted on October 03, 2008 to Rails  

After switching blogging engines, this blog now has a new RSS feed URL:

http://shanti.railsblog.com/posts.xml

Using Rails 2.x's new 'respond_to' to keep controller's DRY.

Posted on October 03, 2008 to Rails  

It would be nice if One Blogging Engine to Rule them All would emerge for Rails, like WordPress has become for PHP.

The problem is, rails makes it too easy just to roll your own.

I'm now blogging from a custom blog platform, complete with support for multiple blogs / domains / themes / etc.

Comments are checked against Akismet. I should note that some shortcuts could be had due to the fact that this app is for internal (read: my own) use only. Thus, we don't get too fancy with Liquid templating engines, etc.

Sorry - legacy comments & categories were lost in the conversion (I'm just too lazy to port everything over), as well as some posts.

Just like everyone seems to end up rolling their own MVC framework for PHP, it seems too people will just end up rolling their own blog engine for Rails.

Posted on January 07, 2008 to Rails  

Update 3/24/08: Fark/reddit strikes, resulting in a 920k pageview day (same setup, just 7 mongrels).

Update 1/13/08: The site was on Digg again; this time receiving 450k views over the course of just 12 hours. Brings it up to 12 requests / second. Again this is not a benchmark, just the actual traffic. CPU usage was never a problem; more than likely the app is RAM bound. Oh and the $400 servers handle many other sites. To me, it’s more important not to have to think about servers, rather then pinching pennies in an attempt to squeeze every last ounce of performance out of machines. Just my $.02.

Over the weekend, this New Christian Science Textbook comic made it to the front page of Digg.

The above traffic graph should give you a sense of the kind of traffic that Digg can drive. It was front-paged sometime early Sunday morning. Chances are, if it hit during the middle of a weekday, the stats would be as much as 30-50% higher.

As you can see, roughly 550,000+ pageviews were logged over the course of 24 hours. Had this been over the course of 2-3 hours, the Incredimazing servers surely would’ve been crushed under the load.

Update note: had it been necessary, which it was not, enabling Rails Page Caching (see below) would have rendered the entire scalability question moot. nginx would’ve become the server from stack to stack for many pages — it’s been benchmarked at 250 to 330 requests per second (10M+ per day) which was clearly unnecessary in this case anyway.

Still, people are always (and probably will always) be wondering, can Rails scale?

The answer will always be amorphous, but at least I can give you some cold hard facts as to withstanding a digging of this nature.

The Hardware

The domain is hosted at LayeredTech on a dedicated two-server (DB + Web/App) setup. Several other sites can comfortably be hosted all on the same setup. The total setup runs about $370 per month.

Web/App Server

* Cost: $127 / mo. * CPU: Intel P4 – 2.8GHz * Memory: 2GB * Bandwidth: 1500GB (the box rarely uses more than 25% each month) * Uplink Port: 10Mbps * Hard drive: 80GB x2 * OS: Fedora Core 4

DB Server

* Cost: $242 / mo. * CPU: AMD Single CPU Dual Core Athlon 3800 * Memory: 2GB * Bandwidth: 2000GB * Hard drive: 500 GB x2 in SATA RAID 1 * Uplink Port: 100Mbps * OS: CentOS 4.x X86_64 Bit

Both have dual NIC cards and are connected across a private switch.

Software Versions Used

* Ruby: 1.8.4 * Rails: 2.0.2 * MySQL: 5.0.27 standard * nginx (web server): 0.4.13 (built by gcc 4.0.2) * mongrel: 1.0.1 * mongrel_cluster: 0.2.1

nginx – the Little Webserver that Could

nginx is a fantastic little web server developed by Igor Sysoev – and despite taking a hammering like this one, its memory usage rarely got above 10-20MB (total).

If you’re still not convinced (not that I’m trying to win converts here), Ezra Zygmuntowicz, author of Deploying Rails Applications, recommends it highly (and uses it extensively at EngineYard ).

Of course, Apache 2, Lighttpd, etc. would most likely have been able to handle the load similarly, so long as they were setup / configured / etc properly.

Rails 2.0 – Better ActiveRecord, etc. Performance?

I have no scientific evidence supporting this, but since making the switch to Rails 2.0, my sites have seemed a lot more stable and zippy. (this blog excluded – it sits lower on the totem pole!)

I believe some benchmarks of Rails 2.0 have shown some modest improvements over 1.0. My recommendation: update early & often — in about a day several of my sites were converted over to 2.0 with minimal issues.