Saturday, September 25, 2010

Custom Form Builders in Rails...

I've started working on a new Rails application and needed to display chunks of spreadsheet-like text for financial data (e.g., Profit & Loss Statement and Balance Sheet). Everything went fine (well, not so fine: money fields were a bit of challenge, but more about that later) until I started displaying my data in a view. As you can imagine, there are a lot of cells for these displays and after some prototyping, I realized that the default size of 30 for an input field was just too big. Of course, that's easy to solve by just adding ":size => 15" to the text_field definition, but the prospect of adding that to numerous fields and than probably having to change them all left me pretty grumpy.  That's when I figured that a Custom Form Builder would be the way to go.

As usual, I headed off to the Rails API but found little there and much of the external documentation on how to use them was either way over-complicated for what I wanted to do, or going in another direction.  I did find a few good resources, including the following:

1. Ryan Bates' "Mastering Rails Forms" episode 3 - Great presentation; worth every penny of the $5 cost per episode.
2. APIDoc description of form_for method - APIDoc is your very good friend.
3. Alex Reisner's "Anatomy of a Form Builder" - Best introduction of the details behind Form Builder I found.
4. Ruby Pond's Semantic Form Builder - Lots of complex changes, nicely done
5. Aldenta post with metaprogramming example - Shows how to replace a slew of methods with metaprogramming; compare to Ruby Pond example where they are all hand-crafted.

Because of their often complicated solutions and the fact that there were different ways of doing things, I found it challenging to extract the minimum that I would have to do make a simple change. In the end, after much reading and trial coding, Ryan Bates' "Mastering Rails Forms, episode 3" completed my current understanding of the process.

Here are the key points that I learned which finally helped me get through the morass and make it work for me. In the discussion that follows, CustomFormBuilder is my class name.

To start, this is the code that I wanted to change...
from this:
<td>f.textfield :name, :size => 12</td>
to this:
f.textfield :name
Not a big change at all, but with many lines like this, I thought this would be a nice, easy way to learn about custom form builders.

Here's what I learned that helped me understand how it all works.

1) the basic form builder code looks like this:
class CustomFormBuilder < ActionView::Helpers::FormBuilder
 ...your code...
end
There may be other approaches out there that will work, but this is the most straight-forward way I have found.

You want to be able to reference your custom form builder easily, as in:
form_for :person, :builder => CustomFormBuilder do |f|
...form code...
end
It turns out there are ways to customize even this, so that your code looks like this:
custom_form_for :person do |f|
...form code which automatically uses your CustomFormBuilder
end
but that's for a little further down the page.

2) There is more than one way to implement the logistics of a custom form builder (reminds me of Perl's TMTOWTDI and I am rapidly coming to the conclusion that this applies to Rails everywhere). This was one of the things that tripped me up at first, since different posts give different instructions on how to create your builder. By logistics I mean where do you put the code that will be your form builder so that you can reference it easily?
  1. You can put it in a helper file... either in application_helper.rb or controller_helper.rb. The basic code layout is as described above... The really important thing about including this code in an existing helper file is that the code MUST come after the end statement that terminates the helper code! Failing to do this will result in your form builder not being found (this one cost me a few hours) or your having to reference it as:
    :builder => ApplicationHelper::CustomFormBuilder
  2. You can put it in a separate file in the helpers directory, with a name that matches the class name, i.e., custom_form_builder.rb
  3. You can put it anywhere you like (for example, like Ryan Bates does, in a separate directory that you create called app/form_builders). In this case, however, you must add that directory to Rails' default search path by adding the following to your environment.rb file (just uncomment and modify the skeleton statements that's already there):
    config.load_paths += %W( #{RAILS_ROOT}/app/form_builders)
3) You can make your CustomFormBuilder the default by adding the following to the bottom of your environment.rb file. Doing this means that you can leave off the :builder => option in your form_for:
ActionView::Base.default_form_builder = CustomFormBuilder
  • However, I think I agree with Ryan Bates that this is not such a great idea -- it hides what's going on and could make it difficult for some one new to your code to figure it out.
  • [Note]: Other authors recommend doing this in app/helpers/application_helper.rb and one author explicitly stated that the environment.rb approach would not work because helpers don't get initialized until they are actually called/used {could this be a version-dependent issue?]
4) Altlernatively, you can make a helper method that encapsulates the CustomFormBuilder and makes it explicit that you've got some extra magic going on. Do this in your Application Helper file:
def custom_form_for(*args, &block)
  options = args.extract_options!
  options.merge!(:builder => CustomFormBuilder)
  form_for(*(args + [options]), &block)
end
5) The code to actually do this very simple change is also very simple:
def text_field(name, options = {})
  options.reverse_merge! (:size => 12)
  @template.content_tag(:td, super)
end
  • The "reverse_merge" is so that I can override the default size of 12 with another value coded in the form
6) You'll see in some of the examples that the method signature is given with *args instead of option as I have shown it. That's a more general approach, but requires that you know a little more about how Rails can manipulate the arguments; see below. Suffice it to say that it works for my simple example, but if you're going to get serious about making more complicated customm form builders, you should get familiar with the following approaches.

extract_options! is a Rails method that will -- not surprisingly -- pull the options hash out of the method arguments. Very useful for the general method signature which uses *args.

merge! and reverse_merge! are methods that will merge and overwrite the receiving hash with the results. So, for example:
options.merge!(:size => 12)
will add :size => 12 to the options hash, while:
options.reverse_merge!(:size => 12)
will add :size => 12 to the options hash ONLY if there is no :size element already there.
  • Note the bang (!) on each of these methods -- very important. If you use the non-bang version, you won't update the options hash and things just won't work.

content_tag is a method on the @template object (provided to you by the FormBuilder) that wraps selected text in the given HTML tag. Very useful for building your output string.

There's lots more, but that should be enough to get you started -- hopefully a little easier than I did. Now that I understand some of these basics, looking at the more complicated examples is much easier for me.

Tuesday, August 24, 2010

Firing up a new Kubuntu machine for Rails development...

Well... it finally happened.

My Kubuntu machine which I prefer to use for Rails development was getting pretty crochety. I had started out on 7.04, did a distribution upgrade to 8.04, ran into some major problems installing DropBox (LinuxFormat Magazine helped me fix this problem). Finally, I tried a distribution upgrade to 9.10 and it was barely limping along. Several folks suggested a clean install from scratch, so I decided to go with Kubuntu, 10.04, 64-bit.  Here's how I went about it.

Prior to the installation, I took a tar backup of my system and also did a copy (cp -a ...) to an external USB drive. Be careful with cp: if you have other media attached (like another network-attached drive I had a /media/wdhd1), the command "cp -a / /" will follow all those links and try to get EVERYTHING onto the USB drive. I wound up simply copying over the directories that I was most interested in gaining quick access to, such as: /home, /etc, /root, /rails-app. If you've got an old system that you're going to replace, pick the directories that you don't want to lose.

BTW, I really like the NDAS combo drives: eSata, Network-Direct Attached, USB. Reasonably-priced and very effective.
  1. First get Kubuntu (or whatever flavor of Linux distro you want). I went to the Kubuntu site at http://www.kubuntu.org/getkubuntu where you can either download directly or get a CD/DVD. I downloaded the 64-bit desktop version here: http://www.kubuntu.org/getkubuntu/download#download-block
  2. Install kubuntu - pretty straight-forward.
    1. Make sure that your machine will boot from a CD
    2. Insert CD, reboot, follow the instructions.
    3. A true linux sysadmin might have gotten fancy with the disk partitioning, but I just went with the default which was everything in a single partition. It’s not like this is a heavily-loaded server...right?
    4. There were a number of prompts for keeping or replacing existing files (such as mysql/mycnt, /etc/apcupsd/apcupsd.conf, /etc/cups/cupsd.conf, etc...) and I just took the default since I had copies of all my previous config files.
    5. At one point, my mouse went inactive (I connect it through a KVM switch) but that cleared up pretty quickly.
  3. Install your favorite browser by using Kubuntu’s built-in Konqueror browser. I chose Chrome -- I’ve been using Firefox for years, but in the past, I’ve run into problems with Firefox on Linux: older versions, conflicts when trying to upgrade... I still use FireFox on Windows/Mac, but Chrome is my preferred browser for Linux.
  4. The Ubuntu community has a very good site on how to do the Rails install: https://help.ubuntu.com/community/RubyOnRails
    1. Make sure you DO NOT do “sudo apt-get install rails”. This gives you an old version and you can’t track your gems easily. If you mistakenly do that, simply type “sudo apt-get remove rails” and go back to the above link
    2. REMEMBER to add the new ruby gems path to your path so that your system can find the stuff you just installed (this is in the . The command is:
               “
      export PATH=/var/lib/gems/1.8/bin:$PATH”
      You’ll have to add this to your .bashrc file so that it will always be done when you start up.
      1. BTW, I add the following two aliases to my .bashrc so that I can quickly and easily edit .bashrc and update my environment for any new values
        1. alias brcs='source /home/jseidel/.bashrc'
        2. alias brcvi='vi /home/jseidel/.bashrc'
      2. The ‘brcvi’ alias drops me into vi (replace with your preferred editor) so that I can make changes. Notice that it always points to my home directory so that I can do this regardless of where I am.
      3. the ‘brcs’ alias “sources” the current/updated .bashrc file. I.e., it “Reads  and  executes  commands  from .bashrc in the current shell environment”, with the result that the changes you made are reflected in your current bash environment. So to add a new alias, I create the alias in my current bash, test it out, then brcvi to add it to my .bashrc and then brcs to make it active and then test to make sure I didn’ have a typo.
  5. Install git -- you are using git, aren’t you? GitHub has a great tutorial on how to do this. Just go to: http://help.github.com/linux-git-installation/ and follow the instructions. There's also an excellent book, Pro Git, by Scott Chacon, that is available on line at http://progit.org/book/.
  6. Once you’ve installed git, you have to generate an SSH keypair (see http://en.wikipedia.org/wiki/Secure_Shell if you want to learn more about SSH and http://en.wikipedia.org/wiki/Public-key_cryptography if you want to learn more about keypairs) so that you can speak encrypted git.  GitHub has another excellent tutorial on how to do this; see http://help.github.com/linux-key-setup/. Just follow the instructions there and you should have no problem. I did install the xclip utility -- makes it easy to copy/paste the information into github.
    1. If you don’t want to key-in your SSH passphrase everytime you use it, GitHub has help here as well. Go to http://help.github.com/working-with-key-passphrases/ and about half-way down the page there’s a section on “autolaunching ssh-agent” when you start your machine so that you only have to enter the passphrase once when you boot your machine.
    2. Turns out I had some permissions problem in my .ssh directory: I kept getting
            “Bad owner or permissions on /home/jseidel/.ssh/config”
      when testing things with “ssh git@github.com”.  Well, GitHub was there again, with their trouble-shooting page at
      http://help.github.com/troubleshooting-ssh/. Towards the bottom of the page in the “SSH Config” section, the two ‘chmod’ commands did the trick for me.
       
         chmod 700 ~/.ssh chmod 600 ~/.ssh/*
    3. And don’t forget to setup your git config options -- at least these two
         
      git config --global user.name "your name"
         git config --global user.email your@email.com
  7. I use RubyMine (http://www.jetbrains.com/ruby/download/index.html#linux for a 30-day free trial) so that was the next thing to install. Replace this section with whatever IDE you prefer: Aptana, NetBeans, ...  
    1. To run RubyMine, you have to have the Sun Java (JDK 1.6). I found that at http://www.oracle.com/technetwork/java/javase/downloads/index.html and followed the instructions. It turned out to be completely straight-forward.
  8. Now you’ve got everything ready to do; let’s create your first rails app in your shiny new Linux development machine:
        
    rails MyApp
        
    cd MyApp
        
    git init
        
    git commit -m "Empty Rails application"
  9. Now create your github repository -- GitHub has the answer again: http://help.github.com/creating-a-repo/
    1. Once this is done, copy in the repository URL
  10. Back to bash and hooking up your new repository with your new rails app
    1. Tell your local git repository about the github repository:
          
      git branch add origin
    2. Push your MyApp rails application up to github:
          
      git push origin master
And you should be good-to-go -- happy rails to you!

Wednesday, May 26, 2010

RMagick (finally) installed on Kubuntu...

Start with the very well-done RMagick installation FAQ at: http://rmagick.rubyforge.org/install-faq.html

./configure runs OK, then make throws this error:
     "error: ‘png_info’ has no member named ‘trans’"

After much searching and trying, ran into several other errors, and finally realized I had to install the ImageMagick development support package

And there's a huge dependency list:

Went to KPackageKit to get the development version which installed correctly...

But it still failed in RMagick installation because it (RMagick) needed a later version (I had 6.5.4 and Rmagick 12.13.1 wanted 6.5.9)

Went to the ImageMagick home page and downloaded and installed their latest version (6.6.1-7) and that installed correctly...

But now when trying to run the ImageMagick convert program (as a quick test), I got another error message:
     "error while loading shared libraries: libMagickCore.so.3: cannot open shared object file: No such file or directory"

Here's the key to getting ImageMagick to finally run... which I finally stumbled upon at http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=15702 after getting that error message. Put the following two lines in your .brashrc (or equivalent) and now it works... Obviously something to do with default paths and where ImageMagic was installed.

     export LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib"
     export LD_LIBRARY_PATH="/usr/local/lib"

Hopefully, some of these pointers will help someone.

Unbelievable...

Thursday, April 29, 2010

Changing erroneous git commit message...

Just the other day, I was making a few small changes to my new site and noticed -- just before I pushed them to github -- that I had created a bad commit message: somehow I had copied the text from the previous commit. Not Good. I figured that would be easy to change and -- while it turned out to be quite easy -- it wasn't easy to find how to do it. Googling under various phrases kept getting me things like:

"...just use git rebase -i and 'edit' the commit." While that's quite true, unless you really get git, it's like the old Microsoft helicopter joke. (I suppose if I really read through the excellent documentation provided for git, I would have been able to figure it out, but that's a lot of reading for a quick fix.)

I finally had some help from my friend Wolf Arnold and with a little bit of trial and error, I was able to modify my commit message and then push my 3 commits to github. Here's how to do it. NOTE: This procedure only works if you have not pushed your commits. Once pushed, the commit message is inviolable - by design - as far as I can tell.
  1. First, make sure you know which commit you want to change. I used gitk (for Linux; gitx on the Mac), which gives you a visual display of your commits and the status of the remotes you are pushing to. Find the relative position of the commit you want to change. In my case, it was the 2nd on the list -- that is, one down from the top.
  2. In your root project directory (where you normally type your "git status" and other commands), type the following:
    git rebase -i HEAD~n
    
    where 'n' is equal to your commit's position on the list, counting from 1 at the top of the list. Notice that the character between HEAD and n is the "twiddle" character or "tilde".
  3. Once you enter that command, you are presented with a screen (in your favorite editor) that looks something like this (the numbers following 'pick' are the internal ids for your commits):
    pick <lots of numbers>
    pick <lots of different numbers>
    # Rebase 9a122e0..9a122e0 onto 9a122e0
    #
    # Commands:
    #  p, pick = use commit
    #  e, edit = use commit, but stop for amending 
    #  s, squash = use commit, but meld into previous commit 
    # # If you remove a line here THAT COMMIT WILL BE LOST.
    # However, if you remove everything, the rebase will be aborted.
    #
    
  4. At this point, you can do one of several things:
    1. Edit the file and change the word "pick" to "edit" on the top line of the file. That's the commit that you want to change. Save the file and exit the editor. That will set you up to make the changes in your commit.
    2. Delete every line in the file and save/close it. That's equivalent to a No-Op" and will terminate the rebase.
    3. There are more options, as you can see from the instructions in the file itself, but we'll leave that for another day.
  5. When the edit finishes, you will receive instructions at the command line.  The next step in the process is to run the command:
    git commit --amend
    
    and you will again be presented with an edit screen where you can change the commit message (or other things, but I didn't get into that either). Just change the line with the commit message to what you want it to be.
  6. Once that's done and you've saved/closed the file, run the command:
    git rebase --continue
    
    and it'll be done. You can use gitk to check things out and then push to whatever remote you're using.

Enjoy...

Tuesday, April 27, 2010

Authlogic login error messages...

I've been using autthlogic to provide login/signup features for my new web application, and I really like it. It provides lots of great features and has a fair number of plugins and tutorials to add functionality.

A few weeks past, I added a password reset feature. This was quite straight-forward: I just followed the password-reset tutorial and was up & running pretty quickly.

However, when I did some additional testing on the basic authlogic functionality, I noticed that it took a very simplistic approach to providing error messages for login failures: it would report "Invalid email address" or "Invalid password" if either of those fields were in error. This is not good security: an attacker could determine a valid email address and then use it for social engineering to gain further information.

Support to fix this is already in Authlogic -- it's just not the default behavior. You have to add the following method call to your UserSession class:

     class UserSession < Authlogic::Session::Base

       generalize_credentials_error_messages true

     end

More details can be found at: http://railsapi.com/doc/authlogic-v2.1.3/classes/Authlogic/Session/Password/Config.html#M000178

This post is mostly about collecting the various sources of information about Authlogic -- it's such a popular Rails plugin, that there are many search results, some of them dead or not very helpful. I spent almost an hour googling to find the right stuff, and figured I'd post what I found in case it might save someone else some time.

General github/authlogic documentation page: http://rdoc.info/projects/binarylogic/authlogic. The "documentation" link sends you to an RDoc Info page: the main page is an overview, and you get more detail by clicking on the 3 buttons in the upper right-hand corner: "Namespace list", "Method list", and "File list".

More documentation can also be found at: http://railsapi.com/doc/authlogic-v2.1.3/ .

All in all, really good stuff!

Wednesday, April 14, 2010

Using PostgreSQL

Now that PostgreSQL is installed and running on my Mac, I need to do something useful with it, like create my database and get it migrated through Rails.  The terminology and approach is a tad different from MySQL, but after a bit of googling, I found this PostgreSQL documentation page which got me rolling. This page on creating a database was actually the starting point, because it worked through the various errors which might occur.

The key difference for me was that the PostgreSQL superuser is called 'postgres', not 'root' as in MySQL. Prior to digging out this piece of information, everything I tried got the following error:

createdb: could not connect to database postgres: 
FATAL:  role "jseidel" does not exist

The other thing that was a minor roadblock was getting out of psql (PostgreSQL's interactive SQL command program). The usual MySQL/bash suspects (quit; exit; bye; {Ctrl-C}) didn't work. With psql you either type "\q" or {ctrl-D} and you're out.

Finally, since I'm using MacPorts, the location of the PostgreSQL files are different than documented in the PostgreSQL guide: my files are rooted off /opt/local/lib/postgresql84 instead of /usr/local/pgsql as is indicated in that guide.

Now I'm able to create my database and see if it connects with Rails...

Monday, April 12, 2010

Getting PostgreSQL to run on a Mac...

So I decided to switch to PostgreSQL from MySQL. First off, it's what's used by default at Heroku, and I'm so far loving Heroku, its great integration with git, and the resulting ease of deployment. Secondly, I'm a tad concerned about what might happen in the future since Oracle now owns MySQL: could be 'interesting'.

Anyway, I'd always heard that installing PostgreSQL was more challenging than installing MySQL: "MySQL put more effort into making things easier for first-time users" is what my friends have told me. But I also heard that PostgreSQL had better and more standard SQL support (for example, support for foreign keys).

Anyway, with a bit of searching, I finally found this RubyOnRails page which seemed to do a great job of laying things out for all 3 platforms. Seemed pretty straight-forward: 

Install the server:
sudo port install postgresql83-server

Start the server:
sudo launchctl load -w /Library/LaunchDaemons/org.macports.postgresql83-server.plist

Setup the database:
sudo mkdir -p /opt/local/var/db/postgresql83/defaultdb
sudo chown postgres:postgres /opt/local/var/db/postgresql83/defaultdb
sudo su postgres -c '/opt/local/lib/postgresql83/bin/initdb -D /opt/local/var/db/postgresql83/defaultdb'

Two problems:
  1. It failed installing the database -- complaining that it couldn't find the header file libpq-fe.h. 
  2. It installed version 8.3 and version 8.4 is now current.

More searching led me to the MacPorts site which has the required library:
sudo port install libpqxx

However, it also wound up installing the 8.4 version of PostgreSQL, but version 8.3 still is the one that gets launched... seems to work, but certainly not as clean as I would like.

Given the problems I had in the past before I cleaned my machine and installed Ruby & Rails "from scratch" with MacPorts (see my previous post on this issue) as I could easily get PostgreSQL with relatively little hassle.

So far, so good...

PS Check out "Enterprise Rails" (Dan Chak, O'Reilly). He does a really nice job of discussing Rails' scaling and reliability issues and does it all in the context of PostgreSQL. Good book!

Monday, March 22, 2010

MacPorts headaches... solved!

So I finally found a way to solve all those MacPorts headaches, thanks to Erik Michaels-Ober, a friend of a friend. As I and many folks have learned, Mac OS X is less than standard *Nix when it comes to setting up their directories, and it doesn't take kindly to co-creation of a parallel directory tree. MacPorts puts things in the (*Nix) standard /opt directory, but once that's done, you get path and name clashes.

I've seen bits and pieces of a solution as noted in my previous posts, but they really didn't fix the problem. And of course, there's the issue that's been reported elsewhere that upgrading to Snow Leopard can wreak havoc on a development setup.

So following Eriks' instructions, here's what I did:
  1. I wiped my system clean and installed Snow Leopard from scratch. You may be able to just do an upgrade, but I chose to go with a clean system.
  2. Install the XCode Developer Tools that come with Snow Leopard (or download the latest version from the Apple website. Warning: it’s a big file).
  3. Remove all the pre-installed rubygems from your system. You can do this using the “gem uninstall” command, or brute force. I chose the brute force method, but only after creating a copy before I did the deletion, just in case. Once I knew it worked, I got rid of my copies.
    rm -rf /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/*

    rm -rf /Library/Ruby/Gems/1.8/*
  4. Install MacPorts (see the MacPorts installation page).
  5. Make sure you’re running MacPorts version 1.8.2 (type: port version). If not, run the command:
    sudo port selfupdate
  6. Install the MacPorts version of ruby itself:
    sudo port install ruby
  7. Install the MacPorts version of rubygems:
    sudo port install rb-rubygems
  8. Install the MacPorts version of git:
    sudo port install git-core
  9. Upgrade to the latest version of rubygems:
    sudo gem update --system
  10. Make sure you’re running rubygems version 1.3.5 (type: gem -v). If not, run the command: update_rubygems
  11. Install all the gems you want (rails, heroku, etc.). They will be installed into /opt/local/lib/ruby/gems/
I set up my machine about a month ago and it's been working fine ever since.

Enjoy...