Rails Plugin: acts_as_fu

March 31st, 2008

We’ve all seen the job listings. We know what they’re looking for. Rockstars. Code Monkeys. Rails Ninjas.

And you’re left thinking, “But I’m just a programmer.” Well, not anymore. Today, you become a Rails Kung-Fu Master!

“Not me,” you say. “I’ve already tried using all the acts_as_something plugins and all the something_fu plugins; and I’m still not a Rockstar or a Ninja. I’m barely a Code Simian!”

Sounds like you’ve tried the rest – now try the best! That’s right: it’s the new and improved ActsAsFu Rails plugin!

Three Easy Steps to Becoming a Rails Kung-Fu Master:

1. Stop web server

mongrel_rails stop

2. Install Plugin

./script/plugin install http://svn.offtheline.net/plugins/acts_as_fu

3. Start web server

mongrel_rails start

(note: if you’re using mongrel_cluster, thin, piston, or anything like that, these instructions don’t apply to you – you figure it out yourself, you Rockstar Ninja!)

Now you’re on your way to programming stardom! Catch ya on fame’s backside, my friend!

Bonus Round: If you work on a team, don’t forget to check this awesome plugin into your repository (or as the ninjas say, svn ci -m ‘added l33t skillz’). Your teammates will undoubtedly be very impressed by your jujitsu karate haxxing abilities. If you push it up to your production server, your boss might even give you a raise!

MoveableMap is a javascript library that allows you to turn any block of HTML into a draggable, moveable entity within a viewport. Similar to the behavior of Google Maps, but hopefully very easy to implement. On the right here, you can see a real simple example; it’s just a div block with an img element in the middle of it.

How simple is the code for this example? The moveableMap library is built on top of the fantastic prototype.js library, the slick scriptaculous library, and the enlightening Low Pro library. It takes just a little CSS magic and a little unobtrusive javascript to get the behavior you see here.

We need two elements: the “container” block, and the block inside the viewport. In this example, they’ll both be divs.

Our CSS sets us up:

1
2
3
4
5
6
7
8
9
10
11
12
#map_holder {    /* the 'container' */
  width: 256px; height: 256px;            /* size of the viewable area */
  overflow: hidden; position:relative;    /* necessary to make the whole thing work */
  padding:0px; border: 3px dotted #990;   /* aesthetics */
}

#the_map {   /* the inner block */
  width: 512px; height: 512px;   /* defining the size keeps us from getting squashed */
  left:-128px; top:-128px;       /* the starting offset - always negative */
  position:relative;             /* better believe it, you need it */
  border:3px solid #550;         /* so we can see the edge of the map */
}

A little javascript to create the MoveableMap object and apply some behaviors:

1
2
3
4
5
6
7
8
9
10
11
12
// we don't attempt anything until the DOM is loaded
Event.observe(window, 'load', function() {
  // create a new MoveableMap object and pass in the two elements we need
  // I'm using the prototype shortcut $('my_html_id') to represent an element:
  mainMap = new MoveableMap($('the_map'), $('map_holder'));
  // here's the Low Pro unobtrusive helper:
  Event.addBehavior({
    // specify the CSS selector for the inner block and 
    // apply the MapDrag behavior to it (specifying the MoveableMap object)
    '#the_map' : MapDrag(mainMap)
  });
});

And the HTML, which is pretty trim:

1
2
3
4
5
<div id="map_holder">
  <div id="the_map" style="background: url(/examples/map/images/fakemap.jpg)">
     <!- just empty to give us a div with a image background -->
  </div>
</div>

Notice the lack of onclick, onmousedown, onmouseup in the HTML? That’s the unobtrusive bit, baby. All that stuff is handled by the MoveableMap library (as Behaviors a la Low Pro).

Let’s see some more impressive examples. Here’s something similar to what’s on the page here, but with a compass in the corner that you can click on to slide the map in that direction. Double-click to slide all the way to the edge. Take note of the fake HTML attribute called ‘mapdir’ inside each compass direction.
Check it: Old World Map with Compass

That was fun. What else you got? Here’s a (crudely drawn) map of Aethora. The ‘compass’ from the previous example has been placed around the edges of the map to give the viewer a feeling of clicking on the sides of the map to make it scroll. Additionally, you can see a list of cities below the map – click on any city to make the map slide to that location. In reality, the ‘cities’ are positioned div elements. The clickable element has a fake HTML attribute, targetelement, which names the id of the element you want to center the map on.
Peep: Fantasy map with clickable city names

This last map is more of the same really, but the big deal is that the inner block of code is a table. It’s basically here to prove to you that you can put some pretty complex stuff on the inside (not just a jpg of a street map) and still move the map around. This example is closer to the actual purpose I created this library for (Aethora, my RPG written in Ruby on Rails and javascript).
Observe: Tiled (table-based) ‘battle field’

Here’s the MoveableMap js file. You’ll need to load prototype, the scriptaculous effects.js library, Low Pro, MoveableMap, and then your own javascript in that order. Check out the source code of those examples above for more details. The code seems to work well in Firefox, IE6, and IE7. In Opera, it seems to do a slight, 3-4 pixel detour before getting on track when using the compass movement. If anyone can tell me how to fix it, I’ll buy you a beer/coffee (I had to sell my soul just to get it to work in IE). I haven’t had a chance to test it in Safari or anything else, but I’d love to hear how it works out.

So enjoy – and let me know if you find any bugs. And definitely let me know if you find it useful!

Update: Here’s a great example, this one provided Kahil, who is the developer of a browser-based game called WMD Tank Battle: Vertical Scrolling a la old school arcade shooters

Parallax Animation with CSS/JS

February 27th, 2008

I just read an article on ThinkVitamin about creating some nifty parallax scrolling style animation by using multiple layers of images that shrink at various proportions when you resize the browser window. A few of the comments noted that it’s unfortunate that the animation is only apparent when resizing the window, and something that would go fairly unnoticed by most people.

Being a lover of all video games, old and new, I thought this trick that I’ve seen in so many side scrollers was brilliant. My first thought was how to combine this with Script.aculo.us’s Morph effect so that you can see the animation without actually resizing the browser window. Turns out it wasn’t that hard – check it out!

Apologies for: (a) stealing the images from Paul’s example on the ThinkVitamin site for demonstration purposes and (b) for using onclick (I should be practicing unobtrusive javascript, but this is a really simple example that I wanted to crank out).

Pretty slick. Now I’m determined to find a need to use this trick in Aethora....

Has anyone noticed that the newer rake (as of 8.0) truncates task descriptions a lot shorter than the old rake (7.3)? Here’s what I mean:

jason@old-box:~/dev/some_app$ rake --version
rake, version 0.7.3
jason@old-box:~/dev/some_app$ rake -T rails
(in /home/jason/dev/some_app)
rake doc:clobber_rails         # Remove rdoc products
rake doc:rails                 # Build the rails HTML Files
rake doc:rerails               # Force a rebuild of the RDOC files
rake rails:freeze:edge         # Lock to latest Edge Rails or a specific revision with REVISION=X (ex: REVISION=4021) or a tag with TAG=Y (ex: TAG=rel_1-1-0)
(etc...)
jason@new-box:~/dev/some_app$ rake --version
rake, version 0.8.1
jason@new-box:~/dev/some_app$ rake -T rails
(in /home/jason/dev/some_app)
rake doc:clobber_rails         # Remove rdoc products
rake doc:rails                 # Build the rails HTML Files
rake doc:rerails               # Force a rebuild of the RDOC files
rake rails:freeze:edge         # Lock to latest Edge Rails or a specific re...
(etc...)

If you dig into the rake 8.0/8.1 gems, you might notice that the task list output is being truncated to 80 columns. This change was the result of a suggestion to make the task list more condensed, while at the same time adding a -D option for full descriptions (Rake 0.8.0 Released [rubyforge.org]):

jason@jason-4200:~/dev/phoenixmedia$ rake -D rails
(in /home/jason/dev/phoenixmedia)
rake doc:clobber_rails
    Remove rdoc products

rake doc:rails
    Build the rails HTML Files

rake doc:rerails
    Force a rebuild of the RDOC files

rake rails:freeze:edge
    Lock to latest Edge Rails or a specific revision with REVISION=X (ex: REVISION=4021) or a tag with TAG=Y (ex: TAG=rel_1-1-0)

(etc...)

...but as you can see, the full-description version puts a bunch of line breaks in the output.

I liked it better the old way! I don’t doubt the change was made for the best (line-wrapped descriptions in a small window are not very nice to look at), but I’d really like the option to set my own column width. For now, I’ve resorted to dropping this rake task into my Rails apps (you can save this as something.rake under my_rails_app_root/lib/tasks):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NUMBER_OF_COLUMNS_TO_DISPLAY = 180

task :t, :p do |t, args|
  pattern = args.p || '.'
  displayable_tasks = Rake.application.tasks.select { |t|
    t.comment && t.name =~ /#{pattern}/
  }

  width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
  cols = ENV['RAKE_DESC_COLS'] ? ENV['RAKE_DESC_COLS'].to_i : NUMBER_OF_COLUMNS_TO_DISPLAY
  max_column = cols - Rake.application.name.size - width - 7

  displayable_tasks.each do |t|
    printf "#{Rake.application.name} %-#{width}s  # %s\n",
      t.name_with_args, truncate(t.comment, max_column)
  end
end

def truncate(string, width)
  return '' if string.nil?
  if string.length <= width
    string
  else
    string[0, width-3] + "..."
  end
end

Note that this uses the new rake 8.x feature of allowing for command-line arguments to get passed into a task, so we can pass in a ‘pattern’ to search on without using an environment variable. Now I can do this:

jason@new-box:~/dev/some_app$ rake t[rails]
(in /home/jason/dev/some_app)
rake doc:clobber_rails         # Remove rdoc products
rake doc:rails                 # Build the rails HTML Files
rake doc:rerails               # Force a rebuild of the RDOC files
rake rails:freeze:edge         # Lock to latest Edge Rails or a specific revision with REVISION=X (ex: REVISION=4021) or a tag with TAG=Y (ex: TAG=rel_1-1-0)
(etc...)

I set the default number of columns at 180 because that’s just right for a maximized terminal window with the Monospace font (default in Ubuntu) at 9pt on my 1280×1024 display. Of course, I left an option to easily change the number of columns via an environment variable. For example, on my EeePC, 110 is about right:

jason@eeePC:~/dev/some_app$ export RAKE_DESC_COLS=110
jason@eeePC:~/dev/some_app$ rake t[rails] 
(in /home/jason/dev/some_app)
(in /home/jason/dev/phoenixmedia)
rake doc:clobber_rails         # Remove rdoc products
rake doc:rails                 # Build the rails HTML Files
rake doc:rerails               # Force a rebuild of the RDOC files
rake rails:freeze:edge         # Lock to latest Edge Rails or a specific revision with REVISION=X (ex: RE...
(etc...)

Hey, I can actually read the descriptions again!

This week I finally broke down and decided to find out what this Unobtrusive Javascript thing is all about. First, I got really excited about trying out the UJS plugin for Rails. I’m not linking to it, because after a couple of hours of digging around, Googling for the errors I was getting and finally digging into the source to find out it was breaking the tag_options helper in Rails 2, I discovered it’s no longer a supported plugin. Dan Webb, the original author, came to the conclusion that the unobtrusive message was not getting through to the typical Rails developer who picked up the plugin for it’s shortcuts. He pulled the lowpro.js library out of the plugin and ditched the Rails bits. I think it was a good move (I just wish someone would update ujs4rails.com to say something to that effect – it’s still the same page it was over a year ago, and it really sells the plugin well…)

There’s not really a lot of documentation for Low Pro out there, but to be honest, once you start using it I think you find there really isn’t a lot to it. Built on prototype, the lowpro.js file is only 300 or so lines of code, including some helpful comments. There are some links on the lowpro wiki to a couple blog posts with examples. In particular I found Jarkko Laine’s article very helpful and of course Dan’s blog is worth a read. Unfortunately, due to the speed of technology, some of Dan’s examples are a little dated (thanks to improvements he’s made to the code).

So I figure, how’s about another example? One key concept behind unobtrusive javascripting is yanking those obnoxious mouse events out of your anchor tags; Onmouseover, onmouseout, etc – they’ve got to go. So how can we use Low Pro to add a rollover behavior to some images?

note: at the time of this writing, Low Pro is version 0.5 and requires prototype version 1.6.x

Step 1: Plain HTML

Let’s start with the HTML. Imagine a menu that’s just a column of images; no javascript yet, just plain ol images (and of course styled with CSS in another file):

1
2
3
4
5
<ul class="main_menu">
  <li><a href="/pages/home"><img src="home.gif" alt="Home"/></a></li>
  <li><a href="/pages/about"><img src="about.gif" alt="About"/></a></li>
  <li><a href="/pages/contact"><img src="contact.gif" alt="Contact"/></a></li>
</ul>

Very simple. Probably everyone knows the mechanics of a rollover by now: what we want is for the img src attribute to change to a different value when the mouse cursor is placed over the image. The effect is getting a different image to display in place of the current image. And of course, when the cursor leaves the area of the image, we want the src to switch back. So how do we do that without using our tried and true “onmouseover/onmouseout” HTML attributes?

Step 2: Create a Behavior

We want to define a Behavior to describe the effect certain mouse events have on a particular image object. Low Pro makes this nice and clean, using the Class module baked into prototype to give us an object-oriented feel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var ImageRollover = Behavior.create({
  // define some attributes for this new Behavior (mmmm, OOP...)
  orig_name: null,    // the filename without extension
  image_ext: null,    // jpg, gif, etc, plus any ?123 nonsense at the end
  roll_suffix: null,  // whatever we want to stick on the end of the image name

  // one of these instances of ImageRollover will be created for each image 
  // when we set them up, we'll need to know the "suffix" of the rolled over image
  // for example, home.gif might be our image and home_on.gif might be our 
  // rollover, making '_on' our suffix
  initialize: function(roll_suffix) {
    this.roll_suffix = roll_suffix

    // here comes some lovely regex
    // image.src example: http://example.com/images/something.gif?1234567890
    // we need to chop that up into: 
    // 'http://example.com/images/something' and '.gif?1234567890' 
    matches = this.element.src.match(/(.*)((?:.gif|.jpg|.png).*)/);
    this.orig_name = matches[1]; 
    this.image_ext = matches[2];
  },

  // this function will get called on mouseover
  onmouseover: function() {
    // we're just stapling the pieces back together with the suffix between 
    // the file name and the file extension 
    this.element.src = this.orig_name + this.roll_suffix + this.image_ext
  },
  
  onmouseout: function() {
    // back to the original, no suffix
    this.element.src = this.orig_name + this.image_ext
  }
});

So there’s our behavior. It’s nice and generic – we could apply it to any image element. I’m not great with regex, but this will work for any image that ends with .gif, .jpg, or .png and optionally if it has an asset timestamp at the end following a question mark (Ruby on Rails likes to stick these on the end of assets to trick browsers into reloading cached image, css, and js files that might have changed when code has been updated). I recently came across this great testing tool for regex expressions: http://www.rubular.com/. Of course, the Firebug console is also great for this type of thing as well. For some reason my regular expression yields a third, unnecessary match (just the file extension with no timestamp) and I can’t quite nail it down. It doesn’t affect anything, but hey – let me know if you have a suggestion. Edit: Andy (in the comments) points out where the extra match comes from and how to ignore it. I updated the regex and it works.

Step 3: Assign Behavior

Finally, we can assign our ImageRollover behavior to some images. The magic of CSS selectors makes it easy to assign a behavior to a whole group.

1
2
3
Event.addBehavior({
  '.main_menu img': ImageRollover('_on')
});

The addBehavior method takes a hash where every key is a CSS selector and the value is a behavior. In this example, we can see that the CSS selector is saying “look for elements of the main_menu class and collect any of their children that have the img tag”. We also have to pass a value to ImageRollover that designates the rollover image suffix needed by the initialize method of the ImageRollover class.

Want proof that it works? See it in action (warning: extremely bad graphics that I cranked out in 5 minutes).

So there you have it. If anyone who knows Low Pro has any feedback on improving this example, I’d love to hear it.

receiving email with Rails

February 13th, 2008

Craig Ambrose writes about how he came up with a way to receive mail RESTfully in Rails. Basically, he makes the point that the method for processing email described in the Rails wiki is a little scary, as it can lead to the loading of the full Rails stack on every email you want to process. Craig speculates that this method could be problematic in terms of memory usage, and I tend to agree with him. He comes up with a pretty clever hack to basically redirect each incoming email at his actual running web application by grabbing it from postfix and posting it to his site via the use of Ruby’s net/http library. After that, it’s up to the Rails controller to handle the request.

While I like the cleverness of the solution, I can’t help but feel like it’s somehow asking for trouble. As some comments on the post point out, there doesn’t seem to be an elegant way to handle the mail if the web server happens to be down. Another concern is hammering the web server with too many requests in the case of multiple emails or text messages coming in, as each incoming email takes a up an http connection and occupies a mongrel server (or whatever) for however long it is needed (it’s no worse than submitting form data, but I think the concern is that there is potential for more volume).

I’ve been thinking about a way to handle incoming mail myself, and here’s the solution I’ve come up with. I have not had time to implement it, and maybe it’s been done before (please let me know if it has and has been documented).

  1. Postfix accepts incoming mail and stores the mail as a file somewhere on the server. A simple procmail recipe would probably be useful here to identify where to store incoming mail depending on it’s address, contents, day of the week, or whatever.
  2. A backgroundrb worker hangs around and processes the mail files on a periodic basis, by reading files in a specified directory one at a time, parsing each message with TMail, and doing any other Rails stuff you need it to do with each message. Remove each file after the message has been processed.

Backgroundrb has a great built-in scheduling system, so there’s no need to think cron in this setup. Backgroundrb will load the whole rails stack once. The workers sit in a separate process. Backgroundrb won’t run itself over: if you set the schedule to process mail every 2 minutes and the mail takes more than 2 minutes to process, backgroundrb will queue it’s jobs up. In this scenario, a worker is just looking in one directory, so once all the mail is processed, it gets deleted. If no new mail comes in by the next time the worker looks at the directory, it can see there’s nothing to do and go back to sleep. Knowing the expected volume of email coming in is important, but I’m willing to bet in most cases you could ask backgroundrb to run your mail-processing worker once a minute.

This method doesn’t get to claim the label RESTful, but to be honest I don’t think regurgitating incoming mail out to your web server is really in the spirit of “REST” either. If you stay true to the skinny controller, fat model mantra, handling email via model methods (all accessible via backgroundrb) or even module/other non-model class methods in a backgroundrb worker should be as easy as (if not easier than) doing so from within a controller.

NetBeans 6.0 Cheat Sheet

December 11th, 2007

I stitched together a cheat sheet for NetBeans 6.0, boiling it down to what I find are the most useful shortcuts for Ruby and Rails stuff.

NetBeans 6.0 Cheat Sheets:
PC (Linux/Windows/etc) [PDF] [ODT]
Mac (OS X) [PDF] [ODT]

I’ve been using NB6 for a couple of weeks and I’m starting to feel good about it. A couple of tweaks after the initial install (of the Ruby flavor of NB6) that I have to make before I’m really happy:

  • Go to Tools, Plugins, and install Extra Ruby Color Themes, Ruby Extra Hints, and Ruby RSpec Support
  • Go to Options, Fonts & Colors, change “Profile” to “Ruby Dark Pastels” (this is the TextMate-like color scheme, I guess)
  • A couple of things bug me about the color scheme – most notably, under Syntax Highlighting, Ruby, I have to change Mark Occurrences to the Inherited background (otherwise, whenever your cursor is in a variable, it and all it’s occurrences will light up on the page, which I find very distracting). I also like to change the color of the highlighted row under the Highlighting tab on that page (the default background is too bright for me).
  • AutoComplete drives me crazy. I’ve always hated it – I feel like it slows my computer down and sometimes it even seems to derail me in the middle of typing something because the window comes up and somehow snags focus. I turn it off under Options, Editor, General (uncheck Auto Popup Completion Window).

Most of the TextMate snippets have been imported into NetBeans 6. I didn’t squeeze all of these into the cheat sheet because there are so many, and personally, I don’t use ‘em (there’s probably some irony in the fact that I’m too lazy to learn a shortcut). You can peep the list of default ruby snippets and rhtml snippets or just go search for a TextMate cheat sheet.

click thumbnail for larger image

For a no-cost, cross-platform IDE, NetBeans 6 may be hard to beat. I still feel like there are some minor stability issues, but for the most part it hums right along on my Ubuntu desktop and laptop (both Gutsy-AMD64 installs) and even works fairly well on my ASUS EeePC (running the pre-installed custom build of Xandros).

really breaking up an array

December 6th, 2007

Last time I tried to break up an array, I learned about enumerable’s partition method. This time, I’m taking it one step further and returning multiple arrays rather than just two:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Array
  def group_by(attribute = nil)
    arrays = {}
    for i in 0...size
      object = self[i]
      value = attribute ? object.send(attribute.to_sym) : yield(object)
      if arrays[value.to_s]
        arrays[value.to_s] << object
      else
        arrays[value.to_s] = [object]
      end
    end
    return arrays.values
  end
end

And, the examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>> # you can group by attribute name
>> my_array = ["cats", "dogs", "people", "things", "elephants", "computers", "cars", "1001"]
=> ["cats", "dogs", "people", "things", "elephants", "computers", "cars", "1001"]
>> my_array.group_by "size"
=> [["people", "things"], ["elephants", "computers"], ["cats", "dogs", "cars", "1001"]]

>> # also can take a block - equal evaluation of the block determines grouping
>> my_array.group_by { |a| a[0] }
=> [["things"], ["cats", "computers", "cars"], ["1001"], ["dogs"], ["elephants"], ["people"]]

>> [4, "foo", 500, "bar", 4.5].group_by { |a| a.is_a? Integer }
=> [[4, 500], ["foo", "bar", 4.5]]

>> [4, "foo", 500, "bar", 4.5].group_by { |a| a.to_s.size }
=> [[4], ["foo", 500, "bar", 4.5]]

Again, I find this method useful for grouping ActiveRecord sets based on different attributes. It's especially handy when constructing views where records need to be grouped under different headings or in different tables.

breaking up an array

October 13th, 2007

There’s probably a better way to name this, but whattayagonna do?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Array
  # split an array based on block
  def divorce
    mom = []
    dad = []
    for i in 0...size
      value = self[i]
      if yield(value)
        mom << value
      else
        dad << value
      end
    end
    return [mom, dad]
  end
end

Examples:

1
2
3
4
5
6
7
8
9
10
>> strings, non_strings = [5,6,"aaa", 7, "ccc"].divorce { |element| element.is_a? String }
=> [["aaa", "ccc"], [5, 6, 7]]

>> longs, shorts = ["Hello.", "Hi, how are you today?", "Great!"].divorce { |st| st.size > 10 }
=> [["Hi, how are you today?"], ["Hello.", "Great!"]]

>> mults_of_3, non_mults_of_3 = (1..10).to_a.divorce { |n| n % 3 == 0 }
=> [[3, 6, 9], [1, 2, 4, 5, 7, 8, 10]]
>> mults_of_2, non_mults_of_2_or_3 = non_mults_of_3.divorce { |n| n % 2 == 0 }
=> [[2, 4, 8, 10], [1, 5, 7]]

I use it mostly for breaking up collections of ActiveRecord objects, where I want to process them differently depending on some attribute, but don't want to make multiple database/find calls or select/detect calls.

Ruby for Batching

September 16th, 2007

The other day I wanted to resize a bunch of images using ImageMagick’s convert command-line utility. I wanted to both resize and change their names from image_name.png to image_name-sm.png, but I couldn’t figure out how to guide ‘convert’ to use part of the original name in the new name. The next obvious tactic would be to use some common shell utilities like ‘for’ and ‘do’. My shell-fu is rusty however, and I still couldn’t get things quite the way I wanted them. Then I think to myself, this would be much easier to do with Ruby.

First, I use ‘puts’ to see if the syntax will come out right:

1
2
3
4
5
6
irb(main):017:0> Dir['*.png'].each { |f| puts "convert #{f} -resize 400x400 #{f[0..-5]}-sm.png" }
convert character_creation.png -resize 400x400 character_creation-sm.png
convert battle-mountain_pass.png -resize 400x400 battle-mountain_pass-sm.png
convert loot-drag_and_drop.png -resize 400x400 loot-drag_and_drop-sm.png
convert market.png -resize 400x400 market-sm.png
...(etc)...

Then I just run the same line with system instead of puts:


Dir['*.png'].each { |f| system "convert #{f} -resize 400x400 #{f[0..-5]}-sm.png" }

With a little practice, this can be done on the command line - who needs shell scripting?

1
2
# run from the shell prompt:
ruby -e 'Dir["*.png"].each { |f| system "convert #{f} -resize 400x400 #{f[0..-5]}-sm.png" }'


Handling IE6 Incompatibilities

August 30th, 2007

While developing my browser-based game, I’m always using Firefox and periodically testing in Internet Explorer 7. A few months ago, I set up a wiki for the game and asked players to contribute to the documentation. One helpful player posted some screenshots of the game in Internet Explorer 6 and I was horrified. I knew there were transparency issues with png graphics in IE6, as well as some CSS incompatibilities, and I gave a disclaimer to say so, but coming face to face with what the site actually looked like in IE6 was quite revealing. I checked out my Google Analytics page and I could see that my visitors are split almost half and half between Firefox and IE. When I looked at the IE breakdown, I saw that it’s pretty close to half IE7 and half IE6. This means 25% of my visitors are still using IE6. I decided I didn’t want to rely 100% on a disclaimer to push IE6 out of mind; I needed to do do some fixin’.

My first problem was alpha transparency in png graphics. As many web designers know, these graphics end up with solid background colors in IE6. In most cases, it ends up looking like crap. I initially thought, am I going to have to change all my PNG graphics to the inferior GIF format just so it won’t look so bad in IE6? There are javascript hacks out there that will force transparencies to mostly work on IE6. I decided instead I wanted to create a copy of every png image as a gif and serve up the gifs to IE6 users and give pngs to everyone else. This way I wouldn’t have to worry about any problems in IE6, and my disclaimer still applies – the graphics look slightly better in Firefox, Opera, IE7, or Safari than they do in IE6.

To accomplish this task in Rails, I created an ‘images_ie6’ directory alongside my ‘images’ directory in ‘public’. I used ImageMagick’s ‘convert’ command-line tool to convert all the pngs into gifs and placed them in the same folder structure, but in the base ‘images_ie6’ directory. Then I rewrote image_tag to detect the user agent and change the images path to ‘images_ie6’, and change .png to .gif if it’s IE6.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# in app/helpers/application_helper.rb 
# override image tag to point IE6 users to a different directory with GIFs instead of PNGs
module ActionView::Helpers::AssetTagHelper
  alias_method(:orig_image_tag, :image_tag) unless method_defined?(:orig_image_tag) 
  
  def image_tag(path, options = {})
    if request.env['HTTP_USER_AGENT'] and request.env['HTTP_USER_AGENT'].include? "MSIE 6.0" 
      path.gsub! ".png", ".gif"
      path.gsub! "/images/", ""
      path = "/images_ie6/#{path}"
    end
    orig_image_tag(path, options)
  end
end

Notice that I need to check to see if the environment variable for HTTP_USER_AGENT is set before checking to see if it contains “MSIE 6.0”. For some reason, the test environment sets some variables in request.env, but leaves out the user agent string. The extra check keeps tests from failing.

Aside from the image_tag hack, I had to deal with the problems that some images were referenced from CSS (background properties for example), and there were other CSS-related issues (IE6 has trouble with the min-height and min-width properties). So to handle those problems, I’m loading an extra ie6fixes.css file if the user agent indicates IE6. These styles overwrite only the necessary elements defined in the normal stylesheets.

1
2
# in app/views/layouts/application.html  
stylesheet_link_tag 'ie6fixes' if request.env['HTTP_USER_AGENT'] and request.env['HTTP_USER_AGENT'].include? "MSIE 6.0" 

Standard disclaimer about trusting user agent strings applies. Frankly, I'm of the opinion (like many) that if a visitor messes with their user agent string, they're on their own.

I have been using Err’s acts_as_textiled plugin for making certain fields of an ActiveRecord model always display with textile formatting. It’s smart enough to know that when used in a input field, the data should be displayed unformatted. The nice thing about textiling a field is not worrying about bad HTML polluting your page or attempts at XSS.

Being that the acts_as_textiled plugin saves me from accidentally displaying a field without HTML stripping (like when I forget to use the handy <%=h erb shortcut), I started using it on fields all over the place. Sometimes, where not really appropriate.

For example, let’s say you have a User model and one of the attributes is “display_name”. Now if someone picks a name like “I*am*the*greatest*ever!”, I don’t care that they are using non-alpha-numeric characters. I just don’t want it to show up like “Iamthegreatestever!”

The problem was, I had been relying on acts_as_textiled to save me from bad HTML in that field, so now if I turn it off, someone can make their display_name “Run <script blah blah blah > or something” and I have all these places in my views where I left out the h in <%=h so I’m screwed. I needed another way to strip HTML without using textiled, but I wanted to be able to auto-strip where appropriate by setting up a model the same way acts_as_textiled does.

Enter: acts_as_stripped

From the README:

Strips HTML out of an attribute whenever it's displayed - even if it's in a form 
input box/textarea (for the purposes of this plugin, I'm considering HTML evil 
in the specified fields, no matter what). 
NOTE: no stripping happens when the attribute is written, only read.
NOTE: value is converted to string; so for example if you errantly list an integer 
attribute in the attribute list it's going to come back as a string.

Inspired by Err's acts_as_textiled, but I needed some attributes to be displayed 
without textile messing with underscores and asterisks. 

And I don't trust myself to sanitize HTML in views 100% of the time. 

Use like so: 

class SomeModel < ActiveRecord::Base
  acts_as_stripped :name, :description

    # ...
end

If you need to get the unstripped value, you can always use: 
your_model.attributes["att_name"]

Install with:

./script/plugin install http://svn.offtheline.net/plugins/acts_as_stripped/

(or just use svn or piston to import into your vendor/plugins directory)

Any feedback? Questions? Criticism? All welcome and appreciated.

One of my servers has a registration page that sends an activation code via email, and if someone signs up with a yahoo account, my logs were showing:

host f.mx.mail.yahoo.com[68.142.202.247] said: 421 Message temporarily deferred - 4.16.51. Please refer to http://help.yahoo.com/help/us/mail/defer/defer-06.html (in reply to end of DATA command)

The help page is decidedly unhelpful (I filled out a form and got an email back from yahoo that pointed me to the same page that the error message points to). After some googling, it seems to be the best way to get around this yahoo deferring problem is to set up DomainKeys (notice there is no mention of DomainKeys on that yahoo ‘help’ page). Now it seems there are several different ways of implementing DomainKeys or one of the alternative methods of signing outgoing mail. In the end, I went with DKIM Proxy.

DKIM Proxy is written in perl. It’s designed to sit on your mail server, open a couple ports, and let your mail server pass messages in and out of it. On the outgoing side, it applies a DomainKey signature (actually two – it applies both the Domain Keys Identified Mail standard and the older Yahoo! DomainKeys standard). On the incoming side, it reads signatures and verifies their integrity.

As the DKIM Proxy page notes, if you’re already using something like SpamAssassin, you’ve already got DKIM checking on your incoming mail. If you want to be able to manage spam, I’d highly recommend using SpamAssassin.

I only needed DKIM Proxy for outgoing mail. The DKIM Proxy page has fairly decent instructions, but I ran into a few snags, so hopefully this will help someone.

Before you even get started: some things you need to know.

  1. You need to have perl on your system (I don’t know of any distros that don’t come with perl) and you need to be able to install perl modules.
  2. You’ll need the SSL dev packages for your distro installed for these perl modules to install correctly (for example: apt-get install libssl-dev )
  3. You need to be able to edit your mail server’s configuration file – if you’re using postfix, there’s an example provided.
  4. You need to be able to add a sub-domain to your DNS listing for your domain.

First up is perl. There are a bunch of modules you need to get the Mail::DKIM module installed, and then you can install DKIM Proxy. The required modules are listed on the DKIM Proxy site – here is the easy way to get them installed (make sure you are superuser/root):

Note 1: If you’re using Debian or Ubuntu, you should be able to skip this step by installing the libmail-dkim-perl and libnet-server-perl packages. I believe that will get your perl install all the necessary modules and you can go on to installing DKIM-Proxy.

Note 2: If you’ve never run CPAN before, it’s going to ask you to do a manual configuration right off the bat – I usually say ‘no’ to that question and let it attempt autoconfiguration. If you don’t need to set up any proxies or anything, autoconfigure should work.

Note 3: Some of these modules have their own dependencies. If it asks you: “Shall I follow them and prepend them to the queue of modules we are processing right now?” Just hit Enter to take the default answer of [Yes].

perl -MCPAN -e'CPAN::Shell->install("Crypt::OpenSSL::RSA")'
perl -MCPAN -e'CPAN::Shell->install("Digest::SHA")'
perl -MCPAN -e'CPAN::Shell->install("Digest::SHA1")'
perl -MCPAN -e'CPAN::Shell->install("Error")'
perl -MCPAN -e'CPAN::Shell->install("Mail::Address")'
perl -MCPAN -e'CPAN::Shell->install("MIME::Base64")'
perl -MCPAN -e'CPAN::Shell->install("Net::DNS")'
perl -MCPAN -e'CPAN::Shell->install("Net::Server")'

Once your perl modules are installed, you can install dkimproxy. I won’t go into detail – this part is pretty straightforward (follow the instructions on the site).

If you’ve got dkimproxy installed now, the instructions tell you how to generate a private/public key pair and then set up your DNS record. You have to put the public key in the actual DNS record, and this means removing the line breaks and white space.

Before you go any further, reload bind/named (on most systems: /etc/init.d/named restart or /etc/init.d/bind9 restart) and make sure you see your big long public key when you do the following command:

dig selector1._domainkey.my-domain-name.com TXT

If you don’t see your public key there in a big long string, then you did something wrong in DNS. Go back and fix it before you try anything else – trust me. You’ll just end up in circles if your DNS is wrong. I had misspelled something and wasted about 20 minutes trying to figure out what was wrong with DKIM Proxy when it was my DNS entry that was the problem all along.

At this point, if you haven’t already, you should probably create a user and group called ‘dkfilter’ (or something similar).

The instructions give you an example of how the ‘dkimproxy.out’ script could be run. There is also a file in the root of the dkimproxy archive called sample-dkim-init-script.sh. This script is the easiest way to get dkimproxy running as a daemon. Edit the vars at the top of the script to meet your needs – if you used the default selector name of selector1 and the user name dkfilter you probably don’t need to change anything. I changed the domain line to read: DOMAIN=my-domain1.com,my-domain2.com,my-domain3.com

(note: if you are doing this for multiple domains, I hope you remembered to add the TXT DNS record to each domain’s zone file – and verify with dig!)

If the init script works good, copy it to /etc/init.d or wherever your init scripts live, and set it up to start at boot time (distro-dependent; it’s something I do rarely enough that I always have to look it up).

For the last part, you need to set up your mail server to direct it’s outgoing messages at dkimproxy for signing before they head out. Hopefully, you’re using postifx, because there is a cut-and-paste example for the postfix master.cf on the DKIM Proxy site. Don’t forget to do a postfix reload.

Once postfix is set up, they have a couple of test mail addresses you can use. Watch your maillog to see if things are working right. I still got a few delays from Yahoo at first, but now they seem to be flowing normally.

reversing svn externals

May 23rd, 2007

Generally when you install a plugin in a Rails app that is under version control with subversion, it’s recommended you use the –x switch to export the plugin to your directory. The –x just uses the svn export command the svn externals definition. I’ve happily installed many a plugin this way. It’s a great way to stay up to date – if the plugin ever gets updated, the changes get pulled in with svn update.

Recently, I found myself wanting to do some cleanup on an app however, and I had a couple plugins in there that I tried out at some point but I wasn’t using any more. I tried to use svn rm vendor/plugins/plugin_x , and it appeared to work (showed those files as marked with a D) but it didn’t actually remove them. Or rather, it had removed them, but they came right back when I did svn update.

Now I love subversion, but I didn’t even know where to look to figure out how to unhook these external plugins from my app. Eventually I figured out I need to check on the svn properties of the plugin directory.

jason@jwl-dev:~/dev/rails_app$ svn proplist vendor/plugins/
Properties on 'vendor/plugins':
  svn:externals

Ah, there we go. Let’s see what’s actually set in the svn:externals property.

jason@jwl-dev:~/dev/rails_app$ svn propget svn:externals vendor/plugins/
acts_as_versioned       http://svn.techno-weenie.net/projects/plugins/acts_as_versioned
unwanted_plugin         http://blahblahblah/plugins/unwanted_plugin

In order to remove the unwanted plugin, I just needed to use svn propedit svn:externals vendor/plugins and delete that line. After that, the unwanted plugin was no longer under version control and I could just delete the whole ‘unwanted_plugin’ directory (didn’t even have to use svn rm).

Observer is Deprecated

April 30th, 2007

So if you’ve seen deprecation warnings in your tests, you’ve probably been to http://www.rubyonrails.org/deprecation – for months, this page was just blank. I noticed today that it was updated, but it’s still pretty flimsy, sadly.

In one of my apps, I was using acts_as_authenticated from way back, and I’d been ignoring the error:

DEPRECATION WARNING: observer is deprecated and will be removed from Rails 2.0 See http://www.rubyonrails.org/deprecation for details. (called from /home/jason/dev/myapp/config/../app/controllers/account_controller.rb:2)

DEPRECATION WARNING: depend_on is deprecated and will be removed from Rails 2.0 See http://www.rubyonrails.org/deprecation for details. (called from observer_without_deprecation at /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/deprecated_dependencies.rb:29)

This particular deprecation is not yet mentioned on the deprecation page, but I managed to track it down. What’s deprecated is calling ‘observer’ from a controller. Instead, you need to set it in your config/environment.rb file:


        config.active_record.observers = :user_observer

That’s it.