make camping output a doctype properly

If you have a layout in your Camping app, like this:

  def layout
    xhtml_strict do
      # awesome stuff here
    end
  end

it will not render with a DOCTYPE, even though it’s supposed to. The solution is to wrap it in a capture block:

  def layout
    capture do
      xhtml_strict do
        # awesome standards-compliant stuff here
      end
    end
  end

This is a -Markaby– Camping bug.

in which we explore attr_writer

Attribute writers are nice, because they can do things other than just assign attributes. If we spelunk some, though, a troll pops up and startles us with unexpected return values. Let’s explore.

into the mines

irb:0> def jumping= q
irb:1>   puts "jumped #{q}"
irb:2>   "jumping is so cool"
irb:3> end
=> nil

If we invoke the method as explicitly as possible, it behaves normally:

irb:4> self.send(:jumping=, "around")
jumped around
=> "jumping is so cool"

If we invoke it via the dot, but without send, the method gets called, but the syntax parser delivers the parameter as the return value. No local variable gets assigned (jumping is still undefined), but the actual return value is lost:

irb:5> self.jumping=("around")
jumped around
=> "around"

If we invoke the method “bare”, the = syntax triggers Ruby’s local variable assignment routine. Our method is never called:

irb:6> jumping=("around")
=> "around"

Watch out for those stalagtites.

activerecord is always relevant

This seems to be why assigning to a field via field= "something" in an ActiveRecord callback doesn’t do anything. It assigns a local variable and never actually calls the accessor method.

@field= "something" doesn’t work either, for a similar reason. Even though you can access fields with instance variables, the accessor methods don’t get called on assignment, so the new value never finds its way back to the hash used to update the database.

make false be true

Let’s be evil, and fake out false with some metaprogramming. We will do this by using method_missing to delegate identity methods to another object, namely, true.

meta meta meta

In Ruby there’s only one hard-coded false object. You can’t FalseClass.new to make a new, clean one. Even if you could, false’s singleton class (also known as metaclass) is strangely identical to its regular class:

irb(main):001:0> FalseClass == (class << false; self; end)
=> true

This is unlike Fixnum or Symbol, which don’t have singleton classes at all (see here for why).

There’s no way to retrieve regular false functionality after you do this, as far as I know.

even more meta meta

Anyone know why metametaclasses inherit the methods of metaclasses?

irb(main):001:0> require 'rubygems'; require 'metaid' #=> true
irb(main):002:0> a = "something" #=> "something"
irb(main):003:0> a.metaclass.instance_eval("def ok; true; end") #=> nil
irb(main):004:0> a.ok #=> NoMethodError
irb(main):005:0> a.class.ok #=> NoMethodError
irb(main):006:0> a.metaclass.ok #=> true
irb(main):007:0> a.metaclass.metaclass.ok #=> true

That doesn’t seem right. Shouldn’t the last one be NoMethodError?

postscript, which is not meta at all

Bee is on RedHanded today; hooray.

sti support in has_many_polymorphs

At the request of Kevin Marsh, the polymorphs plugin now supports single-table inheritance.

example

the sti base class
class Person < ActiveRecord::Base
end
sti children
class Hipster < Person
end

class Groupie < Person
end
the collection parent class
class Concert < ActiveRecord::Base
  has_many_polymorphs :concert_goers,
      :from => [:hipsters, :groupies]
end

Then you can do a_concert.concert_goers, a_concert.groupies, etc., and everything will work.

download

Get it here.

redirect aliased hosts to a single canonical host with apache

We have a new Apache 2.2/mongrel server set up, and wanted people who type in host.com to be redirected to http://www.host.com, instead of merely aliasing all the same content and confusing Google. This is an obvious thing, but I still had trouble finding a straightforward how-to, so here it is.

old, bad way

All of this takes place in your httpd.conf, or your vhost.conf, or whatever file refers to your host in question in whatever of the infinite Apache configuration possibilities you have managed to implement.

Here’s what you probably had before:

<VirtualHost *:80>
  ServerName www.site.com
  ServerAlias site.com blog.site.com

  <Proxy balancer://site_com_cluster>
    BalancerMember http://127.0.0.1:3050
# etc...

new, awesome way

If you want a specific redirect type (303, permanent, etc.), you can put it after the Redirect keyword. But this way works fine, and the word on the street is that Google treats every type as a 301 anyway, so it doesn’t really matter what you put.

Here’s what you should have:

<VirtualHost *:80>
  ServerName site.com
  ServerAlias blog.site.com
  Redirect / http://www.site.com/
</VirtualHost>

<VirtualHost *:80>
  ServerName www.site.com

  <Proxy balancer://site_com_cluster>
    BalancerMember http://127.0.0.1:3050
# etc...

the end

Shed a tear for lighttpd; we were driven away by a bizarre rendering bug in Camping/fastcgi (extra linebreak before every output body, even with x-sendfile). Unfortunately Apache/mongrel does not seem super-stable at this point.

Deployment… sucks.

how to publish feeds with resource_feeder

The resource_feeder plugin, along with simply_helpful, recently snuck into edge Rails, courtesy of DHH. They’re nice. resource_feeder lets a controller return a well-formed RSS or Atom feed for any array of ActiveRecord instances. I don’t really know what simply_helpful does, but resource_feeder requires it, so whatever.

install the plugins

First, check out the plugins as svn:externals so you can keep up to date easily.

Go to whatever folder you keep Camping plugins in, or make a folder, and run:

svn propedit svn:externals .

Add these lines to the file:

resource_feeder http://dev.rubyonrails.org/svn/rails/plugins/resource_feeder
simply_helpful http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful

Now, svn up. You will have the plugins.

require them

Now your Camping application needs to load them. One way to do that is this:

%w[simply_helpful resource_feeder].each do |plugin|
  $LOAD_PATH.unshift("plugins/#{plugin}/lib")
  require plugin
  $LOAD_PATH.shift
end

You also need to add a few support methods somewhere in your application (or in an external require), since this is not Rails.

require 'rubygems'
require_gem 'actionpack'
require_gem 'activesupport'

module ResourceFeeder::Atom
  #emulate Rail's request object with a singleton method on @env
  def request
    class << @env
      def host_with_port
        self['HTTP_HOST']
      end
    end
    @env
  end
end

class String
  def pluralize
      Inflector.pluralize(self)
  end
end

That it. It’s all Ruby in the end, so we can make resource_feeder think it’s operating in a Rails environment even though that’s totally a lie.

usage example

Now we can make a feed controller. Assume our Camping app is named Awesome, and that we want to use Atom and not RSS:

module Awesome::Controllers

  class FeedMe
    include ResourceFeeder::Atom

    def get
      @things_to_eat = ThingsToEat.find(:all, :limit => 15, :order => "created_at DESC")
      @things_to_eat = [ThingsToEat.new(:title=>"No results", :id => 0)] if @things_to_eat.blank?
      @headers['Content-Type'] = "application/xml"
      atom_feed_for @things_to_eat,
        {:feed => {:title => "Things to eat 4 u", # feed title
                    :ttl => 40}, # time-to-live in minutes
         :items => {:title => :title,
                    :pub_date => :created_at}}
    end

That would be it, except we also need some specially named methods to return the canonical url of the feed itself and also of the individual records.


    def awesome_models_thing_to_eats_url
       "http://www.aweso.me/" + R(ViewThingToEat)
    end

    def awesome_models_thing_to_eat_url(obj)
       "http://www.aweso.me/" + R(ViewThingToEat, obj.id)
    end

  end
end

One last thing: your models have to respond to .title and .description methods, which provide the title and body of the feed entry, respectively. Sometimes you can just point this at another field, but sometimes you will want to mess around with formatting and aggregating multiple fields into one, specially for the RSS output.

this bug juice won’t come out of my shirt

In order to be able to feed resource_feeder an empty array, you need to fix two small bugs so that we can make it work with new() records. Currently resource_feeder bombs out if there are no records, which we clearly don’t want if we are making a feed for a dynamic search or something like that, in which case an empty result set is perfectly valid.

Also, we will fix it externally so that we don’t run into merge conflicts in the future when the plugin gets updated.

First, the new record must have an id, even if it’s completely bogus, or resource_feeder complains. So we assign it 0, as above.

Second, resource_feeder likes to retrieve the xmlschema of things even if they are nil. So we will give nil its own special xmlschema:

class NilClass
  def xmlschema; ""; end
end

Of course you knew to do that already :) .

postcript

I’d like to use Remember the Milk as my scheduler now that I don’t have a corporate job anymore. But I want events to find their way wirelessly onto my Treo’s calendar and there isn’t a way to do that. I never use that Palm Desktop thing.

I realize I am probably like one of three people in the universe who would want this.

rails security note

Rails Ticket 4339 suggests that there is a denial-of-service vulnerability because of the way the Rails request handler instantiates a symbol for every incoming request method. This is technically true, but turns out not to matter, as seen below.

help i’m allocated and i can’t get released

There are only a few valid HTTP request methods: GET, POST, PUT, etc. But Rails creates the symbol before it figures out what to do about it. If you make a FOO /index.html HTTP/1.1 request, Rails will instantiate a :foo symbol. As you know each unique symbol requires ~60 bytes of memory in the Ruby intepreter and is never garbage collected. The idea is that by spamming the server with lots of bogus request methods you can exhaust its memory space.

whatever man

It’s easy to write a script to exploit this with a small change to rfuzz. I ran such a script against a localhost-mounted generic Rails app (the ever-present cookbook example). The test environment was a dual-core Intel Mac, one mongrel process, production environment, and terminal output redirected to /dev/null. I increased the number of attempted simultaneous requests until the mongrel thread reached 100% cpu usage.

Rails memory usage at start-up:
time real memory virtual memory
7:54am 21.04mb 51.24mb

After one hour of fuzzing:
8:56am 37.5mb 69.87mb

I should have counted how many requests were made, but I didn’t, for the sake of speed. We can guess, though. 35.09mb / 60b = 613,242 maximum requests.

The upshot of this is that a successful exploit of this issue entails a fierce regular-style denial-of-service attack. On a real network, the sheer number of requests will cause more issues than the possibility of eventually crashing some mongrels or fastcgi processes. So basically, it doesn’t matter.

silly strings

Also, this will be fixed completely with the new symbol handling in Ruby 1.9, since then Symbol will go out of scope and get garbage-collected like any old String.

make camping connect to mysql

By default, Camping connects to a sqlite database in an undisclosed location, similar to Dick Cheney. This confuses practically everybody. Here’s how to make your application use MySQL (or anything you have a Ruby binding for) when you are ready to move it to production.

update

Make sure to read the comments section.

preliminaries

You can’t change the database connection as long as you are using the camping command line tool. You have to bootstrap your app yourself.

This means that you have to be able to start it with ruby myapp.rb, because camping myapp.rb will never pay attention to your special configuration and continue to use a sqlite database. This database will either be the file .camping.db in your home folder on Unix-like systems, or the file camping.db in /Documents and Settings/{current_user}/Application Data/ on Windows, if I remember correctly.

with mongrel

With mongrel, you can make the harness be a postamble in your application’s main file.

put this at the end of your_app.rb
if __FILE__ == $0
  require 'mongrel/camping'

  YourApp::Models::Base.establish_connection :adapter => 'mysql',
    :database => 'camping_yourapp',
    :username => 'camper',
    :password => 'secret'
  YourApp::Models::Base.logger = Logger.new('your_app.log')
  YourApp::Models::Base.threaded_connections = false
  YourApp.create # only if you have a .create method
                 # for loading the schema

  server = Mongrel::Camping::start("0.0.0.0",80,"/", YourApp)
  puts "YourApp is running at http://localhost:80/"
  server.run.join
end

Now you can instantiate mongrels by executing your_app.rb directly, and proxy them through your webserver if necessary. Add a command-line switch to change the port if you need to instantiate a bunch of mongrels all at once.

Please also read this if you need multiple instances of your application to use a single sqlite database concurrently.

with fastcgi

If you are using fastcgi, be sure to put the harness in a separate file. If you don’t, you may get very strange superclass mismatch errors.

your_app_dispatch.rb
#!/usr/local/bin/ruby
require 'rubygems'
require 'camping/fastcgi'

Camping::Models::Base.establish_connection :adapter => 'mysql',
  :database => 'camping_yourapp',
  :username => 'camper',
  :password => 'secret'
Camping::FastCGI.serve("/path/to/your_app.rb")

Then your server can use your_app_dispatch.rb as the fastcgi executable.

Notice how the above uses Camping as the module name, and not YourApp. Also, we didn’t establish a log file here for Base, but we could, similar to the mongrel example.

done

That’s all. If your Camping app connects to Postgres instead of MySQL (:adapter => 'postgresql') you are officially hardcore.

make polymorphic children belong to only one parent

What is the best way to set up a polymorphic collection such that a child is only allowed to have one parent? WheeledOne asked this recently in #polymorphs. It turns out there are several solutions.

example

A good example of such a structure is a file tree. Directory can contain Directories and Files in its directory_contents, but a directory_contents item can’t belong to multiple parent directories.

an association which i can define

We could merely have a belongs_to :directory in File and Directory, and construct a directory_contents field by hand, like so:

class Directory < ActiveRecord::Base
  has_many :directories
  has_many :files

  def directory_contents
    directories + files
  end
end

This way we don’t need a join table. However, the simplest solution, in this case, is not the best. The query for directory_contents will scale at O(n) where n is the number of classes in the polymorphic collection. Also, you can’t add arbitrary items to the collection; instead, you have to assign the parent to the item, which may be conceptually inappropriate in some cases.

it feels so good to be real

What if we want behavior and attributes on the association? For example, maybe we want to record the time and date the file was added to the directory independently of the time and date the contents of the file were modified. Or maybe we just want to avoid the scaling problem mentioned above, by using has_many_polymorphs and its optimized SQL.

class Directory < ActiveRecord::Base
  has_many_polymorphs :directory_contents,
    :from => [:directories, :files],
    :as => :parent_directory
end

But now a file or directory could belong to two or more parents. If we want a tag-style system, we’re done. But maybe we really loved Pilot Wings, Jamiroquai, and flannel, and multiple parentage is unacceptable, since this is the ‘90s.

We could add validates_uniqueness_of on the join table:

class DirectoriesDirectoryContent < ActiveRecord::Base
  belongs_to :directory_content, :polymorphic_true
  belongs_to :parent_directory, :class => Directory,
    :foreign_key => :parent_directory_id

  validates_uniqueness_of :directory_content_id,
    :scope => [:directory_content_type]
end

This will enforce the one-parent association we want. However, we will always have to check for and delete existing records for a child before we can save the new association.

(The scope is necessary because we want a File and a Directory with the same id to be able co-exist in the join table.)

deeper underground in activerecord

When we move a file in our desktop filesystems, it no longer belongs to the old directory. We don’t have to unlink it by hand first; in fact, we don’t have to pay any attention to the old linkage at all. How can we do this in ActiveRecord? With a callback.

class DirectoriesDirectoryContent < ActiveRecord::Base
  # snip

  def before_save
    if record = DirectoriesDirectoryContent.find(:first,
       :conditions => "id != #{id}
         AND directory_content_id = #{directory_content_id}
         AND directory_content_type = #{directory_content_type}")
       record.destroy
    end
    true
  end
end

Now saving a new association will remove any existing association with the same child (excepting itself).

Virtual insanity! He was really talking about join tables…