May 2007

Trackin’ your gems like you just don’t care

After going through the hell of chasing down gem after gem for a Rails project at work, then discovering that it actually uses a few older gems than what are currently released, I decided to write the gemtracker plugin.

Short summary: you write config/gems.yml, which is nothing more than a YAML hash of gem names and versions. When Rails initializes (but before it starts loading any of your custom initializers), it loads those gems at the requested version.

How is this better than just specifying, for instance, gem rspec, '1.0.1' in the top of your environment.rb? When you run Rails, it will throw an exception that tells you all of the missing gem versions at once, rather than having to go through the tiring loop of running script/server, checking the exceptions for the missing gem, installing it, and repeating ad nauseum. My hope is that it will also encourage developers to explicitly list the version of any gems used, which is a good practice in general for writing production apps.

It also provides a singular rake task, gems:install, which installs any versions of required gems that you’re missing (run it under sudo if you need root privileges). This should prove useful for larger projects with several developers. It should also be pretty trivial to add a Capistrano task that runs rake gems:install during an upgrade, ensuring that the environment there is identical to the developers’.

Hope this helps some of you out there.

Update: Apparently, init.rb went missing when I did some layout reorganization a few days ago. Thanks to an anonymous reader and DerGuteMoritz for bringing this to my attention. This has now been fixed.

Technical
Rails
Ruby

Comments (1)

Permalink

Behold! RSpec and autotest get married

It’s been tried before, with Nick Sieger and Philippe Hanrigou both providing some .autotest files to customize autotest for the purposes of RSpec. Using both their code as a template, I’ve come up with a few small changes for my own nefarious purposes. Behold, my .autotest!

Autotest.add_hook :initialize do |at|
# run spec with rcov
if at.respond_to? :spec_command
at.spec_command = %{script/spec --diff unified --colour}
end
end

module Autotest::GnomeNotify

# Time notification will be displayed before disappearing
# automatically
EXPIRATION_IN_SECONDS = 4
ERROR_STOCK_ICON      = "gtk-dialog-error"
SUCCESS_STOCK_ICON    = "gtk-dialog-info"

# Convenience method to send an error notification message
#
# [stock_icon]   Stock icon name of icon to display
# [title]        Notification message title
# [message]      Core message for the notification
def self.notify stock_icon, title, message
options = "-t #{EXPIRATION_IN_SECONDS * 1000} -i #{stock_icon}"
system "notify-send #{options} '#{title}' \"#{message.gsub(%r{"}, "\"")}\""
end

Autotest.add_hook :red do |at|
notify ERROR_STOCK_ICON, "Tests failed",
"#{at.files_to_test.size} test#{'s' if at.files_to_test.size != 1} failed.n" + at.results.scan(%r{'(.*)'}).flatten.first
end

Autotest.add_hook :green do |at|
if at.results.empty?
notify ERROR_STOCK_ICON, "No results", "Did you throw an exception?"
else
notify SUCCESS_STOCK_ICON, "All tests passed, good job!", at.results.scan(%r{d+ example[s]?, d+ failure[s]?}).first
end
end

end

What advantages does this bring? Well for one, you get colored output. For another, you get better notification of successes (number of tests passed, etc.) and failures (a description of the first failure). Even better, you get notified when you throw an exception (for instance, by mistyping the name of a method), which autotest would otherwise report as a success. It also does a dumb shell escape on any single quotes in the message sent to notify-send, so specs with apostrophes don’t break anything.

Of course, I’m sure it completely breaks if you try and use Test::Unit in conjunction, but who uses Test::Unit nowadays?

Update: Please excuse the unindented nature of my code. WordPress has a pathological need to reformat (read: “fuck with”) preformatted text. Because of general unhappiness with WordPress, I’m likely moving away quite soon. Possibly to Typo or Mephisto.

Technical
Rails
Ruby

Comments (0)

Permalink

Joyent Slingshot a Disappointment

The Atlanta RUG met today, and one of the topics discussed was the brand spanking new (released today) Joyent Slingshot. While sounding promising, it’s not really all that groundbreaking. Keep in mind that the following rant stems from initial impressions based on the presenter’s demonstration and group discussion about the application.

In case you aren’t familiar with it, Slingshot offers a way to run Rails applications in an online or offline mode for OS X and (theoretically) Windows. When online, changes made to the application by the user are done live, and when in offline mode, the user makes changes to a local copy that get synchronized when he can connect to the application again. It also runs as a native OS application, which seems great.

Unfortunately it’s not all that impressive. “Native OS application” simply means that it runs in an OS window, although all it really contains is an HTML control that renders the web page. Apparently there may be some OS integration features, but I’m not clear on that part. Essentially, all Slingshot does is runs script/server locally and gives the user a “control-less” web browser to use the application with. In other words, they’re dropped into a minimalist web browser with no back/forward button, no navigation controls, etc. The database is hosted locally in SQLite 3. When pulling changes, it serializes objects from the remote webapp into XML and loads them into your models locally. When pushing changes back to the webapp, it pretty much just calls ModelName.new(:foo => bar, :baz => :quux) which means anything you created likely gets a new id in the database (since other users may have added other objects and increased the primary key counter).

So, all it really seems to be is a glorified “single-click” script/server and local Rails instance, plus some lightweight synchronization and a way to detect whether the user is in online or offline mode.

But this has problems. The synchronization is intentionally dumb (real synchronization would need semantic understanding of your classes and how to manipulate them), which means lots of strange behavior. In a simple case, imagine creating a new Foo object with id 38. Sitting at http://localhost:3000/foos/38, you synchronize. Someone else already created a Foo object with id 38, so yours becomes 39. Now you’re pointed at the wrong page, but it hasn’t refreshed so you don’t know. With poorly-implemented local caching, this could become quite serious a problem when even a refresh fails to show the changed record.

In a more worrisome example, imagine you create an object and its id gets stored in the session for whatever reason. Now, you hit the synchronize button. The id of the object you were referring to changes, and another object holds that id instead. This could give you completely different sets of permissions (in the case of a User object), or do all sorts of other nasty things. You can potentially mitigate these issues by finding a way to use a UUID as your primary key, but it still doesn’t provide any sort of useful merging ability for when people edit the same record. acts_as_versioned might help you out here, but again this means you’ll need to add significant complexity in all but the simplest of cases.

In a way, Slingshot fails to live up to the excitement surrounding it. It seems kind of neat on the surface, but it looks like it will add so many potential problems to how your users access the application that on all but the smallest of sites with one or two concurrent users will suffer greatly.

Pros:

  • lets users work on a locally-cached copy of data while working offline
  • write local-only apps using only HTML and rails rather than Qt/GTK/Win32/Cocoa

Cons:

  • introduces synchronization issues
  • 100MB downloadable (ruby + rails + additional gems + application + slingshot)
  • no browser controls
  • won’t work if you use database-specific features in PostgreSQL, MySQL, etc.

Worries, potential gotchas, and sad reality:

  • running multiple slingshot apps at the same time might not work yet (just speculation)
  • no real support for OS-native features (my current understanding)
  • really just script/server and a local cached copy of the database (disappointing)

There are definitely some uses for this sort of thing (local apps without having to write a lick of GUI code), but far more limited than I was hoping for. The general consensus of the RUG seemed to be that it could potentially evolve into something neat, but it currently lacks anything generally compelling. Plus it has the potential of introducing lots of nasty synchronization bugs that would be hard to fix.

Perhaps this is a mischaracterization of Slingshot. I hope it is. I have never used it, and these comments are based on an hour long discussion of Slingshot and demonstration by one of the (seven, supposedly — and this was the only one of those seven who actually ended up implementing something with it) few developers who were granted the privilege of getting a demonstration version of the software in order to beta it.

Technical
Rails
Ruby

Comments (1)

Permalink