LRBlog

Logical Reality Design: Web Design and Software Development

New Feed

May 18, 2008

Yesterday I switched the way permalinks are formatted on this blog, and apparently that hosed the feed URL, breaking my FeedBurner connection. I had to completely delete the feed and start a new one, so if you were subscribed before you will need to re-subscribe using the link on the right.

That link will take you to a feedburner page. If you were expecting a regular XML feed to subscribe in your browser, just click the “view feed xml” link in the box on the right of that page.

Installing Insoshi on a Dreamhost Account

May 17, 2008

With the Insoshi social networking platform rapidly gaining in popularity, I thought it might be useful to folks to know how to install it on the ever-popular Dreamhost shared account. If you need a Dreamhost account, please consider using the promo code “LRDESIGN” when you sign up. It will save you $50 on your first year of membership, and will help me with my site hosting expenses so I can keep this blog going.

So, there are still some possible drawbacks to this approach, but I was able to get a running install of Insoshi on my Dreamhost account with this sequence. You might want to read to the bottom of this post to learn about the difficulties with acts_as_ferret and Dreamhost before you commit to running your insoshi site on DH. Hopefully these problems will have a solution soon, and I’ll update this post if/when they do.

If you use this method and it works (or doesn’t!) please let me know in comments.

1) Set up a domain

Use the DH control panel to create a new fully hosted domain for your Insoshi site, for example yourdomain.com or insoshi.yourdomain.com. I will use “insoshi.yourdomain.com” through the rest of this post to indicate the domain that you want to use to run insoshi. I set up http://insoshi.lrdesign.com/ in the process of writing this post, but I can’t guarantee that it will stay up.

When you set up the domain:

  • Make sure that fastcgi support is selected.
  • Set “specify your web directory” to point to /home/username/insoshi.yourdomain.com/public/

2) Set up mysql databases

  1. In the dreamhost panel, select “Goodies -> Manage MySQL”
  2. Scroll down to “create a new mysql database”

I used these example settings:

  • database name: insoshi
  • use hostname: mysql.yourdomain.com (use “new hostname” to create this if you do not already have it)
  • New user: insoshi
  • New password:

If you want to run tests, you should create a second database called insoshi_test; you can leave the other settings the same.

3) Download the tarball of the current insoshi distribution:

cd
wget http://insoshi.com/home/tarball

You’ll get a tarball with a name like “insoshi-insoshi-e1fd8b8e440c9f3ab34161d4e87de78e956c1012.tar.gz”. Unzip the tarball and copy the contents to the directory you want the website to appear in:

tar xzf insoshi-insoshi-e1fd8b8e440c9f3ab34161d4e87de78e956c1012.tar.gz
cp -r insoshi-insoshi-e1fd8b8e440c9f3ab34161d4e87de78e956c1012/* insoshi.yourdomain.com/

4) Set up your database.yml file

cd ~/insoshi.yourdomain.com/config
cp database.example database.yml

Edit ~/insoshi.yourdomain.com/database.yml and make it look like the following, where <password> is the password you chose in the previous step:

development:
  adapter: mysql
  database: insoshi
  username: insoshi
  password: <password>
  host: mysql.lrdesign.com
  port: 3306

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
  adapter: mysql
  database: insoshi_test
  username: insoshi
  password: <password>
  host: mysql.lrdesign.com
  port: 3306

production:
  adapter: mysql
  database: insoshi
  username: insoshi
  password: <password>
  host: mysql.lrdesign.com
  port: 3306

5) Run the insoshi install script.

This will migrate the database and do some insoshi-specific setup. It’s also an excellent way to check that you’ve configured your database.yml correctly.

cd ~/insoshi.yourdomain.com
rake install

If it works, you should see a bunch of migrations (22 as of the current version of insoshi). If not, go back and figure out what’s wrong with your database.yml file. :)

6) Get rails working

These instructions are an adaptation of the instructions at the Dreamhost Wiki page about Rails.

Generate a dummy rails app and copy the dispatch scripts to your insoshi install:

cd ~
rails dummy
cp dummy/public/dispatch.* insoshi.yourdomain.com/public/

Edit ~/insoshi.yourdomain.com/public/.htaccess file to enable fastCGI. Change the line with dispatch.cgi to look like this:

RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

change your permissions on the public and dispatch files:

cd ~/insoshi.yourdomai.com
chmod 755 public
chmod 755 public/dispatch.*  

I also had to make my log files writeable:

chmod a+w log/
chmod a+w log/*

7) Start the ferret server

I was not able to get insoshi to run in production mode on Dreamhost at first
because in production mode it needs the ferret server (text search) to be
running or it will refuse to load some of the models. (In test and development
mode, acts_as_ferret will access the ferret databases directly, so this problem only appears in production). Based on this post, I found I could get it working by running:

script/ferret_server start -e production

At this point, I was able to load Insoshi in my browser.

Unfortunately, there are still some problems

I am pretty confident that the approach to running ferret_server above will not be a long term solution, because Dreamhost kills any processes that you leave running for more than a few hours. So ferret_server will go down after a while, and with it your site, so this will probably only get your insoshi site up for a few hours before you have to restart ferret_server. This basically means there’s no good way to use the rails plugin acts_as_ferret on a DH shared account, and unfortunately Insoshi depends on AAF.

Possible Workarounds

You could try putting the startup command in a cron script, to restart ferret_server when Dreamhost kills it, for example:

0,15,30,45 * * * * cd ~/insoshi.yourdomain.com; script/ferret_server start -e production

would attempt to start the ferret server every fifteen minutes. But that might run afoul of Dreamhost server policies (does anyone know for sure?), and in any case your site would still be down in between the time DH killed the ferret_server process and your cron job started again.

You can also alter config/ferret_server.yml to have ferret treat production mode the same as development, directly accessing the ferret database and bypassing ferret_server entirely. However, if you get concurrent access with multiple users, you are very likely to get a corrupted ferret database with that approach.

Hopefully, some permanent solutions?

The Insoshi guys are working on replacing ferret with Sphinx, and that may be a permanent solution to this problem.

You also may want to consider lobbying Dreamhost to allow users run persistent processes like ferret_server. If you are a Dreamhost subscriber you can vote for this feature by following this link to Dreamhost’s Policies Suggestions and voting for “Be able to run simple, persistent scripts!”.

Getting mysql and sphinx to work on OS X 10.4 on a G5

May 16, 2008

After some significant problems using acts_as_ferret (for some unknown reason, it absolutely will not run on my deployment server; the models will load in test but not in development or production if they use acts_as_ferret), I decided to try sphinx on the recommendation of Michael Hartl. Unfortunately, I had some problems there too: sphinx would not compile on my development workstation, a Dual G5 mac running OS X 10.4.

I did manage to solve it, though, by switching from the package-installed version of MySQL to a compiled-from-source copy. In case anyone else runs into the problem, here’s a record of how I solved it.

Symptoms of the problem

Sphinx would not compile on my mac. The error seemed to be a complaint about the header libraries, certain MySQL symbols were not getting correctly defined:


[20:05:10] sphinx-0.9.8-rc2$ make
Making all in src
g++ -o indexer indexer.o libsphinx.a -L/usr/local/mysql/lib -lmysqlclient -lz
/usr/libexec/gcc/powerpc-apple-darwin8/4.0.1/ld: Undefined symbols:
_mysql_close
_mysql_errno
_mysql_error
_mysql_fetch_fields
_mysql_fetch_row
_mysql_free_result
_mysql_init
_mysql_num_fields
_mysql_query
_mysql_real_connect
_mysql_use_result
collect2: ld returned 1 exit status
make[1]: * [indexer] Error 1
make: * [all-recursive] Error 1

It also fail to build with MacPorts (port install sphinx), with similar errors. So, after some digging I decided to try swapping out MySQL, which I’d installed from the .dmg package installer, to a copy compiled from source, hoping that this would get the header libraries set up correctly. It worked, and in the process I upgraded from 5.0.45 to the current 5.0.51b. Here’s the sequence:

First, upgrade mysql to a hand-built one.

These instructions are straight from the HiveLogic instructions on building MySQL on OS X. All I’ve added is the backup and restore of databases plus a newer URLs for the most recent MySQL 5.0 release (as of May 16, 2008).

Back up and turn off the old server:

First, back up all your existing databases using mysqldump. I did it to a bzipped file in my home directory.


[11:39:03] evan$ mysqldump --all-databases -p | bzip2 -c > databasebackup.sql.bz2

This could take a while, depending on how many databases you have, and how large. About ten minutes, for me. Now, shut down the server and remove the symlink /usr/local/mysql:


[11:49:13] evan$ sudo mysqladmin shutdown
[11:51:56] bin$ cd /usr/local
[11:52:14] local$ sudo rm mysql

Now download and build the new mysql:


[11:47:10] evan$ cd ~/src
[11:47:23] src$ wget http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-5.0.51b.tar.gz/from/http://mysql.he.net/
[11:48:09] src$ tar xzf mysql-5.0.51b.tar.gz
[11:48:25] src$ cd mysql-5.0.51b

Configure and compile:


[11:49:10] mysql-5.0.51b$ CC=gcc CFLAGS="-O3 -fno-omit-frame-pointer" CXX=gcc 
CXXFLAGS="-O3 -fno-omit-frame-pointer -felide-constructors 
-fno-exceptions -fno-rtti" 
[11:49:32] mysql-5.0.51b$ ./configure --prefix=/usr/local/mysql 
--with-extra-charsets=complex --enable-thread-safe-client 
--enable-local-infile --enable-shared
[11:49:32] mysql-5.0.51b$ make; sudo make install;

This takes a while. I went to take a shower. Fortunately, for me, it worked the first time and required no hackery, which is a pleasant surprise when building OSS software. Now setup the initial directories and databases:


[12:10:17] mysql-5.0.51b$  cd /usr/local/mysql
[12:10:21] mysql-5.0.51b$ sudo ./bin/mysql_install_db --user=mysql
[12:10:28] mysql-5.0.51b$ sudo chown -R mysql ./var

And finally start the new server and import the backed-up databases:


cd /usr/local/mysql/bin
sudo ./mysqld_safe &
bzcat ~/databasebackup.sql.bz2 | mysql

After another ten-minute import, I’m back up and running with a fresh build of MySQL.

Installing Sphinx

After that, sphinx (0.9.8-rc2) compiled normally:


[13:20:51] src$ wget http://www.sphinxsearch.com/downloads/sphinx-0.9.8-rc2.tar.gz
[13:20:56] src$ tar xzf sphinx-0.9.8-rc2.tar.gz 
[13:21:09] src$ cd sphinx-0.9.8-rc2
[13:21:12] sphinx-0.9.8-rc2$ ./configure; make
[13:23:30] sphinx-0.9.8-rc2$ make install 
... success ...

New LRDesign Site Launched

May 15, 2008

The Logical Reality main site has been badly neglected for far too long: for several months it’s just been a plain-text site with no theme, listing a bare portfolio. It’s so hard to prioritize working on my own site when there is plenty of work to do for my clients!

A custom Ruby on Rails portfolio display engine is in the works (patterned after the one I did for La Cañada Design Group). My goal is to make it skinnable with switchable themes, implemented entirely in CSS on semantic markup, a la the CSS Zen Garden. In the meantime, though, I’ve uploaded a basic site (written in RoR, but with only static pages for the moment) with a new minimalist theme.

Fixing the test setup bug in Rails 2.0.2

May 13, 2008

Help! My setup method is not running!

Much to my annoyance (and seemingly, very few other developers), Rails 2.0.2 introduced a bug in ActionController::TestCase — setup methods for tests are not run. It worked in Rails 2.0.1, and broke in Rails 2.0.2. This was fixed in the very next changeset, Revision 8442, but mysteriously it was never rolled out in a 2.0.3. That was five months ago, and given that this basically breaks all tests it’s pretty mysterious that there is still no released version of Rails with this bug fixed.

(NOTE: This was true when written. Now, Rails 2.1 is out, and it includes the fixes. You can use the workarounds below if your project depends on Rails 2.0.2, otherwise I recommend that you simply upgrade to 2.1!)

I’m pretty sure I’m not the only Rails developer who writes tests! So I’m surprised there hasn’t been more of an outcry about this bug. But the Rails dev process has always been rather mysterious to me. So: we need a workaround. I know of two.

Option one: subclass Test::Unit::TestCase

This is what I’ve been doing till now: just going back and writing my tests Rails 1.x style. It’s annoying and clunky: the whole point of ActionController::TestCase in Rails 2.0 was to eliminate all the redundant and non-DRY junk you had to put in your test setup to establish the instance variables @request, @controller, etc. In any case, it looks like this:


class FooControllerTest < Test::Unit::TestCase
  fixtures :tasks, :users, :people
  
  def setup
    super
    @controller = FooController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    # ... your setup code here ...
  end

Option two: use a revision from edge with the bug fixed

Normally, I abhor using unreleased software in a production environment. I definitely don’t use EDGE rails in my released software for clients in general … the hassle is too much to maintain with the constant changesets. I’d rather understand and work with stable software, even if that means I’m not one of the cool Rails edgerati: stable software for my customers is the higher priority.

However, in this case I’m making an exception, and I’ve started using revision 8442 of Rails as if it were a released 2.0.3, because this bug just annoys me too much. Installing it is simple. If you’re using frozen Rails in your application (and you should be!), it’s just this at the command-line, in your application root:


$rake rails:freeze:edge REVISION=8442

That should give you a “2.0.3″ that fixes this irritating bug and lets you write your tests and setup the way God intended:


class FooControllerTest < ActionController::TestCase
  fixtures :tasks, :users, :people
  
  def setup
    super
    #... your setup code here ...
  end

Don’t forget the call to super, otherwise you’ll clobber the setup method in ActionController::TestCase and your tests won’t work.

Important note for subversion users!!

If you are maintaining your project in subversion, re-freezing your rails code with the above command will hose all of your .svn/ directories under vendor/rails. This will give you a few minutes of headaches as you get things straightened back out. I recommend deleting your old rails freeze first, committing, and then re-freezing:


$svn rm vendor/rails
$svn commit -m "removed old rails version"
$rake rails:freeze:edge REVISION=8442
$svn add vendor/rails
$svn commit -m "froze rails to revision 8442"

I maintain most of my projects in Git, now, so this wasn’t a problem, but I wasn’t thinking when I converted one of my older projects that’s still in svn. It was definitely a headache when the working repository broke, so avoid that if you can.

So there you go. I never thought I’d recommend using unreleased software in production, but just hold your nose and pretend that Rev 8442 is “Rails 2.0.3″ and you’ll probably be fine. I haven’t found any problems with it, myself.

Tabbed Panels in Rails, Part 1

May 11, 2008

The tabbed panel is one of the most powerful GUI tools: an easy, intuitive way to make more information quickly accessible on a screen. This post is a practical walkthrough of implementing a tabbed panel solution in Rails using a GPL’ed plugin. Part 1 will demonstrate a basic tabbed panel implementation.

I’m developing a custom project management application in Rails for one of my clients. They need to organize large number of tasks for each project, and those tasks are divided into three categories: Action Items, Problem Reports, and Work Orders. The three types are pretty parallel in structure, and in fact I implemented them all in one table called Tasks with Single Table Inheritance.

Here’s an example of tables of the three types of tasks.

The goal is to collapse these three tables into a single tabbed panel, with tabs for Action Items, Work Orders, and Problems reports to quickly switch between the three tables.

Why I’m not using AJAX for this

Many Rails developers would implement this by drawing the tabs and using tab clicks to trigger a reload of the task listing, replacing the old listing with the right kind of tasks using AJAX and link_to_remote. This would work nicely, but would fail to satisfy one of my clients other desires: when a project’s page is printed, they would like all three categories of tasks to display on the page, each in its own table. If we actually render all three tables in a tabbed panel, then we can turn all three on with CSS in a stylesheet set to media=”print”, and solve both problems without having to generate a separate page!

The railstabbedpanel plugin

Several plugins exist that can do tabbed panels in Rails. After glancing at a couple, I picked railstabbedpanel, which is simple and looked like it would suit my needs nicely. The documentation is a bit sparse, but the “simple example” was enough to put together the basic task I needed. Download the archive and drop it in as vendor/plugins/railstabbedpanel.

As it turns out, this plugin needs a little hackery to implement a more advanced feature I want, but we’ll get to that in Part 2.

On to the implementation!

I start by loading the tasks I want to display into three instance variables:

in app/controllers/project.rb:


  def show
      @project = Project.find(params[:id])
      @action_items=@project.action_items
      @work_orders=@project.work_orders
      @problem_reports=@project.problem_reports
  end

To render a set of panels with railstabbedpanel, you pass a block to the tabbed_panel function, which creates a “tab context” object for you to use. The tab context object (which I called “tabctx” as per the example docs for railstabbedpanel) has a function, panel, which itself takes a block - the content you want inside the panel, and a parameter, which is the title you want to appear on your tab. There are plenty of other options you can pass, but we won’t need any of them in part one.

Since I’m going to be making three panels, I’ll put that in a partial called _task_panel, to which I’ll pass the tab context object and the list of tasks to display.

in app/views/project/show.html.erb


  <% tabbed_panel do |tabctx| %>
    <%= render :partial => "task_panel", :locals => {
      :tabctx => tabctx,
      :tasks => @action_items,
      :title => "Action Items"} %>    
    <%= render :partial => "task_panel", :locals => {
      :tabctx => tabctx,
      :tasks => @work_orders,
      :title => "Work Orders"} %>    
    <%= render :partial => "task_panel", :locals => {
      :tabctx => tabctx,
      :tasks => @problem_reports,
      :title => "Problem Reports"} %>    
  <% end %>

in app/views/project/_task_panel.html.erb:


  <% tabctx.panel(title) do %>
    <table class='listing'>
      <%= render :partial => "task_header_row" %>
      <%= render :partial => "task", :collection => tasks %>
    </table>
  <% end %>    

The two partials above render a fairly simple array of s (_task_header_row.html.erb) or s (_task.html.erb) to fill out the table of information about the task: creation date, due date, responsible party, summary, etc. I’ll spare you the code for those; they’re pretty straightforward.

We also need a little help from CSS to get the display right, as indicated by the documentation for railstabbedpanel:

in public/stylesheets/application.css:


.panel_selected {
    display: block;
}
.panel_unselected {
    display: none;
}
.tab_selected {
    background-color: gray;
}
.tab_unselected {
    background-color: white;
}

This works — sort of — we get our tab names, and clicking on them will activate the appropriate panels. But it certainly doesn’t look too good@ With just the suggested formatting, railstabbedpanel outputs our tabs as a stack of list items:

Obviously we’d like to improve on this. Let’s take a look at the HTML generated by the railstabbedpanel plugin:


<div class='tabbed_panel' id='tabbed_panel_124'>
<ul class='tab_container' id='tabbed_panel_124_tabs'>
  <li class='panel_tab tab_selected ' id='tabbed_panel_124_126_tab'>
    <a href="#" onclick="tabbed_panel_124_125('tabbed_panel_124_126'); 
                    return false;">Action Items</a>
  </li>
  <li class='panel_tab tab_unselected ' id='tabbed_panel_124_127_tab'>
    <a href="#" onclick="tabbed_panel_124_125('tabbed_panel_124_127'); 
                    return false;">Work Orders</a>
  </li>
  <li class='panel_tab tab_unselected ' id='tabbed_panel_124_128_tab'>
    <a href="#" onclick="tabbed_panel_124_125('tabbed_panel_124_128'); 
                    return false;">Problem Reports</a>
  </li>
</ul>
<ul class='panel_panels' id='tabbed_panel_124_panels'>
<li class='panel_panel panel_selected ' id='tabbed_panel_124_126_panel'> 
    <table class='listing' id='action_items'>  
    ... etc ....

So what we have is a ul with the class tab_container, with one li for each tab, each of which has class panel_tab and an additional class for either selected or unselected. The panels themselves are in a second ul of class panel_panels, with list items of class panel_panel. That’s fairly clean semantic markup, and in fact this is one of the reasons I chose railstabbedpanel in the first place.

So, let’s add a couple of basic rules to li.panel_tab to float the tabs next to each other, and some extra space at the top of ul.panel_panels. These few extra rules get us much closer to what we want:


li.panel_tab {
    list-style: none;
    float: left;
    padding: 1em;
    margin-right: .5em; 
}
ul.panel_panels {
    clear: both;
}

The result looks like this, which is a nice little functional tabbed panel:

We could play this game with CSS all day trying to get the tab styling just right and testing it in all the relevant web browsers. But as with everything else, some other developer has already struggled through this before, and with Google I dug up this lovely little reference at Adobe. I applied those styles to the containers determined previously by inspecting the HTML generated by the tabbed panel plugin, and tweaked the colors and fonts a bit to match my client’s application. The resulting CSS looks like this:


/*------------ TABBED PANELS -------------------*/
/* many styles from http://labs.adobe.com/technologies/spry/articles/tabbed_panel/ */
.tabbed_panel {   
    padding: 0px;
    clear: both;
    width: 100%; /* IE Hack (hasLayout Bug)*/    
}
.panel_panel {
    padding: 0.5em 0;
}
.panel_selected {
    display: block;
}
.panel_unselected {
    display: none;
}
.tab_container {
    margin: 0px;
    padding: 0px;
}
ul.panel_panels {
    clear: both;
    border-left: solid 1px #CCC;
    border-bottom: solid 1px #CCC;
    border-top: solid 1px #999;
    border-right: solid 1px #999;
    background-color: white;
    padding: 10px;
}
li.panel_tab {
    position: relative;
    top: 1px;
    float: left;
    padding: 4px 10px;
    margin: 0px 3px 0px 0px;
    font: bold 1.2em sans-serif;
    list-style: none;
    border-left: solid 1px #CCC;
    border-bottom: solid 1px #999;
    border-top: solid 1px #999;
    border-right: solid 1px #999;
    -moz-user-select: none;
    -khtml-user-select: none;
    cursor: pointer;        
}
li.tab_selected {
    background-color: white;
    border-bottom: 1px solid white;
}
li.tab_unselected {
    background-color: #aac;
}
li.tab_unselected:hover {
    background-color: #A77;
}
li.panel_tab a {
    color: black;
    text-decoration: none;
}
li.panel_tab a:hover {
    color: #009;    
}

Which results in this much improved tabbed panel:

Testing It

As I mentioned in a previous post, I think it’s important for tutorials to explain not just how to write the code, but how to test it as well, so let’s take a look at some tests that make this happen.

To start with, I need to add one small bit to the HTML to make it easily testable. If the tables that contain the task information each have an id specifying which group of tasks they contain, it will be easier to target them with assert_tag. So, I added such an ID to each table, using the name of the title, converted to lowercase with underscores for spaces, making them ‘action_items’ and so forth:

In app/views/project/_task_panel.html.erb


 <table class='listing' id='<%= title.gsub(/ /,'_').downcase %>'>   

Now in the test case for the show action on a project, I created a method that can look for a table whose id matches such a title. Inside the same function, I look for rows with an id constructed to match that task’s ID number (this is generated by the _task.html.erb partial, which I didn’t show above). And I look for a cell in that row whose text matches the “summary” field of the task. That should be sufficient to determine that each task of the appropriate type has a row in that table, though you can of course test for plenty more tags and attributes if you like.

In test/functional/project_controller_test.rb:


  private
  # Test that the table containing rows for all registered tasks are correctly
  # rendered.  The task type should be sent in underscore format, like 
  # "action_item" or "work_order"
  def test_tasks_table(title,tasks)     
    id = title.gsub(/ /,'_').downcase
    table_tag = {:tag=>'table',  :attributes=>{:id=>id}}
    assert_tag table_tag
    
    #li containing the appropriate title for the tab
    assert_tag( { :tag => 'li', 
        :attributes => { :class => /panel_tab/ },
        :content => /#{Regexp.escape(title)}/ } )
    
    # check for all the task rows
    tasks.each do |task|
      row_tag = { :tag => 'tr', :ancestor => table_tag, 
        :attributes => { :id => "task_row_#{task.id}" } }
      assert_tag row_tag
      assert_tag( { :tag => 'td', :parent => row_tag, 
          :content => /#{task.summary}/ } ) 
      #...test any other important aspects of the tasks' TR row here...      
    end
  end

And then I simply call this for all three task types in the method that tests the “show” action for a project. In this case, @proj_one contains an instance of the project model pulled from fixtures:

In test/functional/project_controller_test.rb:


  def test_show
    get :show, :id => @proj_one.id

    assert_response :success
    assert_template 'show'
    
    #assert the project object
    assert_not_nil assigns(:project)
    assert assigns(:project).valid?    

   #confirm that tasks row summaries are listed correctly
    test_tasks_table("Action Items",@proj_one.action_items)
    test_tasks_table("Work Orders",@proj_one.work_orders)
    test_tasks_table("Problem Reports",@proj_one.problem_reports)
  end

Okay, that works great, now we’ve confirmed that the tables are output correctly. Let’s move on and test the tabs themselves. As we saw above when examining the HTML, the tabs are a set of li.panel_tabs inside a ul.tab_container. The whole thing is contained inside a div.tabbed_panel. We want to confirm that each tab contains text matching the appropriate title. So, one quick helper method an three lines added to our test_show method will test these for us:

Added to test/functional/project_controller_test.rb


   def test_show
      ... 
    #confirm that the three tabs exist
    assert_panel_tab "Action Items"
    assert_panel_tab "Work Orders"
    assert_panel_tab "Problem Reports"
  end
  
  def assert_panel_tab title 
    ancestor = {:tag => 'div', :attributes => {:class => "tabbed_panel" }}
    parent = {:tag => 'ul', :attributes => {:class => "tab_container" }}
    assert_tag({:tag => 'li', 
        :attributes => {:class => /panel_tab/},
        :parent => parent,
        :ancestor => ancestor,
        :content => /#{title}/  
        }) 
  end
  

Note that I’m not testing :class => "panel_tab" for the tabs, but using match instead of a string. This is because the actual full class is going to be either class="panel_tab selected" or class="panel_tab unselected", and we want to match the tag in either case.

What’s next?

That’s it for this installment. In Part 2, I’ll explain how to endow the tabs with memory, so that if you click on, say, Problem Reports, and then leave the page, that same tab will still be in front when you come back. In Part 3, I’ll look at some more advanced styling, including making all three tables show nicely on the printout.

Website Launch: Insoshi Social Networking Platform

April 30, 2008

The Insoshi Open-Source Social Networking Platform project was just launched yesterday! Logical Reality designed both the Insoshi logo, and landing/informational site that leads new users to the project itself.

Insoshi is built with Ruby on Rails, and is the first major social networking tool to be released completely open-source. Here’s an article on Insoshi and the launch at TechCrunch.

The site was a rapid-development Web-2.0 style project, developed using the blueprint CSS framework and other standards-compliant techniques. The logo was chosen to represent Insoshi’s core functionality in social networking: communication between people. The lowercase i’s on the ends invoke iconified people, with the “broadcast” arcs indicating communication from one to the other.

Book Recommendation: RailsSpace by Hartl and Prochazka

April 30, 2008

RailsSpace

Which book should a beginning rails developer choose?

I want to plug the book I learned Rails from: RailsSpace by Michael Hartl and Aurelius Prochazka. Most people in the rails community recommend Agile Web Development, but I’m going to disagree. Strongly. I think most people who recommend AWD aren’t familiar with RailsSpace, and having studied from both I think RailsSpace is the better book by far.

I tried AWD first, and found that while it’s a decent introduction to Rails, it does a poor job of explaining basic Ruby concepts to programmers who are not used to it. (I was coming to RoR from a background in PHP, Java, and Javascript). As one example, it used the Ruby :symbol syntax consistently, without ever explaining just what the heck that was. I went through a couple of chapters copying down code and making minor modifications trial-and-error style, but I lacked a thorough understanding of what was going on because I was coding in an unknown language. I am used to learning languages very quickly, and AWD just wasn’t delivering.

When it just got too frustrating, I tried RailsSpace. I had both books available but tried AWD first because the title of RS suggested to me that it was for experienced rails developers looking specifically to making social networks. Not so: it starts from the beginning, for new developers who have never touched Ruby before, and is much more thorough in its explanations. Every time a new construct or concept gets used in the course of an example, the authors set up a nicely formatted sidebar explaining that concept with its own examples. This gives you the explanation you need to avoid getting lost, without interrupting the flow of the main narrative. In the first two chapters it answered all the confusions that I had suffered while reading AWD, and I felt like I gained a solid understanding of Ruby fundamentals as well as Rails technique.

Yes, the example they build is a social network, but I felt like that was only as a functional example, analogous to the shopping cart AWD uses. The techniques are expandable to any site you’d want to build, and because you understand them better with RS you’ll be able to apply them to other types of sites more easily.

Though fewer people are aware of it, I think it’s really a better book for the Rails beginner.

A note: the current version of RS uses Rails 1.2.3 instead of the newer Rails 2.x. However, I actually think that’s a good place for a beginner to start: in my opinion, most of the stuff changed or added in Rails 2 is great for a developer who knows rails, but actually harder for a beginner who’s never used Ruby before.

First post!

April 30, 2008

My plan is to use this space to post challenges I’ve faced (or am facing) in the course of Ruby on Rails development, and my solutions to them, when found.  I have switched mostly to Ruby on Rails for web applications development these days. While I enjoy the power and capability of the system, I find that the documentation and community resources still leave something to be desired, so this is my way of contributing back to the community.

When posting an example or technique, it’s my intention to include both the code and the tests I wrote for it. In my experience many of the otherwise lovely Rails blogs out (e.g. the indispensible RailsCasts by Ryan Bates) there rarely include tests, and often it’s not obvious how to test a feature correctly.   The close integration of testing is, in my opinion, one of the most enticing features of Rails as a webdev framework. So I’d love to see testing become a more integral part of community technique examples and tutorials.