Hoopla!

now with extra whiz-bang!

Hoopla!

Admiteer - your online ticketing app

December 29, 2007 · 4 comments

Last September Ryan Bates, Kelli Shaver and I took 3rd place in the Rails Rumble with Admiteer.

Now that we’ve raked in our massive cash prizes, bought some yachts and sports cars and all three of us retired to a tropical island we’re open sourcing our app under the MIT license.

There’s been a number of folks interested in building off of what we started. It’s a great beginning for any event-oriented application and it’s extremely easy to modify. What we’re posting is precisely what we submitted to the contest. No improvements, no corrections.

Enjoy: Admiteer via SVN

→ 4 comments Tags:

Sinatra, Merb, and Camping

October 18, 2007 · 6 comments

I found Camping first. It was hard to figure out because Google kept offering me places to vacation. Then I saw Merb. I was too busy shaving yaks to ever give it a real go. Now there’s Sinatra. It’s probably worth learning because it shouldn’t take more than 5 minutes.

I love that Ruby makes kickass websites in so many colors.

Thanks to DFisch for the heads up.

→ 6 comments Tags:

Time your Rails tests

October 13, 2007 · 3 comments

As a project gets bigger the tests take longer. There’s no getting around that. So unless you want to abandon testing entirely it might help to see who’s eating all those cpu cycles.

in test/test_helper.rb:

class Test::Unit::TestCase

  def run_with_timing(*args, &block)
    @timer = Time.now
    run_without_timing(*args, &block)
    puts "#{Time.now - @timer} - #{self}" 
  end
  alias_method_chain :run, :timing if ENV['TIMER']

end
And run it like so:

$ rake TIMER=true
# or, to see the 10 slowest tests:
$ rake test:units TIMER=true 2>/dev/null | grep " - " | sort -r | head -n 10
And if you really want to air the dirty laundry:

$ grep -R 'def test_truth' test/ | grep -v .svn

→ 3 comments Tags:

SimplePages plugin

February 23, 2007 · 1 comment

Rails is great. I love it. I want it to have my babies. But it's not so good at making folks who are stuck in the world of PageMaker and Dreamweaver feel at home. I have some clients who have bricks of html and they just want it to go web-side. Some are actually pretty web-savvy but they don't have any interest in learning rhtml. Or Liquid. They don't want to re-write their html in markdown or textile. And they certainly don't want every revision of a page to go through me and have me charge them just for Railsifying their document. They want to make a page in one of their WYSIWYG editors and post it online. That's it.

Any app is bound to get to a size where it needs an 'About' page, a Privacy Policy, or a Terms of Service. These are not worth their own controllers and often should be managed by the site's owners - not the developer. Ergo: the SimplePages plugin.

Update: The plugin now uses page caching - so you get all the benefits of a db-powered Rails app and all the performance of a static page!

View Plugin in SVN

note: Simple Pages requires the new-and-awesomeproved Engines plugin. Engines is now lightweight and allows any plugin to behave more like a Django app (a feature sorely needed in Rails).

What is SimplePages?

SimplePages provides a full controller/model/view/helper/migration/test stack for managing plain html pages that are editable right from your browser. You paste in your html and everybody else sees your Rails layout with the html inside. It's cake.

How to install SimplePages:


ruby script/plugin install -x http://svn.rails-engines.org/plugins/engines
ruby script/plugin source http://svn.6brand.com/projects/plugins/
ruby script/plugin install -x simple_pages

Add the following line to your config/routes.rb file:


ActionController::Routing::Routes.draw do |map|
  # all your important routes
  map.from_plugin :simple_pages
  # some lower-priority routes
end

And, finally, install the migration with the following command:


ruby script/generate plugin_migration

If you navigate to yoursite.com/pages you'll find you can now start doin' stuff.

Enhancements you should do:

One of the first things you might notice is that errors start popping up with helpful messages. They'll guide you to figuring out how to integrate your authentication scheme into the plugin and they'll remind you if you forgot to do any of the necessary installation. The most important change you should make is to add a method in application.rb called can_manage_pages? and have it test for whether the current user should be allowed to do stuff.

Version control your pages!

SimplePages has built-in support for techno-weenie's acts_as_versioned. Simply install his plugin (and run SimplePage.create_versioned_table if you've already installed SimplePages) and now you'll have access to all changes you make for your pages.

→ 1 comment Tags:

Advanced Rails Creation Script

February 10, 2007 · 0 comments

Akhil Bansal recently wrote two excellent scripts for creating new Rails projects. The first was in bash (a sensible choice) and the second was a Ruby port of the same code. I've taken his code one step further and developed it into a Ruby program that's easier to modify to your own needs.

Take the following code, throw it on your system somewhere and run it. It'll set up a Rails app and take care of all the nutty details like ignoring log files and removing those pesky tmp and components directories from your repository.

#!/usr/bin/env ruby
require 'fileutils'

class LayRails

  attr_accessor :path, :name, :svn_username, :svn_url, :svn_password

  def initialize
    setup && lay
  end

  def setup

    unless system('rails -v') || system('rails.cmd -v')
      puts "Cannot find rails. Terminating..."
      exit 0
    end    

    self.path = enter "Enter Rails Application Path:(eg: /home/akhil/ror): "
    self.name = ARGV.first || path.split('/').last
    self.svn_username = enter  "Enter svn username: "
    self.svn_password = enter "Enter the svn password: "
    self.svn_url = enter "Enter the svn url: "

    puts "
      ---------------------------------------
      Please verify the following variables: 
      Svn Username: #{svn_username}
      Svn URL: #{svn_url}
      Svn Password: #{svn_password.gsub(/./, '*')}
      Application Path: #{path}
      Application name: #{name}"

    while yes_no = enter("Proceed (y/n)").strip.upcase
      return true if 'Y' == yes_no
      if 'N' == yes_no
        puts 'Terminating...'
        exit 0
      else
        puts 'Please enter either a "y" or a "n"'
        puts 'yes_no: '+yes_no.inspect
      end
    end
  end

  def lay

    puts  "Generating rails project: (#{path})"
    @windows ?
      psystem("rails.cmd #{f path}") :
      psystem("rails #{f path}")

    puts  "SVNinitial import: "
    puts "svn import #{path} #{svn_url}/trunk -m \"Initial Import\" --username #{svn_username} --password #{svn_password.gsub(/./, '*')}"
    system "svn import #{path} #{svn_url}/trunk -m \"Initial Import\" --username #{svn_username} --password #{svn_password}"

    FileUtils.remove_dir(path, true)

    puts  "Checking out from svn: "

    psystem "svn checkout #{svn_url}/trunk #{path}"
    FileUtils.cd(path, :verbose => true)

    remove f('log/*'), 'Removing all log files from SVN'
    ignore '*', 'log', 'Ignoring all log files under log dir'
    remove 'tmp', 'Removing tmp directory from SVN'
    remove 'components', 'Removing components directory from SVN'
    ignore 'tmp', '.', 'Ignoring tmp dir'

    puts  "Generating optimized database file"
    File.open(f('config/database.yml'), 'w') {|f| f.write(database_yml_file) }
    puts  "Moving database.yml to database.example"
    psystem f('svn move config/database.yml config/database.example')
    psystem 'svn commit -m "Moving database.yml to database.example to provide a template for anyone who checks out the code " '
    ignore 'database.yml', 'config', 'Ignoring database.yml'
    puts  "Finished."
  end

  def remove(what, log)
    psystem "svn remove #{what}"
    commit log
  end

  def ignore(what, where, log)
    psystem "svn propset svn:ignore '#{what}' #{where}"
    psystem "svn update"
    commit log
  end

  def commit(log)
    psystem "svn commit -m '#{log}'"
  end

  def f(string)
    @windows ||= RUBY_PLATFORM =~ /mswin/i
    @windows ? 
      string.gsub('/', '\\') : 
      string
  end

  def psystem(string)
    puts string
    system string
  end

  def enter(string)
    puts string
    gets.strip
  end

  def database_yml_file
    <

production:
  <<: *credentials
development:
  <<: *credentials
  database: #{name}_development
test:
  <<: *credentials
  database: #{name}_test
DBYML
  end

  def credits
    puts < - based on the work of Akhil Bansal"
CREDITS
  end

end

# and away we go
LayRails.new

→ 0 comments Tags:

Ruby on Rails Web Hosting - A Complete Guide

January 23, 2007 · 28 comments

Update: It turns out that a lot of folks are looking for this information. I'll do my best to keep this list updated as new hosts appear. Please drop me a line in the comments to tell me what I'm missing.

Update2: Nick Snels of RailsForum has put together an excellent app that automatically compares Rails hosts. Way to go Nick! RailsHostingInfo.

Note: This list is 100% devoid of affiliate links.

One of my clients has a site that just started picking up a lot of traction. We developed it on a Dreamhost shared account because that was simply the best starter option for hosting Rails apps. Dreamhost has been largely reliable and they clearly are the industry leaders in deploying massive shared hosting accounts with all the latest technologies and shell access. What they don't have is scalability for those of us who dislike the occasional 500 error and would like some resources actually dedicated to our use.

So my client has asked me to compile a list of the hosts that are the next level up from DH's exteremely cheap (under $100 for two years!) hosting. I'm going to grab a bunch of this data from a post on RailsForum, though I'll try to parse it in a more readable format:

Shared Hosts

Dreamhost

  • $100 for two years (Google around for a special discount code)
  • 1TB transfer
  • 20GB space
  • known for cheapness, high limits, and occasional errors/downtime

Site5

  • $6.95/month for two years
  • 200GB transfer
  • 10GB space
  • seems about like DH but many customers claim Site5 is more reliable.
  • offers fewer accounts per server with higher-paying plans.

ASmallOrange

  • $25/year
  • 3GB transfer
  • 75MB space
  • This is the bare-minimal plan ASO offers but it's still more than more Rails sites will need.

PlanetArgon

  • $11.25/month for one year
  • 15GB transfer
  • 500MB space
  • can be configured to use: mongrel, lighttpd, pound, capistrano
  • Planet Argon has been heavily involved in the Rails community, it's run by RobbyonRails

Textdrive

  • $8.50/month for one year + $25 setup
  • 3GB transfer
  • 1GB storage
  • 6 databases
  • cannot be used for development or testing, only production
  • I'm not as impressed with these guys as with some of their competition, but they're still in the running.

Rails Playground

  • $5/month for one year
  • 30GB transfer
  • 3GB space
  • These guys excel at being a good development ground, hence the name. This starter plan is probably great for folks who are looking for the simplest and easiest way to get their feet wet.

VPS Hosts

RailsMachine

  • $75/month + $30 setup
  • 100GB transfer
  • 10GB space
  • 256MB dedicated memory
  • up to 6 Rails apps
  • 2 unique IPs
  • Perfect setup for a serious application. Just about the most stable and ideal setup. A little pricey, but well worth it.

RimuHosting

  • $29.95/month
  • 60GB transfer
  • 4-8GB space
  • 128MB memory
  • Seems like an affordable VPS option

Engine Yard

  • $249/month
  • 125 GB transfer
  • 15GB space
  • memory: unclear
  • A really novel approach. They have massive clusters and they sell 'slices' of those clusters. Easy to upgrade/downgrade but their base price is quite high.

SliceHost

  • $20/month
  • 100GB transfer
  • 10GB space
  • 256MB memory
  • Very high value for the price. A complete VPS with plenty of memory for RAM-hogging Rails apps. Web panel gives you control to reboot (or reinstall!) on the fly for no additional cost.

→ 28 comments Tags:

Sorry for the downtime, blame Rev 5776

January 18, 2007 · 0 comments

In my desire to stay on the cutting edge I often get cut. Recently I froze this app to edge Rails (two days out of date was too much for me I guess) and got the nasty end of a session bug. It's all better now - please proceed in an orderly fashion straight to the post of your choosing.

Unless you're not really interested in anything on this site. In that case - check this out! popularity of american names over time

→ 0 comments Tags:

Amazon S3 Backup via Rails Plugin

January 02, 2007 · 13 comments

I think Amazon S3 is awesome. I was looking into building a RAID NAS (Network-Attached Storage) for backing up all my important data and I nearly bought a setup that would run into the hundreds of dollars - but then I did a little fancy multiplication and addition and realized S3 would cost me less than one one-hundredth what the NAT would have cost.

In case you’re as in the dark about S3 as I recently was, here’s a little rundown: it’s a very simple, super fast, extremely large backup system that Amazon uses for all of it’s own storage needs. It’s opened the service up to the public on a pay-as-you-go basis.

Costs:

  • $0.15 per GB-Month of storage used
  • $0.20 per GB of data transferred

In other words, it’s damn cheap. Say you want to upload 500 MS Word documents that are around 45KB each? How much would it cost to store those on some easy-to-access, highly secure, permanent backup place? Less than one cent per month. Eight years later you’d only be out a half-dollar ($0.32 to be precise).

So S3 is my new storage/backup location of choice. The one difficulty of using it is that I need to figure out some way of automating the backups so that the backups are actually useful and can easily be recovered if necessary. In particular, I need some way to automatically backup the website data that is so crucial to me.

So I made a plugin.

The S3 plugin will allow you to backup your crucial website data to S3 via a handy Rake task (written by the talented Adam Greene).

Amazon has been an excellent supporter of Ruby/Rails lately (they fund 43things.com among other things) and they’ve made sure to release a ruby library for S3. I’ve combined that with Adam’s S3 rake task into a handy S3 backup plugin.

You can install it via the following two commands:

1
2
ruby script/plugin source http://svn.6brand.com/projects/plugins
ruby script/plugin install -x s3

Then backing up is easy as:

1
2
3
rake s3:backup:db
rake s3:backup:code
rake s3:backup:scm

or, to get them all together:

1
rake s3:backup

→ 13 comments Tags:

"Warning: colon will be obsoleted; use semicolon"

August 23, 2006 · 9 comments

If you’re getting the title of this post as a strange error showing up in your rails app you’re not alone. If you happen to see this you’re probably running a new version of Ruby (1.8.5).

The source behind this error is in the syntax for Ruby’s control structures (particularly using case and if. Here’s an example of some code from the Rails trunk that follows the currently popular style:

active_record/base.rb:

      def convert_number_column_value(value)
        case value
          when FalseClass: 0
          when TrueClass:  1
          when '':         nil
          else value
        end
      end
action_view/base.rb:

      def find_template_extension_for(template_path)
        if match = delegate_template_exists?(template_path)
          match.first.to_sym
        elsif erb_template_exists?(template_path):        :rhtml
        elsif builder_template_exists?(template_path):    :rxml
        elsif javascript_template_exists?(template_path): :rjs
        else
          raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}" 
        end
      end

If you’re getting the title of this post as a strange error showing up in your rails app you’re not alone. If you happen to see this you’re probably running a new version of Ruby (1.8.5).

The source behind this error is in the syntax for Ruby’s control structures (particularly using case and if. Here’s an example of some code from the Rails trunk that follows the currently popular style:

active_record/base.rb:

      def convert_number_column_value(value)
        case value
          when FalseClass: 0
          when TrueClass:  1
          when '':         nil
          else value
        end
      end
action_view/base.rb:

      def find_template_extension_for(template_path)
        if match = delegate_template_exists?(template_path)
          match.first.to_sym
        elsif erb_template_exists?(template_path):        :rhtml
        elsif builder_template_exists?(template_path):    :rxml
        elsif javascript_template_exists?(template_path): :rjs
        else
          raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}" 
        end
      end

The warning comes from the use of colons to separate a condition from a statement. Apparently Ruby is working toward eliminating the use of this colon in favor of a semicolon. Since a semicolon marks the end of a line of code it already does what this colon is doing. Here’s the code rewritten to use semicolons (so Ruby won’t barf out errors)

      def convert_number_column_value(value)
        case value
          when FalseClass; 0
          when TrueClass;  1
          when '';         nil
          else value
        end
      end
which is really just the same as:
      def convert_number_column_value(value)
        case value
          when FalseClass then        0
          when TrueClass  then        1
          when ''         then        nil
          else value
        end
      end
and with newlines instead of “then”s:
      def convert_number_column_value(value)
        case value
          when FalseClass
           0
          when TrueClass
           1
          when ''
           nil
          else value
        end
      end
and
      def find_template_extension_for(template_path)
        if match = delegate_template_exists?(template_path)
          match.first.to_sym
        elsif erb_template_exists?(template_path);        :rhtml
        elsif builder_template_exists?(template_path);    :rxml
        elsif javascript_template_exists?(template_path); :rjs
        else
          raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}" 
        end
      end
which is really:
      def find_template_extension_for(template_path)
        if match = delegate_template_exists?(template_path)
          match.first.to_sym
        elsif erb_template_exists?(template_path)        then     :rhtml
        elsif builder_template_exists?(template_path)    then     :rxml
        elsif javascript_template_exists?(template_path) then     :rjs
        else
          raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}" 
        end
      end
and with newlines:
      def find_template_extension_for(template_path)
        if match = delegate_template_exists?(template_path)
          match.first.to_sym
        elsif erb_template_exists?(template_path) 
         :rhtml
        elsif builder_template_exists?(template_path)
         :rxml
        elsif javascript_template_exists?(template_path)
         :rjs
        else
          raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}" 
        end
      end

As is often the case with Ruby/Rails this is largely an aesthetic issue. You can see some discussion about it here: http://www.ruby-forum.com/topic/75349

I’m curious what you think, are the colons pretty enough to stay or should the Rails code be adopted?

Edit: Ryan’s right, I’ve been making some alterations to this code while the post was live – my apologies for the underhandedness. Also I’m totally sorry for my spazzy comments. Even if it gives you some weird error it probably saved just fine. I’m working on un-jacking it.

Edit: If you’re running some form of *x system (macs included) and you’re getting annoyed by the warning output theres a simple way to hide everything but the regular ruby output:
rake test:units 2> /dev/null 

→ 9 comments Tags:

Howto: Make CSS Files Act Like Rails Partials

August 20, 2006 · 10 comments

Gone are the days when you could just throw a few CSS definitions in something called ‘style.css’ and call it a good day’s work. With the arrival of semantic html where none of the styling is done inline it has become important to practice good coding habits while creating CSS styles.

It was embarrasingly recently that I had the habit of putting all my CSS into one file. I didn’t realize that it’s the same bad habit of making programs with only one (usually massive) file of source code. Learning to partition the code properly is a natural development step and it’s just as important for CSS as Ruby, Java, C, or any other.

How I do CSS: —-

What I’ve found is I have a few default CSS files named after the following pattern:

  • application.css: usually will overwrite bad default values (like margins on forms)
  • structure.css: the layout of the blocks that make up my layout (body, #wrap, #head, #main, #foot, etc.)
  • fonts.css: font-family, font-size, etc. for all the popular styles
  • forms.css: whether using label/input pairs or definition lists I need lots of styling for forms
  • colors.css: super important to keep all color stuff together so you can both keep the colors in harmony and replace this file with some alternate color theme

These worked for a while but then I realized that I had little pieces of code that were needed that didn’t belong in any of these. I need to specify that a certain logo floats to the right or a users profile just happens to need a different background color.

The answer to this comes in using CSS files kinda like Rails uses partials. Some encapsulized code that has usefulness in a certain portion of your app. I did this at first by making stylesheets named after each of my controllers. This worked for about three seconds until I realized that I needed some conditional way to include them.

Conditionally including CSS files —- Including stylesheets conditionally turned out to be a fairly easy task. I’ll outline the solution here using extractions from my code:

1
2
3
4
5
6
class ApplicationController < ActionController::Base

  attr_accessor :stylesheets
  before_filter {|c| c.stylesheets ||= []; c.stylesheets << c.class.name.sub('Controller','').downcase }

end

This adds an instance var @stylesheets that starts as an array. It is given, by default the name of the currently active controller. note: this prints the derived class’s name, not ‘application’.

This means that you can now add stylesheets to the soon-to-be-rendered view anywhere in your code you want.

1
2
3
4
5
6
class UsersController < ApplicationController
  # if you have a stylesheet that governs just one action
  def show
    stylesheets << 'users_show'
  end
end

And it’s surprisingly simple to output the required code to the browser. Just add one line to your app/views/layouts/application.rhtml file:

1
2
3
4
5
6
7
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'structure'', :media => 'all' %>
<%= stylesheet_link_tag 'fonts'', :media => 'all' %>
<%= stylesheet_link_tag 'forms', :media => 'all' %>
<%= stylesheet_link_tag 'colors', :media => 'all' %>
<%= @stylesheets.collect { |file| stylesheet_link_tag file }.join("\n") if @stylesheets %>
<%= javascript_include_tag :defaults %>

This is so simple that there’s no real point in turning it into a plugin but if anybody wants it that way just let me know.

Update: The version I ended up using in my app did some testing for the existence of the file before it included it as a stylesheet. I decided it was more important to avoid the 404 errors (especially the way they cruft up the logfile) than it was to have the app disk-optimized. So here’s my new application_controller:

1
2
3
4
5
6
7
8
9
10
class ApplicationController < ActionController::Base

  attr_accessor :stylesheets
  before_filter do |c|
    c.stylesheets ||= []
    klass = c.class.name.sub('Controller','').downcase
    c.stylesheets << klass if File.exists?("#{RAILS_ROOT}/public/stylesheets/#{klass}.css")
  end

end

→ 10 comments Tags: