#ruby#rails#gems

The kitchen sink libraries

Hemant's avatar

Hemant

There was a time when Ruby libraries where like small Unix utilities, they did one thing and they did it well. If you go read code of some of the older plugins, it will be quite simple and straightforward affair.

Today however trend is, to write kitchen sink libraries that do a lot. You want file upload, they work at Rack level and support not only Rails but all the rack complaint frameworks under the sun, not only relational databases but Mongo as well, not just local file storage but S3/Cloudfiles as well, not just file uploads but some kind of caching via Rack::Cache as well.

Now at its core, handling file uploads is very simple and straightforward affair. Uploading to S3 or Cloudfiles is even simpler. I daresay, getting started with the kitchen sink library will be easy and simple as well. But if one thing Law of leaky Abstractions has taught us that is - eventually you will have look under the hood of these kitchen sink libraries and suddently MysqlAdapter which subclasses AbstractPersistenceAdapter won't appear very attractive, because your application doesn't gives two hoots about Mongo support. Jumping through hooves to grab that uploaded file via Rack will appear waste of time as well because you are probably never going to need this working on framework other than Rails. And while support for Rack::Cache was nice, you can't use it in production anyways.

I don't know about you, but I prefer gems and plugins that I include in my code easily grokkable. Below is a time bomb taken from a popular file upload gem and good luck - if you will have to grok this code and fix something yourself.

  autoload_files_in_dir = proc do |path, namespace|
    # Define the module
    eval("module #{namespace}; end")
    # Autoload modules/classes in that module
    Dir.glob("#{path}/*.rb").each do |file|
      file = File.expand_path(file)
      sub_const_name = camelize[ File.basename(file, '.rb') ]
      eval("#{namespace}.autoload('#{sub_const_name}', '#{file}')")
    end
    # Recurse on subdirectories
    Dir.glob("#{path}/*/").each do |dir|
      sub_namespace = camelize[ File.basename(dir) ]
      autoload_files_in_dir[dir, "#{namespace}::#{sub_namespace}"]
    end
  end