Duplo

Building Blocks & Learning Experiences

Browsing Posts in Ruby

I’ve often been sad that there is no default keybinding for comment-region in Emacs’s ruby-mode. Eventually it annoyed me enough that I added one:

(add-hook 'ruby-mode-hook
	  (lambda ()
	    (define-key ruby-mode-map "\C-c#" 'comment-or-uncomment-region)
	    )
	  )

This assigns C-c # to comment the current region, or if the current region is already commented, it will uncomment the region.

But what about when there’s no region currently marked? It would be nice if emacs would (un)comment the current line. To do this, I took a page from DJCB at Emacs-fu.

(defadvice comment-or-uncomment-region (before slick-comment activate compile)
  "When called interactively with no active region, comment a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
	   (line-beginning-position 2)))))

Now with nothing marked, pressing C-c # will cause emacs to toggle commenting on the current line.

In case it isn’t obvious, one should add the abbove snippets to their .emacs file to gain their benefits.

Autotest-inotify is a gem that extends autotest to use Linux’s inotify to monitor changes to your source and test files, running the appropriate tests as files are modified.

By default, autotest implements filesystem polling to detect these changes. This can use a significant amount of CPU cycles1, and can impact battery life in laptops.

Through Linux’s inotify, autotest-inotify inserts callbacks into the underlying filesystem, which allows the filesystem to notify us when files we’re interested in have been modified. This allows autotest to sleep until inotify indicates a change has been made to one our files of interest, i.e. we don’t have to constantly poll the filesystem.

This work was inspired by Sven Schwyn’s work on autotest-fsevent, which extends autotest to use Mac OS X’s FSEvents system, as well as Alban Peignier’s work using the INotify gem. Where autotest-inotify differs from Schwyn’s work, is that autotest-inotify can be used in Linux, whereas autotest-fsevent uses FSEvent, which is Mac OS X specific. Autotest-inotify differs from Peignier’s work in that it offers a simpler gem-based installation, and it is less obtrusive to autotest’s methods for determining which files to watch for changes.

Source code for the autotest-inotify gem can be found at http://github.com/ewollesen/autotest-inotify. It is licensed under the MIT license. See the README for installation instructions.

  1. On one of my machines, ~25% of a single core’s cycles were spent polling the filesystem []

In the process of setting up metric_fu, I found that, one way or another, saikuro’s output wasn’t getting into the correct place. I’ll spare you the long story, and just show the config settings I had to put into my Rakefile to get things working:

config.saikuro = {
  :output_directory => "#{ENV["CC_BUILD_ARTIFACTS"]}/scratch/saikuro", 
  :input_directory => ["app\" --input_directory \"lib"],
  :cyclo => "",
  :filter_cyclo => "0",
  :warn_cyclo => "5",
  :error_cyclo => "7",
  :formater => "text", #this needs to be set to "text"
}
config.flay = {
  :dirs_to_flay => ['app', 'lib',],
  :minimum_score => 10, 
}


My changes in bold.

There are a number of different ways to handle custom error pages in rails. Most use rails’s rescue_from method. This approach allows your error pages to be dynamically rendered in response to errors.

The way I see it, if my site has just encountered an error, I want to immediately go into damage control mode. Do I want to risk dynamically generating an error page? No, not at all. There could be an error in the page rendering that will cause another error when I try to render the error page. Not an ideal situation to say the least. It’s much safer to serve up previously generated static content.

While rails does come with a default, static, error page, said page is largely unstyled, and contains no links to help the user get back to what they were doing. I feel that it’s bad enough that I have to display an error to the user; leaving them with nowhere to go, looking at a jarring, or even ugly, error page isn’t going to make them the happy users that we all want.

Lastly, I don’t want to have to remember to keep the site’s error pages up to date with the latest layout and style.

To summarize, I want static error pages, rendered using rails’s ActionView, and I want them updated automatically any time the site’s style or layout changes.

To accomplish these goals I wrote a rake task and a capistrano recipe. The rake task visits the site, collects the rendered error pages, and stores them in the <RAILS_ROOT>/public directory, where rails will automatically look for them when errors occur. Here’s the rake task:

desc "Generate static error pages (404 and 500)"
task :generate_static_error_pages => [:environment] do
  require "console_app"

  urls_and_paths.each do |url, path|
    r = app.get(url)
    if 200 == r
      File.open(Rails.public_path + path, "w") do |f|
        f.write(app.instance_variable_get(:@body))
      end
    else
      $stderr.puts "Error generating static file #{path} #{r.inspect}"
    end
  end
end

def urls_and_paths
  [["/static/404_not_found", "/404.html"],
   ["/static/500_internal_server_error", "/500.html"],]
end

The capistrano recipe simply runs the rake task each time the application is deployed. Here’s the recipe:

desc "Generate static error pages"
task :generate_static_error_pages, :except => {:no_release => true} do
  run "cd #{current_path} ;
       RAILS_ENV=production rake generate_static_error_pages"
end


I’ve instructed capistrano to execute this recipe each time I deploy my application by using one of capistrano’s many handy callbacks:

after "deploy:symlink", "generate_static_error_pages"

The result is that I have consistently styled, static error pages, that are updated automatically each time I deploy my application.

Voyeur is a very small bit of ruby code that I’ve written to display updates to your MPD stream via Growl.

You can find it here:  http://kill-0.com/voyeur/

You start and stop it as you would a daemon, ie $ ./voyeur start and ./voyeur stop. As an added bonus, you can send it a HUP signal, and it will immediately display the current track information or status. Here’s a screen shot:

Voyeur: Screenshot

I was having all sorts of problems upgrading Mildred to Rails 2.1.  A lot of the errors I was seeing were like the following:

ArgumentError in 'TracksController downloading a track by admin should not be a redirection'
wrong number of arguments (0 for 1)
/Users/ewollesen/src/mildred/app/models/track.rb:52:in `title'
/Users/ewollesen/src/mildred/app/models/track.rb:52:in `filename'
/Users/ewollesen/src/mildred/app/controllers/tracks_controller.rb:49:in `download'
./spec/controllers/tracks_controller_spec.rb:82:

It took me a good while to figure out what was going on. The error was strange because as far as I knew, title was a database attribute in the Album model, which was a descendant of ActiveRecord. There shouldn’t have been any arguments required. Indeed, firing up my debugger and running track.album.read_attribute(:title) returned the expected result of “Test Album 1″. I was very puzzled.

I realized that my title method was being overridden, but by what? I started feeding the title method random arguments in the hopes of learning something new. It wasn’t long before I got lucky:

(rdb:1) e track.album.title("x")
NoMethodError Exception: undefined method `content_tag' for #<Album:0x3d5bbfc>

That tipped me off as to what was overriding my title method. The content_for is part of a pattern I use to set a view’s title in the layout via a method called title in my ApplicationHelper. This made me think of how in Rspec 1.1.4, a Helper module is no longer included by default. I popped over to my application_helper_spec.rb and found something similar to this:

require File.dirname(__FILE__) + '/../spec_helper'

include ApplicationHelper

describe "ApplicationHelper" do

This needed to be changed to:

require File.dirname(__FILE__) + '/../spec_helper'

describe "ApplicationHelper" do

  include ApplicationHelper

Then all was well. Whew. Just to be sure I didn’t run into this sort of thing again, I went ahead and made sure that all of my other Helper specs followed this paradigm.

This is the second time I’ve run into an issue where RSpec had overridden some seemingly random method in some seemingly random object. I guess there’s a lot of magic in there that I don’t have my head wrapped around yet. I just wish it were easier to spot!

Rails’s JavaScript generator provides some great functionality. But unfortunately, one of the things it can’t do for you, is generate JavaScript conditionally, based on the page’s content. The reason is simply that the page’s content doesn’t exist at the time the conditional in your code was executed. I wanted to do something like the following:

if page["album_1_rating"]
  page["album_1_rating"].visual_effect(:highlight)
end

As I said, at the time the if statement above is executed, there is no HTML file yet, so the generator can’t possibly know if the element referenced will exist in the file or not. In fact, page["album_1_rating"] is a ActionView::Helpers::JavaScriptElementProxy object, which is not false, so the if statement will always execute. This wasn’t what I wanted. So I came up with something that would work. I added the following method to lib/prototype_helper_hacks.rb, then required it in config/environment.rb.

def if_element(id, &block)
  self << "if ($(\"#{id}\")) {"
  block.call(self[id])
  self << "}"
end

This lets me do things like this in my views:

page.if_element("album_1_rating") do |element|
  element.visual_effect(:highlight)
end

That made me happy.

In thinking more about the situation, I’m not sure this is really such a great method. It works, but I can’t help but feel there’s some better way of going about this. I could simply use the page append method, to do something like:

page << "if ($(id)) {"
  page["album_1_rating"].visual_effect(:highlight)
page << "}"

But at least if nothing else, my if_element method makes this a lot cleaner looking. So really this is just some syntactic sugar, and I wish at least I could make it more of a general purpose if statement, but since I don’t need that sort of funcationality at this point, YAGNI tells me to leave that hurdle for another day.

The short of it, as you can find with a quick Google search1, is that << is superior to + (or +=) in Ruby. This is because << appends to an existing object, while += creates a new object. Creating a new object in Ruby is not nearly as fast as appending to an existing object. This should not be surprising to anyone that has written the two operations in a lower level language like C or C++. Now, on with the story.

At work, I’m writing a parser for less-than-well-known file format. In the lexical analysis stage, I was adding arrays together. Something along the lines of:

@tokens += token

Where @tokens is an array, and token is an array of the form [:symbol, character]2. At any rate, when I tried to parse a very large file, it took approximately 180 seconds. My smaller tests had all been very fast. Looking at the code, the += was the first thing that came to mind. I checked it with the benchmarker and found that a particular loop full of +=’s was particularly offensive. A quick refactor from += to << later, and parsing the same file now took just under 9 seconds. That’s down from 180 seconds. Nice.

So what have I learned here? Well, I’m not sorry that I used +=. It worked just fine, and anyone that knows me knows I recite “premature optimization is the devil” at least 10 times a day. Now that I’m in the final test phases of the parser, I can take the time to optimize things like this. However this also reinforced the adage of “everything is fast when n is small.” A good reminder to test with larger datasets before claiming any algorithm is complete.

  1. Like this one. []
  2. If you’re paying attention you’ll recognize this format as that used by racc. []

No, I’m not going to say that using MySQL with Ruby on Rails is a bad thing. It works fine. But I do think that using the mysql command line tool to modify your Rails databases is a no-no. The reason is simple, Rails does a lot of work to abstract database details away, so that programmers don’t have to know anything about them. We should respect and embrace that abstraction, not step around it.

So if you’re not supposed to use mysql from the command line, how do you make changes to your models from outside of your application? The answer is script/console. Using console is even better than using mysql, because it a) uses Ruby, a language we already know and love, and b) exercises the same bug-free, well tested code that your Rails application does. Of special use here are ActiveRecord’s validations, associations, and before_* and after_* callbacks.

As a brief example, let’s say you’re writing the ubiquitous blog. You need to delete a user on the blog, but you haven’t implemeted the web framework yet. You open up your trusty mysql command line client and do something like this:

mysql> DELETE FROM users WHERE username = 'foobar';
Query OK, 1 row affected (0.08 sec)

And you think to yourself, “Job well done” as you wipe your hands and pat yourself on the back. But many of you are probably asking, “What happened to all of foobar’s posts?” They’re still there. I guess you have to delete them too:

mysql> DELETE FROM posts WHERE user_id = 666;
Query OK, 1 row affected (0.11 sec)

It’s a good thing you happened to remember foobar’s user_id huh? So that should do it… right? But what about foobar’s user image? Now you have to go into the filesystem and delete his user image.

$ rm RAILS_ROOT/public/images/users/foobar.jpg

Whew. There, that’s all of it. Finally. Or is it? What if your colleague added the concept of “friendships” between users, and now you have a bunch of orphaned table rows? If this doesn’t seem error prone (and tedious!) already, then you’re a lot braver than I am. Especially since if you hadn’t tried so hard to work around Rails’s database abstraction, all you would have had to do is:

>> User.find_by_username("foobar").destroy

That’s it. One simple line. Your well-designed models will take care of the rest. In case you’re new to Rails, and wondering just what this magical User model might look like in Rails, here it is:

class User < ActiveRecord::Base
  after_destroy :delete_user_image
  has_many :posts, :dependent => :destroy

  private

  def delete_user_image
    FileUtils.rm(RAILS_ROOT + "/public/images/users/#{username}.jpg")
  end
end

Yes, if you’re a true wiz, you could write all of that into your database, but you’re trying to write a Ruby on Rails application, remember? Besides, what happens when your CTO decides that your application will now use DatabaseX, since it’s the hottest thing since the sun? If that frightening occurance isn’t enough, consider that DHH, the creator of Rails, frowns on such database trickery.

I guess the real point of what I’m trying to say is, Rails uses the database so you don’t have to. Try to pretend there is no database, and use script/console instead. You’ll save yourself a lot of grief in the end.

How QBeat Works

No comments

QBeat is one piece of the Mildred project. Specifically, QBeat handles the selection of tracks to play. There are a number of subsytems within QBeat that allow tracks to be selected and played. I’ll start with a list of all the pertinent subsystems, then I’ll describe the main responsibilities of each subsystem.

Subsystems

  • QBeat
  • MPD
  • MpdMonitor
  • Selector
    • WeightedSelector
      • Stats
      • Weight
  • Schedule
  • TimeSlot
  • ProgrammingBlock
  • Fetcher

QBeat

QBeat is the glue that holds all the other pieces together. It gets things rolling, daemonizes the process, opens log files, handles signals, etc. Once the housekeeping is done, QBeat enters a loop, passing control to the various subsystems as appropriate.

MPD

MPD, the Music Player Daemon, is a wholly separate program, one that QBeat uses to handle the nitty gritty of playing audio files. QBeat simply tells MPD which files to play, and MPD takes care of opening the files, and sending them to the appropriate audio devices.

MpdMonitor

The MpdMonitor monitors the MPD playlist (imagine that!) It checks the MPD playlist’s length, and indicates to the Selector if a track should be added to the playlist. When additional tracks are to be queued, the Selector picks the tracks, and MpdMonitor feeds this information to MPD. MpdMonitor also monitors the currently playing track, and reports new tracks as they are played. Lastly, MpdMonitor trims the MPD playlist whenever it grows too long.

Selector

Selector is an abstract class, meaning it is meant to be extended, not used as is. It provides the basic interface expected of a Selector. A Selector’s job is to actually pick the next track (or tracks) to queue.

The Selector class also provides a number of utility functions to derivative Selectors. These functions help with such tasks as loading tracks from ProgrammingBlocks, filtering tracks by user-specified criteria (such as minimum time, or minimum rating), and determining (with help from the ProgrammingBlock) if a single track, whole album, or power block should be queued.

QBeat was designed to allow Selectors to be changed easily. This allows for the easy development of new Selectors, with totally different selection behavior. For example, in addition to WeightedSelector, I have a few test selectors which do such things as selecting tracks 100% at random, or selecting tracks in alphabetical order by title.

WeightedSelector

WeightedSelector is a derivative Selector, and the one generally in use by QBeat. It evaluates each track, and assigns it a weight. Weights are based on when the track, its album, and its artist were last queued, as well as their ratings. Last queued and rating data are normalized, and their sum becomes the track’s weight. Special modifications are made to reduce the effect of high ratings, or to weight new tracks more heavily. The final track to be selected is picked at random from the top weighted tracks.

WeightedSelector is where most of QBeat’s time is spent. It is also where I spend the most of my time tweaking, debugging, and dreaming.

Stats

This is a helper class that maintains minimum, maximum, and range stats for the last queued and rating data of tracks. These stats are very useful in calculating normalized values.

Weight

This is a helper class that allows me to easily calculate, compare and debug the weights of different tracks, artists, or albums.

Schedule

The schedule class determines which ProgrammingBlock should be fed to the Selector. It also provides the Mildred website with support in generating the Schedule page, which displays the Schedule for a given date.

The Schedule loads itself from a YAML file. The file describes which ProgrammingBlocks should be played at what times. Any time a specific ProgrammingBlock is not specified, a default random block is used. ProgrammingBlocks can be scheduled by start and end times, days of the week, days of the month, and months of the year.

TimeSlot

The TimeSlot helps the Schedule determine which ProgrammingBlock to choose by allowing the current time to be compared to each of the times specified in the Schedule. For example, the Schedule will load all of the ProgrammingBlocks. It will then filter out those that aren’t eligible to be scheduled today. Then, with TimeSlot’s help, it orders the remaining ProgrammingBlocks by start and end times. Then it can iterate through the ProgrammingBlocks, and determine which one (if any) should be currently active.

ProgrammingBlock

The ProgrammingBlock determines which artists, albums, or moods should be played. Like Schedule, it is read from a YAML file. ProgrammingBlocks also specify the probability of queueing a single track, an entire album, or a power block. Many ProgrammingBlocks exist, but only one is active at a time, as determined by the Schedule.

Fetcher

The Fetcher is a simple utility class with one purpose, to load the actual Track objects specified in the ProgrammingBlock. This means taking the artists, albums, and moods specified in the ProgrammingBlock, and returning a list of Tracks.

The QBeat Loop

The actual loop looks something like this:

  1. MpdMonitor checks to see if the currently playing track has changed, reporting the new track if it has
  2. MpdMonitor checks the size of the MPD playlist
    1. Selector picks a new track if the MPD playlist is too short
    2. MpdMonitor adds the tracks specified by Selector
  3. MpdMonitor checks the size of the MPD playlist, and trims it if it is too long
  4. QBeat sleeps for some amount of time (currently 10 seconds)