Thursday, April 19, 2007

Tempfile issues

I recently started using attachment_fu for uploading images to my rails application.

First I had all the normal issues with the binary tempfiles on windows (ruby's Tempfile class doesn't set the mode of the new file to binary, so every time a '\n' is in the JPG, it becomes '\n\r') which there is a rails plugin to fix (search for rails_tempfile_fix.)

Then I had issues with attachment_fu claiming "size not in list" whenever I uploaded files in windows. Some posts claim that this is caused by windows not correctly telling ruby when it is done creating the tempfile, causes ruby to receive the wrong filesize. Either way, a partial fix is to tell your rails action to "sleep" for a couple of seconds so that windows can finish writing the file before ruby tries to process it. This hack actually didn't work that well for me, but either way the issue (supposedly) only affects windows and my deploy environment is a *nix box so I stopped worrying about it.

Now a new issue: On my deployment machine (rails/fcgid/apache/CentOS), certain images can cause an exception to be raised. Let's call the image XYZ.jpg. Debugging a bit, I found that attachment_fu/Tempfile does the following:

in the directory RAILS_ROOT/tmp/attachment_fu:

For n = 1..10, do:
let tmpfile_name = {current_timestamp}_XYZ_{n}.jpg
let lockfile_name = {current_timestamp}_XYZ_{n}.jpg.lock

make sure tmpfile_name and lockfile_name exist

mkdir(lockfile_name)
create tmpfile_name
rm(lockfile_name)
return tmpfile_name
end



This fails at the line mkdir(lockfile_name) when you try to upload XYZ.jpg. An error is raised saying that permission is denied on creating that file. Now, let's say I have another JPG and name it XYZ.jpg and upload it as well. This does not cause the problem. I'm fairly baffled as the only factor in difference is the content of the file, which isn't even being used in this context.

Possible culprits: Rails? attachment_fu? ruby? CentOS?

Either way, if you set the permissions of the attachment_fu/ directory to 777, it works for now.

Monday, April 2, 2007

page.select and IE

Hey!

I had an earlier post about testing the RJS command page.select. I've found further reason not to use it in the situation it was in. The problem began because originally I had something like:

page.replace_html "somediv", "somecontent"

This was causing a javascript exception sometimes because "somediv" was not always guaranteed to be on the page. My solution was to use the built in page.select in rails:


page.select("somediv").each do |value|
page.replace_html value, "something"
end

I would imagine mapping IDs to DOM elements would be an O(1) operation, but ... in internet explorer 6 it causes the whole application to freeze for a second or two if "somediv" is not present in the DOM. Not the behavior you'd want for a minor AJAX action. The RJS solution:

page << "if ($('somediv')) {"
page.replace_html "somediv", "some content"
page << "}"

Tuesday, March 6, 2007

Version of models on disk don't sync with the migrations as they run

This morning I wanted to drop my development database and rebuild it from the first migration on. When I ran rake db:migrate, it threw a bunch of errors. I discovered a crazy migration issue and found a fix. Here's the problem:

You have a series of migrations 0...X...Y. At migration Y, you also change a model's definition (ie: you add a "phone1" field and you add some validations to ensure that it is correct format or present or something.)

Now, you go to a new computer and check out your CVS repository (or your just drop your database and rebuild from scratch like I did.) Now you need to run migrations 0...Y, which may refer to your model. Unfortunately, they can only refer to the most current version of the model (the one which is in CVS at the time of migration Y.) As you run the migrations, you get to migration X, which involves using the model. Unfortunately, the model on disk (in app/models) from CVS is the version which corresponds to Y, not X (ie it wants to check the format of "phone1", which doesn't exist yet.)

What it should be doing is using the model that was CVS at the time of migration X, not the one that was in CVS at the time of checkout.

The solution I found was that you can redefine the models that you work with inside the migration itself. ie:

class Corrections1 < ActiveRecord::Migration
class Target < ActiveRecord::Base; end

def self.up
t = Target.find_by_id 112703
...


This means that Target.find_by_id is instantiating a dynamically created Corrections1::Target, not the Target in app/models. Unfortunately, this sometimes cascades to dependent models and you need to redefined a bunch of them. Also, it means that this is an extremely basic model which only knows about the database columns and doesn't run any of your validations. The best solution I can think of is copying the bodies of all the models you use in the migration into the migration file as you create it, so that it sticks with the migration file.

Monday, February 5, 2007

assert_select_rjs with on a page with "page.select" in it

I'm writing a bunch of tests RJS actions. One such action updates a DIV if it exists, but the DIV isn't always necessarily present on the page so I use a page.select in the RJS to check if it exists. (Know of a better way to do that? I wish page.replace_html would just silently fail instead of throwing an exception when the element doesn't exist)

Anyway, here's an example of the problem. Let's say we have an RJS page containing:

page.select("some_element_that_may_or_may_not_exist").each do |value|
page.replace_html value, "something"
end



Writing a test like this fails:

assert_select_rjs "some_element_that_may_or_may_not_exist" do
...
end

so does:

assert_select_rjs do
assert_select "some_element_that_may_or_may_not_exist", :count => 1
end


I'm assuming this is because of the page.select. I was forced to drop testing of this for now. If I find a fix, I'll post it here.