Creating acts_as_… gems for Rails 3.1.x

There are a lot of posts on how to build rubygems. With Rails 3.1, though, they’re all old and busted.

The new hotness is built right into rails now. Just incant

rails plugin new APP_PATH

You’ll get:

  • a .gemspec and Gemfile to get started
  • a dummy rails app to integration test your new gem against
  • a (deprecated) RDoc rake task

When you run rake, you’ll see “rake/rdoctask is deprecated. Use rdoc/task instead (in RDoc 2.4.2+)”. To remedy, follow these steps.

The Edge edition of the Ruby on Rails Guides has more information.

Hierarchical Tagging with Rails 3 and Closure Trees

In rebuilding PhotoStructure on Rails, I was surprised that one of the most popular gem for trees used a nested set model. Nested sets are performant for reads, but for adding and deleting nodes, it’s extremely expensive — on average, half of the rows for your model acting as a nested set have to be updated for every add or delete. That’s crazy-talk! It requires a table-level lock for something that, at least for PhotoStructure, is a very common task. To top it off, the gem doesn’t even do the lock!

I then found the ancestory gem, which works by materializing the ancestral path as a string and storing that as a column. Bill Karwin’s excellent Models for hierarchical data presentation describes this as the Path Enumeration algorithm, which doesn’t have referential integrity, and relies on performant LIKE selects.

As the tag hierarchies in PhotoStructure are never moved, closure trees should prove to be ideal. It’s an excellent excuse to learn how to build and publish a rails plugin gem, so I’ve built a new gem to support closure trees: github.com/mceachen/closure_tree.

Update, May 24

1.0.0.beta1 released. All public methods have test coverage.

Update, May 25

1.0.0.beta2 and 1.0.0.beta3 were released, which added find_or_create class and instance methods, ancestry_path, and root instance methods. Documentation is on the README and in the rdocs.

Update, May 29

1.0.0.beta5 is released, which cleaned up the ancestor and descendant relationships to use has_and_belongs_to_many, and adds leaves class and instance methods.

Update, Oct 26

2.0.0.beta1 is released, which added the :dependant option, switched from an embedded dummy rails app for testing to rspec, and much better test coverage (including tests and fixes for a couple reported issues) under sqlite, MySQL, and PostgreSQL.

Update, Nov 27

3.0.0 is released, which supports polymorphic trees.

Rails 3.0.5 broke my routes and kicked my dog

We just upgraded AdGrok from Rails 3.0.4 to 3.0.5, which was released a couple days ago. This is a “patch release,” which according to the rules, has only backwards compatible bug fixes.

A bunch of our integration tests failed before we pushed to production, and we found out that a pretty big change was introduced: namespaced urls are now prefixed by the namespace, and before 3.0.5 (like, 3.0.4), they weren’t.

Here’s the simplest example of what’s going on:

Continue reading

HOWTO: Force https/SSL for Apache2, Phusion Passenger and Rails

There’s a lot of buzz right now about Firesheep and non-secure Rails applications.

This is a pretty simple problem to solve with Apache’s mod_rewrite. If the traffic isn’t on https, force it to be. This configuration only needs to be in production, of course.

Here’s /etc/apache2/sites-enabled/adgrok:

Continue reading

Switching between Rails 2 and Rails 3 on Mac OS X or Ubuntu with RVM

I’m migrating AdGrok to Rails 3 this weekend–what with the new ActiveRecord query functionality, and MongoMapper, and all our gems migrating to Rails 3, it seems like it’s time.

The Railscast (and ASCIIcast) is great, but a couple steps are now outdated. There also aren’t any instructions on how to switch between rails 2 and rails 3 gracefully.

Continue reading

HOWTO: Fix Ruby on Rails 2.3.x textarea Value Truncation

Turns out that Rails versions 2.3.6 through 2.3.8 have a pretty horrid parameter-parsing bug, and I’m surprised there hasn’t been more hoopla about it.

If you have a textarea form that someone types a quote character into, the value you get back from params[:key] is truncated at the point of the quote.

Monkey patching to the rescue!

I decided to add a new raw_params method to ActiveRecord::Request, rather than fixing the bug and dealing with more potential side-effects, assuming this mess will be fixed in Rails 3. If it isn’t, we should fix ActionController::Base.param_parsers[:url_encoded_form] to return the properly-parsed parameters from the raw_post.

In your rails application, create config/initializers/request.rb, with the following contents:

Continue reading

Dialed-in Rails script/console with pretty printing and history

Edit (as root) your /etc/irbrc:

# Some default enhancements/settings for IRB, based on
# http://wiki.rubygarden.org/Ruby/page/show/Irb/TipsAndTricks
 
unless defined? ETC_IRBRC_LOADED
 
  # Require RubyGems by default.
  require 'rubygems'
 
  begin
    require "ap"
    IRB::Irb.class_eval do
      def output_value
        ap @context.last_value
      end
    end
  rescue LoadError => e
    puts "ap gem not found.  Try typing 'gem install awesome_print' to get super-fancy output."
  end
 
  # Activate auto-completion.
  require 'irb/completion'
 
  # Use the simple prompt if possible.
  IRB.conf[:PROMPT_MODE] = :SIMPLE if IRB.conf[:PROMPT_MODE] == :DEFAULT
 
  # Setup permanent history.
  HISTFILE = "~/.irb_history"
  MAXHISTSIZE = 100
  begin
    histfile = File::expand_path(HISTFILE)
    if File::exists?(histfile)
      lines = IO::readlines(histfile).collect { |line| line.chomp }
      puts "Read #{lines.nitems} saved history commands from '#{histfile}'." if $VERBOSE
      Readline::HISTORY.push(* lines)
    else
      puts "History file '#{histfile}' was empty or non-existant." if $VERBOSE
    end
    Kernel::at_exit do
      lines = Readline::HISTORY.to_a.reverse.uniq.reverse
      lines = lines[-MAXHISTSIZE, MAXHISTSIZE] if lines.nitems > MAXHISTSIZE
      puts "Saving #{lines.length} history lines to '#{histfile}'." if $VERBOSE
      File::open(histfile, File::WRONLY|File::CREAT|File::TRUNC) { |io| io.puts lines.join("\n") }
    end
  rescue => e
    puts "Error when configuring permanent history: #{e}" if $VERBOSE
  end
 
  ETC_IRBRC_LOADED=true
end

Thanks to Nick Sieger and Jared Haworth for sharing.

Installing Phusion Passenger on Ruby 1.9.1, Nginx, & Ubuntu 10.04

I wrote this a while back, before I knew about RVM. The Ruby Version Manager has a bunch of features that makes it better than macports for ruby and gem installation. Read about it here.

Getting ruby 1.9.1 and nginx and passenger and ubuntu to all play nicely is fairly straightforward, but it’s not just “apt-get” and “gem install” lovin’.

Making ruby 1.9.1 the default ruby is OK. Follow these steps:

Continue reading

Quick MacPorts Installation of Ruby on Rails

October 2010 update: I wrote this a while back, before I knew about RVM. The Ruby Version Manager has a bunch of features that makes it better than macports for ruby and gem installation. Read about it here.

I finally got to walking through a great gentle introductory ALA article on Ruby on Rails. I wanted to run rails Demo to build my first project, but I got nastiness:

$ rails -v
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:379:in `report_activate_error': RubyGem version error: rake(0.7.3 not >= 0.8.3) (Gem::LoadError)
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:311:in `activate'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:337:in `activate'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:336:in `each'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:336:in `activate'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:65:in `active_gem_with_options'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:50:in `gem'
        from /usr/bin/rails:18

That’s not especially welcoming. Let’s see if MacPorts can come to the rescue.

After installing MacPorts, this got me going:

sudo port selfupdate
sudo port install rb-rubygems
sudo gem install rails

I saw this from the gem install rails:

Successfully installed rake-0.8.3
Successfully installed activesupport-2.2.2
Successfully installed activerecord-2.2.2
Successfully installed actionpack-2.2.2
Successfully installed actionmailer-2.2.2
Successfully installed activeresource-2.2.2
Successfully installed rails-2.2.2
7 gems installed
Installing ri documentation for rake-0.8.3...
Installing ri documentation for activesupport-2.2.2...
Installing ri documentation for activerecord-2.2.2...
Installing ri documentation for actionpack-2.2.2...
Installing ri documentation for actionmailer-2.2.2...
Installing ri documentation for activeresource-2.2.2...
Installing RDoc documentation for rake-0.8.3...
Installing RDoc documentation for activesupport-2.2.2...
Installing RDoc documentation for activerecord-2.2.2...
Installing RDoc documentation for actionpack-2.2.2...
Installing RDoc documentation for actionmailer-2.2.2...
Installing RDoc documentation for activeresource-2.2.2...

I had some issues because my PATH included /usr/bin before /opt/local/bin (so MacOS’s old gem binary got picked up before MacPorts’ version). You can tell you’re running the correct version:

$ which gem
/opt/local/bin/gem
$ gem -v
1.3.1

If you see version 1.0.1, you’re still need to follow these instructions to install MacPorts. Add something like this to your ~/.bashrc or ~/.profile:

export PATH=/opt/local/bin:/opt/local/sbin:$PATH

Anyway, looks like things are good now. Yea new toys.