ActiveRecord error, even when valid?
January 23rd, 2009
Even though record.valid? is true, are you getting false for record.save, and record.save! throws an ActiveRecord::RecordNotSaved error? This one has burned me a couple of times so this time I’m writing it down.
The solution: Check your before and after callbacks! By returning false on any of these callbacks, you are in effect returning false on “save”. Here’s an example:
1 2 3 4 5 |
before_create :set_defaults def set_defaults self.something = false end |
In a ruby method, if you don’t specifically “return” a value, you will always return the result of the last line of the method. In this case, you’re returning the value of self.something, which you just set to “false”. The fix is simple:
1 2 3 4 |
def set_defaults self.something = false true end |
The same thing can happen in your controllers. By unintentionally returning false, in an action or in a before_filter, you can end up with some confusing results.
FTP Authentication via Rails
January 5th, 2009
Problem
We want to be able to allow users to upload files via FTP, and we want them to use the same accounts that they use on our Rails based site. How can we authenticate an FTP server against our Rails app?
Solution
If we use pure-ftp as a server, we can create a custom authentication module that the FTP service will use to validate logins.
Details on creating a custom authentication module for pure-ftp are found here: http://download.pureftpd.org/pub/pure-ftpd/doc/README.Authentication-Modules
The Gist: All we need to do is create a script that will use incoming environment variables and echo a handful of directives.
At minimum, we just need to handle the AUTHD_ACCOUNT and AUTHD_PASSWORD variables. We could do this with a simple rake task, being that rake already has access to environment variables. The downside to that solution is that rake is going to load up our Rails app every time someone tries to log in via FTP. The result is a slight delay in the login process and a spike in memory usage. Think of one of those FTP bots coming along and trying out as many logins as possible and imagine rake loading our Rails app into memory that many times at once.
Of course, we could always use a background process to load up one Rails instance once and then respond to it. Setting up a background processor can be a complicated process if you don’t already have one going for your app.
Since all we really need to do is return a blob of text, why not just hit the Rails app itself, via HTTP? We can create a controller action that returns plain text in the format required by pure-ftp and then use wget to grab it and echo it. With this solution, we’re hitting the web server, which is already loaded into memory, so there is no delay and no memory spike during login (well, only enough to run wget).
Let’s look at the minimum response we need to tell pure-ftp the user is valid:
auth_ok:1
uid:1000
gid:1000
dir:/path/to/rails/app/public/files/userid
endThe first line is either 1 or 0. 1 means the user is valid, 0 means they are invalid (bad login or password). According to the docs, pure-ftp will also accept -1, which means that the login was correct but the password was not. Since authentication plugins for Rails typically don’t distinguish between bad login/password and just bad password (why let the hackers know they guessed one half of the combo?) I’m going to return only 1 or 0.
uid and gid are the system level user and group that you want to use to run the service under. Pure-ftp is giving you the chance to increase security by assigning a unix-level user account to the login here, but since we’re not using unix-level accounts, we can just specify the user and group that has rights to the directory where the files will reside. If you’ve only created one user on your system and you’re deploying your Rails app under that user, chances are pretty good it’s going to be uid 1000 and gid 1000. You can find out by checking /etc/passwd:
jason@jason-1501:~$ grep jason /etc/passwd
jason:x:1000:1000:Jason LaPier,,,:/home/jason:/bin/bashThe dir directive is the directory we want the FTP user to start out in. In this example, we’re saying that if a user has id ‘99’, they get put into a directory at “RAILS_ROOT/public/files/99”.
Let’s take a look at the controller action that will spit out this block of text (I put this in a Users controller, but you can put it wherever you want):
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 |
# somewhere, we have to define SYSTEM_USER, SYSTEM_GROUP, and FTP_ROOT # I always stick these kinds of constants in another module so I can change them # depending on the RAILS_ENV FTP_ROOT = "#{RAILS_ROOT}/public/files" SYSTEM_USER = '1000' SYSTEM_GROUP = '1000' # here's the action - we're expecting params: login, password def ftp_auth user = User.authenticate(params[:login], params[:password]) if user user_root = File.join FTP_ROOT, user.id.to_s # mkdir_p is like "mkdir -p" - it creates the directory and parents as necessary, # doing nothing if they already exist FileUtils.mkdir_p user_root # chown_R is like "chown -R" - make sure the system user owns the files directory FileUtils.chown_R SYSTEM_USER, SYSTEM_GROUP, FTP_ROOT # build the output for a valid user. Be careful not to get whitespace at the # beginning of each line, or pure-ftp will ignore the output output = <<-END auth_ok:1 uid:#{SYSTEM_USER} gid:#{SYSTEM_GROUP} dir:#{user_files_root(user)} end END else # invalid user, so all we need is for auth_ok to be 0 output = "auth_ok:0\n" + "end\n" end render :text => output end |
If you are using sessions or other before_filters, don’t forget to take those out for this action. Since we’re not really making this connection with a web browser, most of that stuff is unnecessary. If you want to send the request via post, you may also need to take out forgery protection.
1 2 3 4 5 |
# at the top of your controller, of course skip_before_filter :my_ridiculous_sidebar_loader, :only => :ftp_auth skip_before_filter :login_required, :only => :ftp_auth skip_before_filter :verify_authenticity_token, :only => :ftp_auth session :off, :only => :ftp_auth |
Now let’s point our browser at the action and see what happens. If we go to http://localhost:3000/users/ftp_auth, we should get:
auth_ok:0 endSince HTML ignores whitespace, you’ll have to view the page source to verify that the whitespace is correct. Better yet, whip up a controller test!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# stick this in your controller's Test::Unit test # try with no params and you should get a failure def test_ftp_auth_fail out = post :ftp_auth assert_not_nil out.body assert_equal "auth_ok:0\nend\n", out.body end # this is a test user that came with the restful_authentication plugin def test_ftp_auth_valid out = post :ftp_auth, :login => 'aaron', :password => 'test' assert_not_nil out.body assert_equal "auth_ok:1\nuid:1000\ngid:1000\n" + "dir:#{RAILS_ROOT}/public/files/#{users(:aaron).id}\nend\n", out.body end |
Alright, we’re halfway there! Now we just need to set up pure-ftp to use a script for custom authentication.
Getting pure-ftp:
Most package installers will have it, or you can download it and compile it yourself fairly easily.
Setting up pure-ftp:
Pure-ftp doesn’t use a configuration file in it’s natural state – only command line arguments. Different Linux distributions come with different “wrappers” which parse configuration files and run pure-ftp with the appropriate arguments. Let’s just run pure-ftp from the command line for now.
First, we need a script that pure-ftp can call. This script just needs to hit the Rails app via HTTP and return the response, so we could whip up something in our favorite scripting language. For now, in the name of simplicity, let’s just shell-script a wget call:
#!/bin/sh
wget --post-data="login=$AUTHD_ACCOUNT&password=$AUTHD_PASSWORD" -qO- http://localhost:3000/users/ftp_auth | catSave this file (for example, as “ftp-auth.sh”) and make it executable (“chmod +x ftp-auth.sh”). If you run it now, you should get the “auth_ok:0” text (since the environment variables are not set). Start up pure-ftp’s auth daemon by running:
pure-authd -s /var/run/ftpd.sock -r /path/to/ftp-auth.sh -BNext, start up the ftp server (remember to kill it first if it’s already running – especially if you installed pure-ftp with a package manager). Here’s an example with command line options:
pure-ftpd -lextauth:/var/run/ftpd.sock -X -A -F /pathto/banner -E -B -c 20 -C 2 -n 50:100I’m using the following options in this example:
-A —chrooteveryone (very important! this will make the user’s home directory the virtual “root”, so the user can’t go up the directory tree and into your system)
-B —daemonize
-C —maxclientsperip (most ftp clients will try to use as many connections as possible, so you might want to limit this)
-c —maxclientsnumber (total number of connections you want to allow)
-d —verboselog (leave this out for production usage)
-E —noanonymous (no anonymous ftp users allowed)
-F —fortunesfile
-n —quota
-X —prohibitdotfilesread (don’t show ‘hidden’ files or folders)
Now point your favorite FTP client at your server and try it out. You should be able to log in with a user in your Rails app, no problemo!
Rails Plugin: Johnny Cache (Timestamping Fragment Caches)
July 10th, 2008
Problem
You’re enjoying the performance benefits of fragment caching, but managing the cache using sweepers is not really appropriate for some fragments. Instead, you’d rather have a “time to live” option for a cached fragment, so that the cache would be expired and the fragment regenerated only if it has passed a certain age.
Update: Code has been changed to work with Rails 2.3.5 and is now hosted at github.
Solution
Of course, there are many ways to skin this cat. In the end, I settled on somewhat of a hack. However, this solution will work no matter what storage option you are using for caching.
Basically, the plugin overrides cache and cache_erb_fragment, inserting an HTML comment into the beginning of the block of HTML being cached. This HTML comment contains a timestamp (using the to_i method on a Ruby Time object) which marks the time (in UTC) at which the fragment expires. The next time cache is called, the fragment gets read, the timestamp can be checked, and the fragment can be expired or preserved depending on the current time.
Examples
In a view:
1 2 3 4 5 6 |
<% cache("some_fragment", :time_to_live => 5.minutes) do -%> This fragment was cached at <%= Time.now.utc.strftime "%H:%M:%S on %m/%d/%Y" %> and will expire at: <%= 5.minutes.from_now.utc.strftime "%H:%M:%S on %m/%d/%Y" %> (UTC) <% end %> |
In a controller:
1 2 3 4 |
# instead of: read_fragment("some_fragment"), try: unless get_johnny_cache("some_fragment") # make some expensive DB queries end |
Installation
script/plugin install git://github.com/jlapier/johnny_cache.git(or use piston or whatever you prefer)
Notes
- Remember: If you want to play with caching and you’re working in the development environment, you need to enable caching. Find the perform_caching line in environments/development.rb and change it to true, and restart your server. If you don’t make this change, you’re never caching any fragments, and therefore, this plugin won’t do anything for you (in development; caching is turned on by default for the production environment).
- Updated to works in Rails 2.3.5; however, the tests no longer work.
- I assume this only works for ERB fragment caching and not Haml (or any other HTML generator). However, the code for the plugin is pretty simple and it should be easy to adapt it to Haml if you desire. If you get it working with Haml and want to send me a patch, please feel free (jason.lapier AT gmail).
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 (or anything else in a 'viewport')
March 12th, 2008
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....
rake and truncated task descriptions
February 24th, 2008
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!
Unobtrusive Image Rollovers with Low Pro
February 17th, 2008
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).
- 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.
- 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.
acts_as_stripped: auto-stripping HTML from a field
June 14th, 2007
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.



