Thursday, December 6, 2007

Getting session data in irb

The irb love continues.
This has been blogged before but it's just worthy of being repeated. I needed to check whether something had been stored into the session for the last (failed!) request. Easy. Check mysql for the last session record updated in the sessions table. Copy the session id. Pop open script/console and
session = CGI::Session::ActiveRecordStore::Session.find_by_session_id("8346c9dc0df")
where the session id is that mess at the end. Now:
>> session.data
=> {:user=>55, :user_login=>"JaneDoe", :skin=>"europe", :traveler_login=>"JaneDoe", "flash"=>{}, :traveler=>55}
>>
It's live so you can make changes on the fly.

Use irb when rails is too heavy

The user_pictures table has multiple rows for each thumbnail size we are using. We are using attachment_fu for picture uploads. Due to programmer stupidity we only added the other attribute data (like titles, descriptions, etc) to one of the thumbnail rows. How to propagate the existing data to their companion rows after hundreds of pictures had already been uploaded by users?

Tried script/console but the update action triggered attachment_fu to re-get the size of the referenced picture. Soooo sloooooow. In fact, too slow to run practically (more than a couple hours). Also tried mysql but got caught up in attempting an UPDATE with a sub-query, and failed to get the syntax right for an UPDATE with a self-referential quasi-join. Note to self - give this stuff to the dba.

Finally used plain old irb. Opened an irb session and the first line was: require 'config/environment'. This loaded ActiveRecord and all the configurations needed to work with the existing models, but without the cruft of the rest of the rails app. The ruby to update all the rows appropriately was trivial. Took all of 15 seconds to complete.

I love ruby. And rails. But ruby more.

Sunday, December 2, 2007

PNG transparancy in IE5.5/6.0 with no size

You've seen the very handy rails plugins for handling png transparency in IE 5.5 and IE 6.0: by Christopher Petersen and by The Rails Way. Don't forget Sam Stephenson's fast/clever function to get a png's size.

They all fail if you are dynamically generating a png image with transparency on the fly. But we have a fix! First some background.

You'd probably be doing this as a controller action that renders directly. The final line of your action probably looks like this:


render :text => slide_image, :status => 200, :content_type => 'image/png', :layout => false


I never figured out if you needed to actually register the png mime type for rails to handle this or if it's already registered, I simply did it anyway. Put this in your environment.rb:


Mime::Type.register "image/png", :png


I use the excellent methods from Christopher Petersen. Assay Depot has several very useful plugins on their blog - Christopher is the software guru there. So I've built on his work. His, and the other techniques, use Microsoft's AlphaImageLoader as part of a filter transform applied to the image. This is an IE-only style feature and is ignored by all other browsers. For safety, the code sniffs the browser and doesn't use the technique for other browsers. The filter usually displays the transformed image (with the correct alpha transparency) and the original image next to it and uses this to size the transformed image. To prevent this looking silly, Christopher's technique replaces the original image with a 1x1 transparent gif sized with the original image's dimensions. But what if you don't know the original image's dimensions?

There's another way to use Microsoft's AlphaImageLoader that does not require an image file size. The trick is to actually supply the image but use another transform filter to make it transparent, too. It requires a layout be applied to the image - usually width or height or position:absolute - all of which would not work for generalized image placement. However you can use the CONTENTEDITABLE html attribute as well. This attribute was invented by Microsoft and although it's part of the HTML 5 draft spec, no other browser has ever provided support for it. It has a couple drawbacks or side effects:
  • the image becomes part of the tab sequence. Not a big deal for me since I'm using the image in a link anyway.
  • the Web user can "edit" the image. The only real editing you can do is delete or resize the image for that rendering only.
If these are an issue, you might want to substitute a CSS style of postion:absolute instead.

Below is Christopher's method pair updated with code to use this technique if no size is supplied. Enjoy! Many thanks to Assay Depot for the initial work!


def png_tag(source, options = {})
options.symbolize_keys!
options[:src] = image_path(source)
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
if options[:size]
options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$}
options.delete(:size)
end
if ie? && ie? < source ="~" src =" options[:src]" src="'#{src}'," sizingmethod="'crop');" src="">"
else
content_tag("span",
image_tag(options[:src],
:style=>"filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0)"),
:style=>"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=#{options[:src]}, sizingMethod='crop');",
:CONTENTEDITABLE=>true)
end
else
"<img src="" />"
end
end

# Called by png_tag above.
def ie?
m = /MSIE\s+([0-9, \.]+)/.match(request.user_agent)
unless m.nil?
m[1].to_f
end
end
Ugh. If you can't read that code snippet very well, download it from here. I really must get a real code formatter for this blog.