S.E.A.N.I.C.U.S.

Friday, April 28, 2006

Do not underestimate the power of the block side!

I discovered some powerful things you can do with blocks this week. Specifically, they make recursive calling patterns very powerful. Case in point: I needed a way to traverse a tree structure BACKWARDS toward the root from the child. Since trees are hierarchical data structures, a recursive method seemed the best. However, as I found in the "Richer Helpers" post, I needed to wrap the contents of caller in the callee -- effectively flipping the recursive calling pattern on its head; that is, using Builder::XmlMarkup, the parent would be at a higher structural level than the child, like so:
xml.root {
xml.parent {
xml.child
}
}

Here's the rub: we don't know how deep in the tree the child node is, nor should we have to! Here's a basic outline of what I came up with:
def xml_tree(node, xml, &block)
if node.has_parent?
xml_tree(node.parent, xml) do
if block_given?
xml.node(:attr => node.attr){
yield
}
else
xml.node(:attr => node.attr)
end
end
else
if block_given?
xml.node(:attr => node.attr){
yield
}
else
xml.node(:attr => node.attr)
end
end
end


Thus, given your starting point node, anywhere in the tree, it will traverse backwards up to the root of the tree, nesting each level in the traversal. I will want to refine this algorithm, consolidating the cases, probably with a Proc.

Dynamic languages rule.

Monday, April 24, 2006

Deploy long-range probe...erm Rails!

Yes, I've watched too much Star Trek lately. But on the bright side, I successfully tested mod_fcgid on Apache 2.2 tonight. Things seem to work as expected, although, of course, it's not as speedy as lighttpd. So yes, Scott B., you can test Rails on my Linux box if you want.

Onward and upward!
(FYI: Using Fedora Core 5 with the FC-supplied RPMs, FastCGI and mod_fcgid compiled from tarballs, ruby-fcgi for the interface, and the standard changes to the .htaccess)

Richer Helpers

I learned a little about how ERb works today. What I've been trying to do is create a helper method that allows me to output a complex layout element (multiple div) and pass it an ERb block in my HTML template, but have the helper wrap its code around the block, much like the form_for method. I planned to use the XML Builder since it provides nice semantics and visually beautiful code. I thought at first that it would be as simple as using yield in the middle of my helper. I struggled with this for quite a while, but I finally discovered that when Rails encounters the block, it's going to output it to the browser automatically, even if that block is passed to the method. The solution was to use a capture, then steal a little trick from form_for: the concat(text, context) method. Here's some sample code:

# In your helper class
def complex_layout(..., &block) # &block last in params
raise ArgumentError, "Missing block" unless block_given?
buffer = capture(&block)
xm = Builder::XmlMarkup.new
xm.div {
...
# the point of your layout
# where you want the passed block to appear
xm << buffer
...
}
concat(xm.target!, block.binding)
end

What does this do exactly?
  1. The raise statement requires that a block is passed.

  2. The capture statement stores the ERb output of the block into buffer.

  3. The next set of statements create the Builder and construct the markup.

  4. The xm << buffer statement pushes the contents of buffer into your Builder markup without escaping the contents

  5. Finally, concat uses the block's binding (calling context) to output the contents of the Builder markup.


I love how Rails has allowed me to gradually discover more about the Ruby language and become more adept at using its advanced features. While I don't completely understand Procs and blocks and all of that, I'm much closer now.

Tuesday, April 18, 2006

Another update for FT

Scott Jungling, who is the only known user of FlexTimes besides myself, helped me debug a bunch of stuff yesterday. Thanks Scott!

As a result of our discussion, I decided to add a little functionality to the model-side of the plugin that I had originally desired. I had originally been disappointed with Rails' helpers' handling of plain :date or :time columns. FlexTimes was supposed to handle this, so I created two more model declarations, date_column and time_column. This will prevent unexpected nil values when your SQL column type is DATE or TIME (as is possible in MySQL).

I tagged yesterday's update as tags/1.0 for those of you who don't want this new functionality. 1.0 should work just fine with plain DATETIME columns, and you can still just filter out the data you don't need using the helper methods. If you have a pre-existing database with DATE and TIME columns (which are a PAIN to migrate, I've found), use the trunk.

Monday, April 17, 2006

FlexTimes update

I just committed a critical update to the FlexTimes plugin. Apparently, the logic in datetime_column was flawed and would only parse datetimes for the last column in the list. That flaw has been remedied.

Also, I added a :minute_step => anInteger option so you can provide users with minute intervals within the hour, rather than all 60 options.

Remember when you use this plugin that it "takes over" all of the instance-var helpers, i.e. datetime_select, date_select, time_select. For any field that uses one of these helpers, you will have to use datetime_column in the model. It also "takes over" select_hour and select_minute and provides select_ampm.

I will be investigating in the future how we might use Rails' builtin multiparameter attribute parsing, but in the meantime, you'll have to use the declaration.

If you want my demo/testing code, check out the branches/test_application section in the repository.

Friday, April 14, 2006

We're moving!

I know I've posted a lot about technical stuff lately, but I wanted to share that Elizabeth and I have selected a place to move to and are going to move on Saturday, May 6 to a carriage house apartment in KCMO. Elizabeth will be closer to UMKC and I'll still be fairly close to major highways so I can get to KCKCC for work. The church will also be under a mile away. It's a little smaller than what we're used to, but the kitchen is much nicer and the apartment more interesting. The owners are very nice and seem excited to have us.

Wednesday, April 12, 2006

FlexTimes plugin released!

The flex_times plugin I recently mentioned is now available via SVN at:

http://65.165.56.35/svn/flex_times/trunk

Please try it out and let me know what you think. The code is not especially pretty, but it is functional, and I'll be fixing that as time goes by.

As I mentioned in my comment on the post announcing the plugin, you may put any string you want into the :order array, along with the parameters you want to be able to modify (like :month, :day, :hour, etc.)

Monday, April 10, 2006

The long road to deployment

You know, I posted a lot of stuff before about deploying Rails apps, and I'd like to recap and revise those comments.
  • If you need to deploy applications quickly and have no prior bias toward servers, use lighttpd. I found it very easy to get my Rails apps running. However, I got discouraged when I had too much difficulty hosting more than one app on a single domain/vhost.

  • Don't rule out Apache + FCGI. If you're running Apache 1.3.x, mod_fastcgi works spectacularly. I discovered this because it's the default configuration for Capistrano; I figured it couldn't be THAT hard if someone else could make it work. Whereas lighttpd has weaknesses in running multiple apps, Apache shines via its extremely flexible mod_rewrite functionality. Make sure your version builds it in, or rebuild the mod using apxs and install it. With minor tweaks to the .htaccess, your app should be very happy.

  • If you're running Apache 2.x, all is not lost. You can use mod_fcgid (or mod_proxy_fcgi for 2.2), which is pretty easy to build/install. The configuration directives are WILDLY different from mod_fastcgi, so be sure to check out the Rails wiki for clues on what to use.


I have apps running in all three of these configurations, just to see if I could do it. Now if there emerges a rock-solid way to host multiple Rails apps on a single domain within lighttpd, I'll whole-heartedly support that. Lighty is just so much less of a beast than Apache. In the meantime, 1.3 or 2.0 with the appropriate module will work just fine for me.

Wednesday, April 05, 2006

Tiiiiiiiiime is on my side! Yes it is...

The first Rails project I completed for KCKCC has a lot of time and date functionality. Early on, I was quite disappointed with the flexibility of the time/date support in Rails. Specifically,

  1. Times can only be in 24-hour format.

  2. There's no time_select helper method, only datetime_select and date_select. Such a method would be helpful for those fields (DB-dependent) that are not complete DATETIMEs but only TIMEs. If you try to create it yourself using :discard_xxx options, the year persists.

  3. Not all of the helper methods let you specify :order.

  4. Specifying :prefix to the select_xxx methods doesn't work the way I expected it to.

  5. Parameters from self-constructed selects couldn't be parsed automagically by Rails. That is, you can't have a single parameter be a hash and expect it to construct the proper datatype. You have to parse it first.


The first problem was mostly solved by the 12_hour_time plugin, but there were a few bugs that prevented the multi-parameter attributes from being assigned properly, and the order was still not configurable.

Because this functionality was important to the users and I wanted to have a guarantee that the incoming data was at least CLOSE to correct, I ended up writing helper methods and some model methods that overcame these difficulties for me. One thing that bugged me from the beginning, however, was that this method was hardly DRY.

Thus, I'm in the process of writing a plugin (for public dissemination, of course) that will allow you to do the things that I had complained about above. Right now it's called "flex_times" but I'll take any suggestions on that front. Here's what you'll be able to do in your code:
# model
datetime_column [:start_time, :end_time]

# view
<%= time_select "object", "start_time",
:order => [:hour, :minute, :second, :ampm],
:twelve_hour => true %>

<%= datetime_select "object", "end_time",
:order => [:month, :day, :year, :hour] %>
The datetime_column declaration in the model will create assignment methods to parse the incoming data automatically, so you can stay DRY. The view helpers will be extremely configurable and I intend to include textual separators like :dash, :mdash, :comma, :slash, :period or allow String elements in the :order.

If it weren't for runtime extension, this would not be possible! I guess we can thank DHH and everyone else on the core team for plugins.

~conducts choir in "Halleluiah Chorus"~

Tuesday, April 04, 2006

KansasCity.rb

A KC Ruby users group already exists.
KCRUG
Thanks to Scott Raymond for the info and linkage.

RHEL is Evil

...or at least our installation of it is evil. It seems that our RHEL box has everything installed FROM SOURCE (potentially) and not from RPMs as would be proper. At the very least, I would love to run up2date, but the system isn't registered. Go figure.

This creates a situation affectionately known as "dependency hell".

I've had the pressure on to get an application into production, but it seems that everytime I try to install the packages I need, either from source, or RPM or from SRPM, it's missing something. The worst was a circular chain of dependencies that stemmed from the bash shell, which happens to already be on the system, just not in RPM form.

I know for a fact that a lot of these packages are there, but without the RPM registry correctly populated, I have no bloody idea whether they're ACTUALLY there.

When we do the website redesign, I'm going to push for a complete wipe and reinstall of the server. Apparently, it's the only way.

*sigh* Back to the grindstone.

Monday, April 03, 2006

ActionMailer attachments

Disclaimer: Binary attachments seem to be broken on Windows because of a problem with the base64 encoding. See the bug I submitted.

ActionMailer is a curious beast. I spent much of last week debugging and retooling what I thought was already working. In testing sending an email with a binary attachment, I found that the file size was smaller, and that the Word document I was using to test it was corrupted -- it thought the character encoding was Japanese, of all things. Turns out this is not a problem on our Linux server, but only on my local Windows box.

That aside, I took procreate's suggestion and looked at some raw emails in my inbox. I wanted to do a gracefully-degrading body content, AND have binary attachments. Below is a snippet of what I came up with. Keep in mind that I'm still dealing with a problem that doesn't allow my templates to be rendered using render_message, so I broke the MVC pattern and put functions in my mailer that do that for me. (Maybe this is a bug I should submit?)

  def attachments(myObject)
subject 'Subject'
recipients 'somebody@kckcc.edu'
from 'somebody-else@kckcc.edu'
content_type "multipart/mixed"

# Message body
part :content_type => "multipart/alternative" do |p|

p.part :content_type => "text/html",
:body => html_view(myObject)

p.part :content_type => "text/plain",
:body => text_view(myObject)

end

# Attachments
part :content_type => "multipart/mixed" do |p|
for att in myObject.attachments
enctype = "Base64"
if att.content_type =~ /^(text\/)/
enctype = "Quoted-printable"
end
p.attachment :content_type => att.content_type,
:body => File.read(att.file),
:filename => File.basename(att.file),
:transfer_encoding => enctype,
:charset => "utf-8"
end
end
end


There's a lot of extra stuff there, like making sure we select Quoted-printable encoding for text content-types. Also, it's not necessary to place your attachments in a separate part -- they can exist at the same level as your message body. I just did it to make sure they weren't interacting with one another unexpectedly.

FYI: I'm using Rails 1.1 and the path/filename and content-type information for my email attachments are stored in the database via an ActiveRecord model called Attachment. As always, YMMV.

Saturday, April 01, 2006

KC Rails Interest Group

I'm thinking of starting a Kansas City-based Ruby on Rails UG/SIG. Since there seem to be several developers in the area, I'm hoping I can get them together.

Some of the great things that could come of this:
  • Contribute significantly as a group to the core Ruby on Rails code
  • Develop F/OSS applications built on Rails
  • Help each other with Rails issues and problems
  • Collaborate on projects and thus help the Rails community
  • Spread the "good news" of Rails to local developers
If you're interested, comment here and pass on the word to others. I'm working on getting my old domain kcsas.org transfered to something more generic, or if there's enough interest, kcrails.org (or something similar).

Ok, so I was wrong...

The other day I tried to deploy a new app on the same server with the config I had posted in previous posts. Although YMMV, I found that I could run one OR the other, not both apps. If I had both configs included, accessing either one of them would give me a 404.

So I got onto the #lighttpd channel and asked about it. weigon (aka Jan, the author/maintainer of lighttpd) responded to me and we discussed my problem. The discussion basically ended with "If you get it working, let me know."

Back to square one!