<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-27393356</id><updated>2012-01-22T23:33:23.976-08:00</updated><category term='ruby'/><category term='Classes'/><category term='Flash'/><category term='jobs'/><category term='SEO'/><category term='CSS'/><category term='REST'/><category term='mongrel'/><category term='rails'/><category term='recruiting'/><category term='development'/><category term='Actionscript'/><category term='XHTML'/><category term='testing'/><category term='ferret'/><category term='Think Barefoot'/><category term='Packages'/><title type='text'>Barefoot Development</title><subtitle type='html'>A web application development blog of techniques and practices we've have come up with in the course of building web-based applications.  Since we've benefitted from others sharing ideas, this is our way of giving back.  

We build applications for &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;, a Cincinnati-based Advertising/Interactive agency.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-27393356.post-8693576509982591608</id><published>2008-02-08T11:48:00.000-08:00</published><updated>2008-02-08T11:52:46.462-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='recruiting'/><title type='text'>Barefoot is hiring developers again</title><content type='html'>Take a look at our website for these two job openings and how to apply.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://www.thinkbarefoot.com/jobs/development/2008/01/senior-application-developer-rails/"&gt;Senior Rails application developer&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.thinkbarefoot.com/jobs/development/2008/02/web-application-developer-2/"&gt;Web application developer&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-8693576509982591608?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/8693576509982591608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=8693576509982591608' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8693576509982591608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8693576509982591608'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2008/02/barefoot-is-hiring-developers-again.html' title='Barefoot is hiring developers again'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-3417094221278162036</id><published>2008-02-01T10:48:00.001-08:00</published><updated>2008-02-01T11:36:52.970-08:00</updated><title type='text'>DRY Up Your SWF SEO</title><content type='html'>We've talked about &lt;a href="http://barefootdevelopment.blogspot.com/2006/05/flash-seo-home-run-for-barefoot-part-1.html"&gt;search engine optimization with Flash before&lt;/a&gt;, and for large database-driven sites the methodology still applies. But for smaller Flash sites that still have great content there may be a simpler, DRYer (Don't Repeat Yourself) solution.&lt;br /&gt;&lt;br /&gt;This technique, just like &lt;a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript"&gt;unobtrusive javascript&lt;/a&gt;, works best when the page is first designed for the lowest common denominator (in this case search bots or folks without Flash) and then &lt;i&gt;progrssively enhanced&lt;/i&gt; with a richer experience when supported. With that in mind, let's start with the navigation.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;div id="nav"&amp;gt;&lt;br /&gt; &amp;lt;ul&amp;gt;&lt;br /&gt;  &amp;lt;li&amp;gt;&amp;lt;a href="/" class="selected"&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;  &amp;lt;li&amp;gt;&amp;lt;a href="/about/"&amp;gt;About Us&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;  &amp;lt;li&amp;gt;&amp;lt;a href="/contact/"&amp;gt;Contact Us&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;/ul&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Next, our page needs some content. It may have images, paragraphs, bulleted lists and some formatting. It's not XML, so we'll want to keep the tags that lend the formatting.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;div id="content"&amp;gt;&lt;br /&gt; &amp;lt;p&amp;gt;Hello &amp;lt;b&amp;gt;World&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This basic HTML has created a great foundation for our site. Screen readers, search engines, and iPhone users will all be able to enjoy our content. But for folks with Flash, we can make the experience even better. If we place the nav and content divs inside a wrapper div, SWFObject can replace the contents of the wrapper with our nifty Flash movie.&lt;br /&gt;&lt;br /&gt;Getting started, that unordered list nav looks very familiar as HTML, but it also looks like something else &amp;#151; XML. So why don't we load this XML into our Flash movie? It even tells us which page we're on (via &lt;code&gt;class="selected"&lt;/code&gt;). That will allow great flexibility with the design of the nav in Flash. Using &lt;a href="http://blog.deconcept.com/swfobject/"&gt;SWFObject&lt;/a&gt;, it's as simple as this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;so.addVariable("xml_input", escape(document.getElementById("nav").innerHTML));&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now we should feed the content into Flash the same way as the faux XML nav:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;so.addVariable("html_input", escape(document.getElementById("content").innerHTML));&lt;/code&gt;&lt;br /&gt;&lt;br /&gt; All that's left is to parse the XML for the nav and render the HTML from the content. Executions will vary, but this should get us started:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;// Parse the xml content&lt;br /&gt;XML.prototype.ignoreWhite = true;&lt;br /&gt;var myXML:XML = new XML();&lt;br /&gt;myXML.parseXML(_level0.xml_input);&lt;br /&gt;var rootNode:XMLNode = myXML.firstChild;&lt;br /&gt;...&lt;br /&gt;// Set the html content&lt;br /&gt;_root.content_txt.htmlText = _level0.html_input;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This method allows the developer to spend more time building a sound, accessible site and less time creating XML schemas to load in duplicate content for two presentations. Flash supports a &lt;a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_14808"&gt;good subset of HTML tags&lt;/a&gt; as well as custom CSS, so you should be able to match most of the usual content formatting.&lt;br /&gt;&lt;br /&gt;Bobby Uhlenbrock, Application Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-3417094221278162036?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/3417094221278162036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=3417094221278162036' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3417094221278162036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3417094221278162036'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2008/02/dry-up-your-swf-seo.html' title='DRY Up Your SWF SEO'/><author><name>Bobby</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/29/buddyicons/77418348@N00.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-2642741894284904648</id><published>2007-12-04T15:35:00.000-08:00</published><updated>2007-12-04T15:37:54.313-08:00</updated><title type='text'>NetBeans Ruby Debugger Doesn't Like Symbolic Links</title><content type='html'>Let me say that the new &lt;a href="http://www.netbeans.org/" target="_blank"&gt;NetBeans 6 IDE&lt;/a&gt;, released in its final version just yesterday, is an amazing &lt;a href="http://www.rubyonrails.org" target="_blank"&gt;Ruby on Rails&lt;/a&gt; development environment.  I love the intellisense features, quick-switching between controller, view, and test files, and so many other things.  But the biggest thing I was looking forward to was the integrated debugger.&lt;br /&gt;&lt;br /&gt;Unfortunately, on my Mac OSX Tiger system I couldn't get breakpoints to work.  NetBeans just ignored them.  I spent several hours trying to debug it, unfortunately led astray by a &lt;a href="http://en.wikipedia.org/wiki/Red_herring" target="_blank"&gt;red herring&lt;/a&gt;.  Since I'm using &lt;a href="http://weblog.rubyonrails.org/2007/11/29/rails-2-0-release-candidate-2" target="_blank"&gt;Rails 2.0 RC2&lt;/A&gt; on my current project, and a new empty app based on Rails 1.2.5 worked great in the NetBeans debugger, I though it must be some problem in Rails 2.0.  However, after eliminating everything different between the two apps, I found the real culprit.&lt;br /&gt;&lt;br /&gt;I keep all of my production working directories in a main directory, and have a symbolic link in my home directory to provide quick access.  Unfortunately, when the NetBeans project includes a symbolic link, breakpoints in the debugger don't fire.  When I changed my reference to use the absolute path (sans symlink), the breakpoints work great.  &lt;br /&gt;&lt;br /&gt;Now NetBeans and I are getting along great.  I highly recommend it for any Ruby or Java development.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-2642741894284904648?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/2642741894284904648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=2642741894284904648' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/2642741894284904648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/2642741894284904648'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/12/netbeans-ruby-debugger-doesnt-like.html' title='NetBeans Ruby Debugger Doesn&apos;t Like Symbolic Links'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-1142516373341468086</id><published>2007-11-27T12:36:00.001-08:00</published><updated>2007-11-28T07:29:28.936-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Processing Remote Files with Attachment Fu</title><content type='html'>I've used &lt;a href="http://techno-weenie.net/"&gt;Rick Olson's&lt;/a&gt; &lt;a href="http://www.clarkware.com/cgi/blosxom/2007/02/24"&gt;attachment_fu&lt;/a&gt; on a few projects now, and it's become one of the first plugins I import into my &lt;a href="http://rubyonrails.org/down"&gt;rails&lt;/a&gt; directory. But for my current task I need to not only upload photos but also import external images from a data service and run them through the usual cropping and scaling. I think it is ideal for all images, regardless of source, to be run through the same model where they are subjected to the same operations and validations. So that's what we'll do.&lt;br /&gt;&lt;br /&gt;Currently, when you post a multi-part form with a file input box named &lt;code&gt;uploaded_data&lt;/code&gt;, attachment_fu will grab that TempFile with a setter of the same name and do its magic. I have followed the same process with a new method. It can go right in the model that &lt;code&gt;has_attachment&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    # Takes input of a remote file via an absolute URL,&lt;br /&gt;    # reads it and passes it to attachment_fu for processing   &lt;br /&gt;    def remote_data=(file_url)&lt;br /&gt;      return nil if file_url.nil?  &lt;br /&gt;      open(file_url) do |data|&lt;br /&gt;        # extract the filename and extension from the url&lt;br /&gt;        temp_filename = URI.split(file_url)[5][/[^\/]+\Z/]&lt;br /&gt;        # pass details to attachment_fu&lt;br /&gt;        self.filename     = temp_filename&lt;br /&gt;        self.temp_data    = data.read&lt;br /&gt;        self.content_type = data.content_type           &lt;br /&gt;      end    &lt;br /&gt;    end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This opens the attachment and places the contents in a TempFile just as if it had been uploaded. Keep in mind that while my input data is quite reliable, you may need to add a few checks to ensure you have a valid extension and mime type. You can use the new method thusly:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    photo = Photo.new&lt;br /&gt;    photo.remote_data = "http://www.somewhere.com/an_image.jpg"&lt;br /&gt;    photo.save&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Bobby Uhlenbrock, Application Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-1142516373341468086?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/1142516373341468086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=1142516373341468086' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1142516373341468086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1142516373341468086'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/11/processing-remote-files-with-attachment.html' title='Processing Remote Files with Attachment Fu'/><author><name>Bobby</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/29/buddyicons/77418348@N00.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-7652705510303196771</id><published>2007-11-09T10:12:00.000-08:00</published><updated>2007-11-13T06:18:36.363-08:00</updated><title type='text'>A Fix for a WordPress Paging Problem</title><content type='html'>I recently discovered a problem in the next/previous paging links of a custom &lt;a href="http://wordpress.org/"&gt;WordPress&lt;/a&gt; 2.2.1 site we created for &lt;a href="http://www.fractionaljetsfocus.com"&gt;Fractional Jets Focus&lt;/a&gt;.  When viewing a list of articles by category, the previous posts link generated a 404 error.  &lt;br /&gt;&lt;br /&gt;After digging for too long through WordPress code and &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt; search results, I found that the problem was related to an apparent bug or conflict between the paging feature and custom permalinks.  &lt;br /&gt;&lt;br /&gt;Our custom permalink setting in Options -&gt; Permalinks is this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;/%category%/%year%/%monthnum%/%postname%/&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;When you want to view all the posts for a certain category, an example URL is /blog/2007/9/.  The problem is that the URL generated by &lt;code&gt;next_posts_link()&lt;/code&gt; was misinterpreted by WordPress because of the permalink.  The link was: &lt;code&gt;/blog/2007/9/page/2/&lt;/code&gt;.  Unfortunately, the string "page" in this URL was interpreted as a post name, instead of a token for the page index.&lt;br /&gt;&lt;br /&gt;There is probably some voodoo I could have done in the .htaccess file with mod_rewrite to fix the issue, but I feared breaking something else.  So, I wrote the following plugin.  It's so short that I'm publishing the whole thing inline.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;/*&lt;br /&gt;Plugin Name: Fix Paging in Category Listings &lt;br /&gt;Plugin URI: http://www.thinkbarefoot.com&lt;br /&gt;Description: Fixes a bug where next/previous links are broken in category by year/month listings&lt;br /&gt;Version: 0.5&lt;br /&gt;Author: Doug Smith&lt;br /&gt;Author URI: http://www.thinkbarefoot.com&lt;br /&gt;&lt;br /&gt;Copyright 2007  Doug Smith  (email: dsmith@thinkbarefoot.com)&lt;br /&gt;&lt;br /&gt;This program is free software; you can redistribute it and/or modify&lt;br /&gt;it under the terms of the GNU General Public License as published by&lt;br /&gt;the Free Software Foundation; either version 2 of the License, or&lt;br /&gt;(at your option) any later version.&lt;br /&gt;&lt;br /&gt;This program is distributed in the hope that it will be useful,&lt;br /&gt;but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;GNU General Public License for more details.&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Function to fix problem where next/previous buttons are broken on list&lt;br /&gt; * of posts in a category when the custom permalink string is:&lt;br /&gt; * /%category%/%year%/%monthnum%/%postname%/ &lt;br /&gt; * The problem is that with a url like this:&lt;br /&gt; * &lt;br /&gt; * /category/2007/10/page/2&lt;br /&gt; * &lt;br /&gt; * the 'page' looks like a post name, not the keyword "page"&lt;br /&gt; */&lt;br /&gt;function remove_page_from_query_string($query_string)&lt;br /&gt;{ &lt;br /&gt;    if ($query_string['name'] == 'page' &amp;&amp; isset($query_string['page'])) {&lt;br /&gt;        unset($query_string['name']);&lt;br /&gt;        // 'page' in the query_string looks like '/2', so split it out&lt;br /&gt;        list($delim, $page_index) = split('/', $query_string['page']);&lt;br /&gt;        $query_string['paged'] = $page_index;&lt;br /&gt;    }      &lt;br /&gt;    return $query_string;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;add_filter('request', 'remove_page_from_query_string');&lt;br /&gt;&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This plugin simply checks to see whether the post name is 'page', and if there is a 'page' parameter too.  If so, it removes the 'name', and assigns the page index to the magic 'paged' parameter.  Paging restored.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-7652705510303196771?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/7652705510303196771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=7652705510303196771' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/7652705510303196771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/7652705510303196771'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/11/fix-for-wordpress-paging-problem.html' title='A Fix for a WordPress Paging Problem'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-8150237020111829115</id><published>2007-11-01T11:08:00.000-07:00</published><updated>2007-11-01T12:19:10.469-07:00</updated><title type='text'>I'm Glad Rails Loves SQL</title><content type='html'>It's a good thing that the core team for &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; has chosen to embrace SQL, and easily expose it to developers.  Other object-relational mapping layers often try to hide SQL as 'evil'.  However, there are times when nothing will substitute for being able to talk directly to the database, in its own language.&lt;br /&gt;&lt;br /&gt;Yesterday was such a time.  I have an application that displays articles divided into hierarchies of categories.  Articles are rendered differently on the site depending on their category. &lt;br /&gt;&lt;br /&gt;I needed to change a list of articles from one top-level category (let's call it food) so that some articles in one of its sub-categories (call it expert reviews) only appeared one time per sub-category.  So, while articles from other sub-categories of food would all appear in the list, only the latest article from any expert reviewer would appear in that same list. &lt;br /&gt;&lt;br /&gt;I wanted to handle this in the model using a custom finder so that controllers could just call something like &lt;code&gt;Category.published_article_list(options)&lt;/code&gt; and the details would be irrelevant. &lt;br /&gt;&lt;br /&gt;Ok, enough background, here's the method I added to the Category model:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;def published_article_list(options = {})&lt;br /&gt; if (food?)&lt;br /&gt;   # Special query that returns only the latest article for each 'reviewer' category&lt;br /&gt;   sql  = "mid_category_id != #{REVIEW_CAT} OR articles.id IN ( "&lt;br /&gt;   sql += "SELECT SUBSTRING( MAX( CONCAT( published_on, id ) ), 11 ) AS ra_id "&lt;br /&gt;   sql += "FROM articles "&lt;br /&gt;   sql += "WHERE mid_category_id = #{REVIEW_CAT} AND "&lt;br /&gt;   sql += "      #{Article.conditions_published} "&lt;br /&gt;   sql += "GROUP BY category_id ) ) "&lt;br /&gt;   options[:conditions] = sql&lt;br /&gt; end&lt;br /&gt; self.published_root_articles.find(:all, options)&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That SQL is a little serious, so here's what it does in English.  It starts by removing any articles that have a mid-level category of "review".  It then adds the articles from that category that we want using a subquery. &lt;br /&gt;&lt;br /&gt;The meat of the subquery is in this part of the &lt;code&gt;SELECT&lt;/code&gt; clause: &lt;code&gt;SUBSTRING( MAX( CONCAT( published_on, id ) ), 11 ) AS ra_id&lt;/code&gt;.  I needed to get the ID of the latest article, grouped by category_id (the individual reviewer).  So, this code concatenates the published_on date to the article ID, finds the max of all those values, then uses &lt;code&gt;SUBSTRING&lt;/code&gt; to chop the concatenated date off, leaving only the id of the latest article.  So, for a list of articles with IDs and dates like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;id    date         category_id&lt;br /&gt;101   2007-10-01   1&lt;br /&gt;102   2007-09-30   1&lt;br /&gt;103   2007-10-02   2&lt;br /&gt;104   2007-10-01   2&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When the concatenated dates and IDs are sorted, they end up like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;2007-10-02103&lt;br /&gt;2007-10-01104&lt;br /&gt;2007-10-01101&lt;br /&gt;2007-09-30102&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Using this method, the subquery accurately adds the IDs of articles 101 and 103 to the IN clause above. &lt;br /&gt;&lt;br /&gt;The only downside to this method is that the &lt;code&gt;CONCAT&lt;/code&gt; function requires a full table scan, so indexes won't be used in the subquery.  Since this page is cached, it wasn't a problem for this application, but if performance becomes an issue other optimization could be done while still using this strategy.&lt;br /&gt;&lt;br /&gt;Thanks for loving SQL, Rails!&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-8150237020111829115?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/8150237020111829115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=8150237020111829115' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8150237020111829115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8150237020111829115'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/11/im-glad-rails-loves-sql.html' title='I&apos;m Glad Rails Loves SQL'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-8677468180222438183</id><published>2007-10-19T14:49:00.000-07:00</published><updated>2007-11-27T19:55:30.899-08:00</updated><title type='text'>classic_pagination is not my friend</title><content type='html'>I recently upgraded a nearly completed &lt;a href="http://www.rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt; project to version &lt;a href="http://weblog.rubyonrails.org/2007/10/5/rails-1-2-4-maintenance-release"&gt;1.2.4&lt;/a&gt;, then to version &lt;a href="http://weblog.rubyonrails.org/2007/10/12/rails-1-2-5-maintenance-release"&gt;1.2.5&lt;/a&gt;.  I worked through the deprecation warnings and everything seemed good to go.  &lt;br /&gt;&lt;br /&gt;Then, I ran into a snag with pagination -- something I didn't have covered in my functional tests.  Because I'm caching this site, I have routes that include the &lt;a href="http://www.railsenvy.com/2007/2/28/rails-caching-tutorial#pagination"&gt;:page property for the paginator in the URL&lt;/a&gt;.  Otherwise, pages 2-n don't get cached.&lt;br /&gt;&lt;br /&gt;After installing the &lt;a href="http://plugins.require.errtheblog.com/browser/classic_pagination"&gt;classic_pagination plugin&lt;/a&gt;, I'd get an error when viewing pages with pagination code like this in the view:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  &amp;lt;%= link_to('&amp;laquo; Previous', :page =&gt; @article_pages.current.previous) if @article_pages.current.previous %&amp;gt;&lt;br /&gt;  &amp;lt;%= link_to('Next &amp;raquo;', :page =&gt; @article_pages.current.next) if @article_pages.current.next %&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This is the standard, recommended method for creating the Next and Previous links.  The error said: &lt;code&gt;undefined method `paginator' for "2":String&lt;/code&gt;.  The top-most offending line of the stack trace was: &lt;code&gt;vendor/plugins/classic_pagination/lib/pagination.rb:307:in `=='&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The confusing part was that when I removed the :page from the route and added ?page=2 back as a query string parameter, the error went away.  In theory, the :page should be the :page whether it is on the query string or embedded in the URL, right?&lt;br /&gt;&lt;br /&gt;Well, long story short, here's the fix.  Unfortunately, I can't explain exactly why this is necessary, and don't have the time now to dig into the innards of classic_pagination and the supporting Rails infrastructure to find out.  I just hope this post helps somebody. &lt;br /&gt;&lt;br /&gt;Oh, the fix?  Add the "number" method to the previous or next Page object reference, like so:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  &amp;lt;%= link_to('&amp;laquo; Previous', :page =&gt; @article_pages.current.previous&lt;b&gt;.number&lt;/b&gt;) if @article_pages.current.previous %&amp;gt;&lt;br /&gt;  &amp;lt;%= link_to('Next &amp;raquo;', :page =&gt; @article_pages.current.next&lt;b&gt;.number&lt;/b&gt;) if @article_pages.current.next %&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I'll probably consider classic_pagination my friend again once I get over being grumpy about this ... or, I may consider one of the &lt;a href="http://plugins.require.errtheblog.com/browser/will_paginate"&gt;other&lt;/a&gt; &lt;a href="http://cardboardrocket.com/pages/paginating_find"&gt;pagination&lt;/a&gt; &lt;a href="http://paginator.rubyforge.org/"&gt;contenders&lt;/a&gt; for a future application.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-8677468180222438183?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/8677468180222438183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=8677468180222438183' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8677468180222438183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8677468180222438183'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/10/classicpagination-is-not-my-friend.html' title='classic_pagination is not my friend'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-3640189469424015004</id><published>2007-10-04T08:13:00.000-07:00</published><updated>2007-10-04T08:17:37.254-07:00</updated><title type='text'>Attorneys on Rails</title><content type='html'>Cross-posting this link to our main Barefoot blog, where we describe &lt;a href="http://www.thinkbarefoot.com/blog/technology/2007/09/attorneys-on-rails/"&gt;a significant Rails site we recently completed&lt;/a&gt;, and show at a high level how we accomplished it.  Let us know if you'd like more details.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-3640189469424015004?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/3640189469424015004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=3640189469424015004' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3640189469424015004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3640189469424015004'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/10/attorneys-on-rails.html' title='Attorneys on Rails'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-2250631672714623648</id><published>2007-09-19T13:11:00.000-07:00</published><updated>2007-09-19T13:48:37.290-07:00</updated><title type='text'>Disable items in Flash ComboBox component</title><content type='html'>Recently, Barefoot built a new promotional site for Miller Lite–&lt;a href="http://millerpregame.com/"&gt;millerpregame.com&lt;/a&gt;. The site is a cross promotion with Wal-Mart. The main attraction allows users to vote on their favorite NFL city to tailgate at. We built a small voting widget in Flash and utilize Flash Remoting. It is a simple form really, just a &lt;a href="http://livedocs.adobe.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&amp;amp;file=00002139.html"&gt;ComboBox&lt;/a&gt; that lists the 32 NFL teams and a textfield so users can add comments as to why the city submitted is really the "best."&lt;br /&gt;&lt;br /&gt;Each week the votes are tallied and a city is declared a winner. We then compile tidbits about the city, the atmosphere the fans create, and any pregame rituals that have been submitted. Once a city wins they are then taken out of the list of eligible cities going forward throughout the season.&lt;br /&gt;&lt;br /&gt;To avoid confusion, we wanted to grey out the previous weeks winners in the ComboBox. Disabling options in the ComboBox Component should be as easy as setting a property in the dataprovider right? Wrong.&lt;br /&gt;&lt;br /&gt;Luckily, the ComboBox uses the &lt;a href="http://livedocs.adobe.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&amp;amp;file=00002485.html"&gt;List Component&lt;/a&gt; and the List Component can utilize the &lt;a href="http://livedocs.adobe.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&amp;amp;file=00002101.html"&gt;CellRenderer API&lt;/a&gt;. The CellRenderer API is a way of styling and manipulating the individual items (aka "cells") in a List Component.&lt;br /&gt;&lt;br /&gt;To implement your own extended &lt;a href="http://livedocs.adobe.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&amp;amp;file=00002101.html"&gt;CellRenderer Class&lt;/a&gt;, you simply define the class with a few required methods and properties. Similar to an Interface structure in Java, etc...&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You then create a MovieClip in your library and define it's linkage properties using the Class you have created to be your CellRenderer.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next, you designate the cellrenderer property of the List inside the ComboBox to point at your new Class. &lt;/div&gt;&lt;div&gt;&lt;code&gt;&lt;br /&gt;// define cellRenderer class&lt;br /&gt;comboBox.dropdown.cellRenderer = "MyCell";&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;MyCell is the name given to the identifier in the MovieClip linkage properties mentioned above.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;OK, so now we have more control over "cell" rendering. We now use the setValue method required in our new MyCell class.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We decided to set a property in our dataprovider array that would identify which items in the ComboBox we wanted to disable.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;// for example&lt;br /&gt;cities.push({label: 'Cincinnati', data: 7, selectable: 'false'});&lt;br /&gt;comboBox.dataProvider =  cities;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We then check for this property in the setValue method.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;// define the item index&lt;br /&gt;var cellIndex:Number = getCellIndex().itemIndex;&lt;br /&gt;&lt;br /&gt;// Apply text formatting&lt;br /&gt;if (listOwner.dataProvider.getItemAt(cellIndex).selectable == 'false') {&lt;br /&gt;// code goes here&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If that if statement is true we set the cell textfield to html so we can easily style it using a font tag and setting a color like grey to indicate to the user the option is disabled. We also need to remove the default rollover action and onpress action for the cell.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;_tLabel.html = true;&lt;br /&gt;_tLabel.htmlText = '&amp;lt;font style="color:#DDDDDD;"&amp;gt;' + sLabel + '&amp;lt;/font&amp;gt;';&lt;br /&gt;// define row&lt;br /&gt;var row = this._parent;&lt;br /&gt;// remove the highlight&lt;br /&gt;row.highlight = row.background;&lt;br /&gt;// add a fake onPress event&lt;br /&gt;row.onPress = null;&lt;br /&gt;// don't show the hand cursor&lt;br /&gt;row.useHandCursor = false;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That is it! Items we designate are now disabled and appear greyed out. Some times Flash is better than HTML and other times HTML is just easier.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-2250631672714623648?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/2250631672714623648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=2250631672714623648' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/2250631672714623648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/2250631672714623648'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/09/disable-items-in-flash-combobox.html' title='Disable items in Flash ComboBox component'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-8032103296956246779</id><published>2007-07-16T16:24:00.001-07:00</published><updated>2007-07-17T10:50:46.227-07:00</updated><title type='text'>JRuby on Rails -- Performance</title><content type='html'>I have been spending some time evaluating &lt;a href="http://jruby.codehaus.org/"&gt;JRuby 1.0&lt;/a&gt; as a possible &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; deployment platform.  Following RailsConf 2007, I was very excited by the potential of JRuby as a vehicle for deploying Rails apps that would have the advantages of robust J2EE application servers, including multi-threaded load management, database connection pools, and wide-ranging enterprise support.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JRuby Install&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;We typically deploy Rails apps using &lt;a href="http://mongrel.rubyforge.org/index.html"&gt;mongrel&lt;/a&gt; clusters behind an &lt;a href="http://httpd.apache.org/"&gt;Apache 2&lt;/a&gt; webserver.  So, I started with a pretty straightforward Rails app I'm building, and deployed it as normal on the mongrels. &lt;br /&gt;&lt;br /&gt;(By the way, unless otherwise mentioned, software versions used are native Ruby 1.8.6, Rails 1.2.3, JRuby 1.0, &lt;a href="http://tomcat.apache.org/"&gt;Tomcat 6.0.13&lt;/a&gt;, Java 1.5.0_07 on OSX, &lt;a href="http://java.sun.com"&gt;Java 1.6.0&lt;/a&gt; on RedHat Linux, Apache web server 2.2.3.)&lt;br /&gt;&lt;br /&gt;The next step was to get JRuby and setup a .war file to deploy.  I found &lt;a href="http://www.techcfl.com/blog/?p=109"&gt;this blog post to be a very helpful&lt;/a&gt; start.  I already had the latest Java from Apple installed, so I installed JRuby to &lt;code&gt;/usr/local/jruby-1.0&lt;/code&gt;.  I edited &lt;code&gt;/etc/profile&lt;/code&gt; to include the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;export JRUBY_HOME="/usr/local/jruby-1.0"&lt;br /&gt;export PATH="/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:$JRUBY_HOME/bin"&lt;br /&gt;export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"&lt;br /&gt;export ANT_HOME="/Developer/Java/Ant"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I decided to deploy the .war into Tomcat since I'm more familiar with it than the recommended &lt;a href="https://glassfish.dev.java.net/"&gt;Glassfish&lt;/a&gt; and I knew how to connect it well with Apache.  So, I created a JDBC connection pool to the MySQL database by adding a META-INF directory to the Rails app, with the following &lt;code&gt;context.xml&lt;/code&gt; file:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;Context path="/myapp" reloadable="true" crossContext="true"&amp;gt;&lt;br /&gt;  &amp;lt;!-- Database Connection Pool --&amp;gt;&lt;br /&gt;    &amp;lt;Resource name="jdbc/myapp" &lt;br /&gt;           auth="Container" &lt;br /&gt;           type="javax.sql.DataSource"&lt;br /&gt;           factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"&lt;br /&gt;           maxWait="1000"&lt;br /&gt;           removeAbandoned="true"&lt;br /&gt;           maxActive="30"&lt;br /&gt;           maxIdle="10"&lt;br /&gt;           removeAbandonedTimeout="60"&lt;br /&gt;           logAbandoned="true"&lt;br /&gt;           username="myuser" &lt;br /&gt;           password="mypass" &lt;br /&gt;           driverClassName="com.mysql.jdbc.Driver"&lt;br /&gt;           url="jdbc:mysql://127.0.0.1:3306/myapp_development?autoReconnect=true" /&amp;gt;&lt;br /&gt;&amp;lt;/Context&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is the corresponding &lt;code&gt;database.yml&lt;/code&gt; file that tells Rails where to find the connection pool data source:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;development:&lt;br /&gt;  adapter: jdbc&lt;br /&gt;  jndi: java:comp/env/jdbc/myapp&lt;br /&gt;  &lt;br /&gt;production:&lt;br /&gt;  adapter: jdbc&lt;br /&gt;  jndi: java:comp/env/jdbc/myapp&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;With these configuration files, I went through the JRuby process for creating the .war file.  I setup Tomcat and deployed the app, which worked after I put the MySQL driver into Tomcat's /lib directory.  I connected the Tomcat app to Apache with mod_proxy_ajp.  I can post more details about these configs if there is interest.&lt;br /&gt;&lt;br /&gt;One important optimization that I added was to include JAVA_OPTS that launched Tomcat with enhanced memory heap limits for the JVM, and with a couple other parameters I found while searching for JRuby optimizations online.  So, I added this line to my &lt;code&gt;/etc/profile&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;export JAVA_OPTS="-Xms128m -Xmx512m -Djruby.objectspace.enabled=false -Djruby.jit.enabled=true"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Performance&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;After configuring and setting everything up on my MacBook Pro (2GHz Intel Core Duo, 1.5GB RAM), I was excited by the performance of JRuby.  Using simple comparison of wget spiders between the native Ruby on Rails (using 3 mongrels behind Apache) and JRuby versions, I was seeing over a 2x improvement in JRuby.  The spider of around 24 URLs was taking about 10 seconds on native Ruby, while only about 4 seconds on JRuby/Tomcat.&lt;br /&gt;&lt;br /&gt;I then moved to deploying on our RedHat Linux-based staging server, where the 24 URL spider was running in about 1.5 seconds with native Ruby (same 3 mongrel cluster behind Apache 2).  With repeated tweaking, I couldn't get the JRuby/Tomcat to run faster than 4 seconds, even swapping out JVMs of Java 5 and Java 6. &lt;br /&gt;&lt;br /&gt;I then considered that JRuby might have an advantage under load, so I setup &lt;a href="http://grinder.sourceforge.net/"&gt;Grinder&lt;/a&gt;, an open-source Java-based load testing tool, to "grind" both versions.  The test simulated 10 simultaneous threads from three users, with each test repeated three times.  Here are some comparison numbers of a couple URLs (times in milliseconds):&lt;br /&gt;&lt;br /&gt;&lt;TABLE style="text-align: right;"&gt;  &lt;TR&gt;   &lt;th&gt;Run #&lt;/th&gt;   &lt;th&gt;Native A&lt;/th&gt;   &lt;th&gt;Java A&lt;/th&gt;   &lt;th&gt;Native B&lt;/th&gt;   &lt;th&gt;Java B&lt;/th&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD&gt;1&lt;/TD&gt;   &lt;TD&gt;554&lt;/TD&gt;   &lt;TD&gt;1070&lt;/TD&gt;   &lt;TD&gt;586&lt;/TD&gt;   &lt;TD&gt;378&lt;/TD&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD&gt;2&lt;/TD&gt;   &lt;TD&gt;861&lt;/TD&gt;   &lt;TD&gt;928&lt;/TD&gt;   &lt;TD&gt;519&lt;/TD&gt;   &lt;TD&gt;885&lt;/TD&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD&gt;3&lt;/TD&gt;   &lt;TD&gt;719&lt;/TD&gt;   &lt;TD&gt;1450&lt;/TD&gt;   &lt;TD&gt;496&lt;/TD&gt;   &lt;TD&gt;680&lt;/TD&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD&gt;4&lt;/TD&gt;   &lt;TD&gt;833&lt;/TD&gt;   &lt;TD&gt;800&lt;/TD&gt;   &lt;TD&gt;484&lt;/TD&gt;   &lt;TD&gt;507&lt;/TD&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD style="border-bottom: 1px solid #000000;"&gt;5&lt;/TD&gt;   &lt;TD style="border-bottom: 1px solid #000000;"&gt;863&lt;/TD&gt;   &lt;TD style="border-bottom: 1px solid #000000;"&gt;827&lt;/TD&gt;   &lt;TD style="border-bottom: 1px solid #000000;"&gt;701&lt;/TD&gt;   &lt;TD style="border-bottom: 1px solid #000000;"&gt;414&lt;/TD&gt;  &lt;/TR&gt;  &lt;TR&gt;   &lt;TD&gt;Average&lt;/TD&gt;   &lt;TD&gt;766&lt;/TD&gt;   &lt;TD&gt;1015&lt;/TD&gt;   &lt;TD&gt;557&lt;/TD&gt;   &lt;TD&gt;572&lt;/TD&gt;  &lt;/TR&gt;&lt;/TABLE&gt;&lt;br /&gt;On average, JRuby is either slower or nearly equal in performance under load.  It's still respectable considering JRuby's 1.0 status.  The main downside was the significant disparity in CPU and RAM usage between JRuby and native Ruby.  JRuby pegged the dual core CPUs under load, using upwards of 250MB of RAM, while the mongrels maintained under 30% CPU with about 40MB per mongrel.  &lt;br /&gt;&lt;br /&gt;I remain excited about JRuby, but can't use it for production work quite yet.  I'm sure the JRuby team will continue with optimization -- and I'll be keeping a close eye on its progress for sure.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-8032103296956246779?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/8032103296956246779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=8032103296956246779' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8032103296956246779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/8032103296956246779'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/07/jruby-on-rails-performance.html' title='JRuby on Rails -- Performance'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-1569584288791452707</id><published>2007-06-25T06:46:00.001-07:00</published><updated>2007-06-25T06:48:51.350-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jobs'/><title type='text'>Barefoot is adding a developer</title><content type='html'>Barefoot has an immediate opening for a Web Application Developer. We’d like you to have 3+ years experience developing database-driven, dynamic web sites.  We’re strong in a number of technologies, the primary ones being Ruby on Rails, C#.NET, and PHP.   You need to be very strong in one of them, and if you’ve got experience in a second, bonus points.  You’ve also got to be at least comfortable with client-side Web development (JavaScript, CSS, XHTML), although that won’t be the main part of your job. You’ll receive excellent benefits and a salary commensurate with your experience. &lt;a href="http://www.thinkbarefoot.com/jobs/development/2007/06/web-application-developer/"&gt;To submit your resume, go here.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-1569584288791452707?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/1569584288791452707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=1569584288791452707' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1569584288791452707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1569584288791452707'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/06/barefoot-is-adding-developer.html' title='Barefoot is adding a developer'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-1404979695730699700</id><published>2007-05-31T15:16:00.000-07:00</published><updated>2007-06-01T10:09:33.188-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>RESTful Rails Naming Conventions with Nested Controllers</title><content type='html'>I recently spent some time learning how to name everything in &lt;a href="http://www.rubyonrails.org"&gt;Rails&lt;/a&gt;' new &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;RESTful&lt;/a&gt; &lt;a href="http://api.rubyonrails.org/classes/ActionController/Resources.html"&gt;map.resources&lt;/a&gt; world, given that I like to group my controllers into subdirectories.  For typical content managed sites, I keep the main site in a directory &lt;code&gt;/site&lt;/code&gt;, and the content management tools in a directory &lt;code&gt;/admin&lt;/code&gt;.  &lt;br /&gt;&lt;br /&gt;So, how do you get your syntactically-sugar-coated paths when you have a controller in a directory like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;/app/controllers/admin/users_controller.rb&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The map.resources line that worked for me is:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;map.resources :users, :controller =&gt; 'admin/users', :name_prefix =&gt; 'admin_', :path_prefix =&gt; '/admin'&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Some of the URLs of the resources that are automatically by that line include:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;/admin/users&lt;/code&gt;: list all users (index action)&lt;br /&gt;&lt;code&gt;/admin/user/1&lt;/code&gt;: show one user's details (show action)&lt;br /&gt;&lt;code&gt;/admin/user/new&lt;/code&gt;: create a new user (new action)&lt;br /&gt;&lt;br /&gt;When you want to use the &lt;code&gt;*_path&lt;/code&gt; or &lt;code&gt;*_url&lt;/code&gt; shortcuts for these nested controllers, these work:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;admin_users_path&lt;/code&gt;: renders &lt;code&gt;/admin/users&lt;/code&gt;&lt;br /&gt;&lt;code&gt;admin_edit_user_path(@user)&lt;/code&gt;: renders &lt;code&gt;/admin/user/1;edit&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Rails is opinionated, but once you figure out how to agree with its opinion of how things should be named, it makes the rest of the development process a lot easier.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-1404979695730699700?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/1404979695730699700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=1404979695730699700' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1404979695730699700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/1404979695730699700'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/05/restful-rails-naming-conventions-with.html' title='RESTful Rails Naming Conventions with Nested Controllers'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-4919572292356193268</id><published>2007-04-23T06:26:00.000-07:00</published><updated>2007-04-28T13:36:47.685-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='ferret'/><title type='text'>Ruby, mongrel_cluster, ferret and Linux PATHs</title><content type='html'>I have less hair.  There's no two ways about it.  In a fit of self-pity this weekend, I considered getting some of that &lt;a href="http://www.onlyhairloss.com/glh/"&gt;great spray paint&lt;/a&gt; to solve the issue, but instead, I've decided to swear off various un*x distributions.  Can't we all just get along?  I digress.&lt;br /&gt;&lt;br /&gt;While preparing a recent &lt;a href="http://www.rubyonrails.org"&gt;Rails&lt;/a&gt; application for production, I came across some odd issues with various ruby-powered applications and &lt;a href="http://docs.rubygems.org"&gt;gems&lt;/a&gt;.  I struggled to understand why these apps failed to launch on startup, but would work without a hitch when run "by hand" at the command prompt.   After tearing through boot logs, application logs and a good portion of my hair, I finally figured it out:  it's a &lt;code&gt;PATH&lt;/code&gt; issue.  On each of the boxes in question (a few Fedora Core and two RHE4 machines), &lt;a href="http://www.ruby-lang.org"&gt;ruby&lt;/a&gt; was installed in &lt;code&gt;/usr/local/bin&lt;/code&gt;, and that, my friends, is not in the default &lt;code&gt;PATH&lt;/code&gt; for root.  So, I thought, "Easy fix.  Simply add &lt;code&gt;/usr/local/bin&lt;/code&gt; to the &lt;code&gt;PATH&lt;/code&gt; in a system wide script like &lt;code&gt;/etc/profile&lt;/code&gt;."  Alas, t'was not to be.  These newer incarnations of &lt;a href="http://www.redhat.com"&gt;RedHat linux&lt;/a&gt; have the added security of SELinux, which, among many positive things, sets and unsets root's &lt;code&gt;PATH&lt;/code&gt; multiple times during the boot sequence.  In the end, we had to add &lt;code&gt;/usr/local/bin&lt;/code&gt; directly within each component's startup script.  The Rails (and gems) components we are using are &lt;a href="http://mongrel.rubyforge.org"&gt;mongrel&lt;/a&gt;, &lt;a href="http://mongrel.rubyforge.org/docs/mongrel_cluster.html"&gt;mongrel_cluster&lt;/a&gt;, and &lt;a href="http://ferret.davebalmain.com/trac/"&gt;ferret&lt;/a&gt; (using the DRb server).  For those keeping score at home, that's a 9.7 on the new technology in production (NTIP) scale.  So you can keep your hair in tact, here are the startup scripts we're using.&lt;br /&gt;&lt;br /&gt;Mongrel cluster:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;#&lt;br /&gt;# Copyright (c) 2006 Bradley Taylor, bradley@railsmachine.com&lt;br /&gt;#&lt;br /&gt;# mongrel_cluster       Startup script for Mongrel clusters.&lt;br /&gt;#&lt;br /&gt;# chkconfig: - 85 15&lt;br /&gt;# description: mongrel_cluster manages multiple Mongrel processes for use \&lt;br /&gt;#              behind a load balancer.&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;CONF_DIR=/etc/mongrel_cluster&lt;br /&gt;RETVAL=0&lt;br /&gt;PATH=/usr/local/bin:$PATH&lt;br /&gt;&lt;br /&gt;# Gracefully exit if the controller is missing.&lt;br /&gt;which mongrel_cluster_ctl &gt;/dev/null || exit 0&lt;br /&gt;&lt;br /&gt;# Go no further if config directory is missing.&lt;br /&gt;[ -d "$CONF_DIR" ] || exit 0&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt;  start)&lt;br /&gt;    mongrel_cluster_ctl start -c $CONF_DIR&lt;br /&gt;    RETVAL=$?&lt;br /&gt;;;&lt;br /&gt;  stop)&lt;br /&gt;    mongrel_cluster_ctl stop -c $CONF_DIR&lt;br /&gt;    RETVAL=$?&lt;br /&gt;;;&lt;br /&gt;  restart)&lt;br /&gt;    mongrel_cluster_ctl restart -c $CONF_DIR&lt;br /&gt;    RETVAL=$?&lt;br /&gt;;;&lt;br /&gt;  *)&lt;br /&gt;    echo "Usage: mongrel_cluster {start|stop|restart}"&lt;br /&gt;    exit 1&lt;br /&gt;;;&lt;br /&gt;esac&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ferret DRb server:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;#&lt;br /&gt;# This script starts and stops the ferret DRb server&lt;br /&gt;# chkconfig: 2345 89 36&lt;br /&gt;# description: Ferret search engine for ruby apps.&lt;br /&gt;#&lt;br /&gt;# Sean Brown, Partner at Barefoot, Inc.&lt;br /&gt;#&lt;br /&gt;# save the current directory&lt;br /&gt;CURDIR=`pwd`&lt;br /&gt;PATH=/usr/local/bin:$PATH&lt;br /&gt;&lt;br /&gt;RORPATH="/path/to/ror_root"&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt; start)&lt;br /&gt;    cd $RORPATH&lt;br /&gt;    echo "Starting ferret DRb server."&lt;br /&gt;    FERRET_USE_LOCAL_INDEX=1 \&lt;br /&gt;               script/runner -e production \&lt;br /&gt;               vendor/plugins/acts_as_ferret/script/ferret_start&lt;br /&gt;    ;;&lt;br /&gt; stop)&lt;br /&gt;    cd $RORPATH&lt;br /&gt;    echo "Stopping ferret DRb server."&lt;br /&gt;    FERRET_USE_LOCAL_INDEX=1 \&lt;br /&gt;               script/runner -e production \&lt;br /&gt;               vendor/plugins/acts_as_ferret/script/ferret_stop&lt;br /&gt;    ;;&lt;br /&gt; *)&lt;br /&gt;    echo $"Usage: $0 {start, stop}"&lt;br /&gt;    exit 1&lt;br /&gt;    ;;&lt;br /&gt;esac&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-4919572292356193268?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/4919572292356193268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=4919572292356193268' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/4919572292356193268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/4919572292356193268'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/04/ruby-mongrelcluster-ferret-and-linux.html' title='Ruby, mongrel_cluster, ferret and Linux PATHs'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-3792982280747124476</id><published>2007-04-12T14:42:00.000-07:00</published><updated>2007-04-12T11:47:00.245-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XHTML'/><category scheme='http://www.blogger.com/atom/ns#' term='recruiting'/><category scheme='http://www.blogger.com/atom/ns#' term='CSS'/><title type='text'>We're hiring a UI Developer (XHTML/CSS)</title><content type='html'>&lt;a href="http://www.thinkbarefoot.com"&gt;We&lt;/a&gt; have just posted &lt;a href="http://jobs.37signals.com/jobs/1394"&gt;a position&lt;/a&gt; on &lt;a href="http://www.37signals.com"&gt;37Signals&lt;/a&gt; job board for an experience UI Developer.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;Barefoot has an immediate opening for an experienced User Interface developer. Strong skills in XHMTL and CSS is a must. Significant experience in Javascript is a big plus. To be considered as a candidate, please submit at least 5 URLs for which you were either the only UI developer or at least the principal developer. No phone calls please.&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;So, all you CSS wizards, this is your dream job. Cool agency. Hip modern design. And you get to bring it to life. Not to mention the development staff is cool too (I'm a little biased). If you understand server-side programming, that is a bonus. If you know some Flash/Actionscript that is a big bonus as well. The big chunk is XHTML/CSS. We're talking about browser compatibility, W3C standards, etc... We want to implement standards and if you know them and know how to code them, then we want you. We work with some amazing clients, including Miller Brewing Company, KnowledgeWorks Foundation, and many many other large and small companies. This is a cool position.&lt;br /&gt;&lt;br /&gt;Send your resume to &lt;a href="mailto:recruiting@thinkbarefoot.com"&gt;recruiting@thinkbarefoot.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Michael Krisher, Senior Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-3792982280747124476?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/3792982280747124476/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=3792982280747124476' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3792982280747124476'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/3792982280747124476'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/04/we-hiring-ui-developer-xhtmlcss.html' title='We&amp;#39;re hiring a UI Developer (XHTML/CSS)'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-6226648306539198599</id><published>2007-03-09T11:22:00.000-08:00</published><updated>2007-04-27T18:53:02.267-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Regex to display X words in Rails</title><content type='html'>I just created a useful combination of regex's in &lt;a href="www.rubyonrails.com"&gt;Ruby on Rails&lt;/a&gt; and wanted to share (and remember).  I needed to display a summary of an article, as the first group of words from a string that might contain HTML, start with garbage, or otherwise not be pretty.  &lt;br /&gt;&lt;br /&gt;So, I stripped the HTML, removed non-printable stuff from the beginning, and came up with this to show the first 40 words of the article's body: &lt;br /&gt;&lt;br /&gt;&lt;code&gt;article.body.gsub(/&lt;\/?[^&gt;]*&gt;/, "").gsub(/^\W*/, "")[/([\S]*[\W]*){40}/]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Enjoy!  (Or, leave a comment with a better way to do it.)&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-6226648306539198599?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/6226648306539198599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=6226648306539198599' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/6226648306539198599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/6226648306539198599'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/03/regex-to-display-x-words-in-rails.html' title='Regex to display X words in Rails'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-5275313340265852932</id><published>2007-03-01T09:58:00.000-08:00</published><updated>2007-03-01T10:31:40.262-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>The Joy of Rails Automated Testing</title><content type='html'>Many developers are not in the habit of adding unit and functional tests to their projects.  With many frameworks, testing is a tedious addition that can get pushed aside because of time constraints.  However, with &lt;a href="http://www.rubyonrails.com/"&gt;Ruby on Rails&lt;/a&gt;, automated testing is baked right in, so there's no excuse for not using it.  And, once you start using unit &amp; functional tests, you will sleep better, have less stress, exercise more, lose weight, improve your marriage, and have more money to save for retirement.  Well, all of those benefits may not come directly from automated testing, but you'll certainly build a much more high-quality application, and that can't hurt the rest of your life!&lt;br /&gt;&lt;br /&gt;I'm not going to go in-depth on the basics of unit/functional testing here ... many other resources do a great job of that, such as the excellent &lt;a href="http://www.pragmaticprogrammer.com/titles/rails/index.html"&gt;Agile Web Development with Rails&lt;/a&gt;.  However, I wanted to show a simple &amp;amp; quick way to add functional tests in response to daily development problems.&lt;br /&gt;&lt;br /&gt;It happens all the time during development: you build a controller with a page and see "Action Controller: Exception caught" when you view it in your browser.  You immediately move to the task of fixing whatever problem is revealed.&lt;br /&gt;&lt;br /&gt;However, it only takes an extra minute to add a new functional test that will give you the added assurance that once you fix the problem, &lt;span style="font-style: italic;"&gt;you'll never be surprised by it again&lt;/span&gt; because your test will automatically tell you if it happens again.&lt;br /&gt;&lt;br /&gt;They key is in the very nice display that Rails renders when an exception is caught.  Immediately following the error trace, Rails displays the current Request parameters.  For example, I just received an error on a seach page with parameters like this:&lt;br /&gt;&lt;h3&gt;Request&lt;/h3&gt;&lt;b&gt;Parameters&lt;/b&gt;: &lt;code&gt;{"commit"=&gt;"Search", "office_id"=&gt;"0", "school_name"=&gt;nil, "first_name"=&gt;nil, "last_name"=&gt;nil}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The parameters are in a hash that is in the exact form needed by the get or post methods you use within a Rails functional test.  So here's what you do:&lt;br /&gt;&lt;br /&gt;1. Create a new method within the functional test file for your controller, something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;def test_search_empty_params&lt;br /&gt;&amp;nbsp;# Use all empty fields&lt;br /&gt;&amp;nbsp;get :search&lt;br /&gt;&amp;nbsp;assert_response :success&lt;br /&gt;&amp;nbsp;assert_template 'search'&lt;br /&gt;&amp;nbsp;assert_not_nil assigns(:search_params)&lt;br /&gt;&amp;nbsp;assert_not_nil assigns(:results)&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;2. Copy &amp; paste the list of request parameters from the error page in your browser to the second parameter of the get method (this also works with the post method), so your test method ends up like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;def test_search_empty_params&lt;br /&gt;&amp;nbsp;# Use all empty fields&lt;br /&gt;&amp;nbsp;get :search, {"commit"=&gt;"Search", "office_id"=&gt;"0", "school_name"=&gt;nil, "first_name"=&gt;nil, "last_name"=&gt;nil}&lt;br /&gt;&amp;nbsp;assert_response :success&lt;br /&gt;&amp;nbsp;assert_template 'search'&lt;br /&gt;&amp;nbsp;assert_not_nil assigns(:search_params)&lt;br /&gt;&amp;nbsp;assert_not_nil assigns(:results)&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, go on to fix whatever caused the problem in the first place, and run your functional test to not only make sure your problem is fixed, but that your fix didn't break anything else.  Now, you'll be sure that the problem will never show up again, at least, not without a message from your test.  Doesn't that bring you joy?&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-5275313340265852932?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/5275313340265852932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=5275313340265852932' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/5275313340265852932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/5275313340265852932'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/03/joy-of-rails-automated-testing.html' title='The Joy of Rails Automated Testing'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-117148383074268513</id><published>2007-02-14T12:10:00.000-08:00</published><updated>2007-02-16T09:47:41.333-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think Barefoot'/><category scheme='http://www.blogger.com/atom/ns#' term='Actionscript'/><category scheme='http://www.blogger.com/atom/ns#' term='Packages'/><category scheme='http://www.blogger.com/atom/ns#' term='Flash'/><category scheme='http://www.blogger.com/atom/ns#' term='Classes'/><title type='text'>Flash: Classes and Packages</title><content type='html'>At Barefoot we have many different kinds of developers. Some come from a Java background. Some have been using Flash since Macromedia purchased it and released it as Flash Version 3. In addition, we work with developers from other agencies or from our clients and that requires project portability.&lt;br /&gt;&lt;br /&gt;In this case, portability means the ability to package up a project with all of its pieces and deliver it to someone else in a way that they can begin working on the project without problems caused by missing dependencies.&lt;br /&gt;&lt;br /&gt;We also use Subversion as a version control system. One way we use Subversion is to deliver project packages. Because of the way Subversion works, we have to be concious of file system restraints. The majority of the time, this means all of our project files need to live under a main parent directory.&lt;br /&gt;&lt;br /&gt;This brings us to the organization of files that make up our projects, in this case Flash projects.&lt;br /&gt;&lt;br /&gt;At Barefoot, we borrow a practice from the Java paradigm called Classes and Packages. For in-depth information about using Classes and Packages in Flash, check out Collin Moock's O'Reilly book–Essential Actionscript 2.0 - &lt;a href="http://moock.org/eas2/"&gt;http://moock.org/eas2/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In simple terms, Classes are external Actionscript files. Classes are how logic for your Flash application is separated and stored.&lt;br /&gt;&lt;br /&gt;Packages are simply a set or group of interrelated classes in a file structure.&lt;br /&gt;&lt;br /&gt;So for example, say Barefoot has created a custom Video Player to play FLV files.&lt;br /&gt;&lt;br /&gt;We would have a Flash directory to hold all of our files related to the project. This includes any Classes. Default Macromedia Classes can stay in their default directory since we assume anyone else working on this project has Flash, including those core Classes. We also don't add Classes to the default Macromedia directory, all of our custom Classes stay with the project–including our FLA file.&lt;br /&gt;&lt;br /&gt;Inside our Flash directory we'll store our FLA(s). We'll also want to store our Packages, remembering that they are just groups of our Classes that are interrelated.&lt;br /&gt;&lt;br /&gt;This is where we borrow from Java and other mature language paradigms. We don't want our Packages, and the Classes inside them, to interfere with code from other developers. So, we store them in unique namespaces. The standard is to start this namespace with our company's domain name.&lt;br /&gt;&lt;br /&gt;Inside the Flash directory with our FLA is a "Classes" directory.  This becomes the root directory of the classpath, or the place where we tell Flash to look for any classes it needs to find.  In "Classes", we add a "com" directory. And inside that is a "thinkbarefoot" subdirectory. Get it, &lt;a href="http://thinkbarefoot.com/"&gt;thinkbarefoot.com&lt;/a&gt;? Now going back to the example, we'll create a "videoplayer" subdirectory inside "thinkbarefoot." Again, this is all covered in Moock's book.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;\flash&lt;br /&gt; |&lt;br /&gt; +--\Classes&lt;br /&gt; |     |&lt;br /&gt; |     ---\com&lt;br /&gt; |          |&lt;br /&gt; |          ---\thinkbarefoot&lt;br /&gt; |                |&lt;br /&gt; |                ---\videoplayer&lt;br /&gt; |                      |&lt;br /&gt; |                      +--Player.as&lt;br /&gt; |                      +--Loader.as&lt;br /&gt; |&lt;br /&gt; +---video_player.fla&lt;br /&gt;&lt;/pre&gt;Inside the "videoplayer" directory will be all of our Classes (*.as), along with maybe some other subdirectories if we break things out further.&lt;br /&gt;&lt;br /&gt;Organizing our files like this has two advantages. First, a check-in to Subversion only has to contain one parent directory. Then, if we deliver the project to someone else, all we have to do is zip the Flash directory and they'll have everything they need. That portability is essential when you are working with multiple developers or sending your code to another company.&lt;br /&gt;&lt;br /&gt;Without this level of file organization, you may deliver an FLA and some of your logic, but forget one key Class that prevents a future developer from compiling a quick text fix.  Adobe has made Actionscript 2.0+ very friendly for use with Classes and Packages.  Give them a try, you'll be happy you did.&lt;br /&gt;&lt;br /&gt;Mike Krisher, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-117148383074268513?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/117148383074268513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=117148383074268513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/117148383074268513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/117148383074268513'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/02/flash-classes-and-packages.html' title='Flash: Classes and Packages'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-116913902615743576</id><published>2007-01-18T08:47:00.000-08:00</published><updated>2007-04-28T13:37:34.776-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Flash'/><title type='text'>SlideShowPro - XML attributes</title><content type='html'>I am using the &lt;a href="http://www.slideshowpro.net/"&gt;SlideShowPro&lt;/a&gt; component in a Flash piece I am working on and so far it has been a great experience. This is a pretty solid as Sears component.&lt;br /&gt;&lt;br /&gt;However, I have the need to extend the XML schema and include additional attributes on my image nodes. It can be done no problem, as discussed &lt;a href="http://www.slideshowpro.net/forums/viewtopic.php?id=1487"&gt;here&lt;/a&gt; on the SlideShowPro forums. BUT, I noticed that the component converts all attribute names to lowercase.&lt;br /&gt;&lt;br /&gt;So, say you want to add an attribute containing the path to download the image, call it "downloadPath." Notice the uppercase P. When retrieving that attribute via the data Object created by SlideShowPro (eventObject.data) the name has to be all lowercase (i.e. eventObject.data.downloadpath). I'm going to stick to just using all lowercase attribute names in the XML to thwart off confusion.&lt;br /&gt;&lt;br /&gt;I was going to add this as a reply in the forums, however, they seem to be closed to new registrations?&lt;br /&gt;&lt;br /&gt;Michael Krisher, Senior Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;br /&gt;&lt;i&gt;cross posted &lt;a href="http://www.mikekrisher.com/?p=436"&gt;here&lt;/a&gt;&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-116913902615743576?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/116913902615743576/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=116913902615743576' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116913902615743576'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116913902615743576'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/01/slideshowpro-xml-attributes.html' title='SlideShowPro - XML attributes'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-116778189983402971</id><published>2007-01-17T08:45:00.000-08:00</published><updated>2007-04-27T18:52:11.929-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Custom Sorting in Ruby</title><content type='html'>I have been an expert Java developer for many years, but over the past year I've had the opportunity to use Ruby on Rails for a couple real projects.  I'm really enjoying Ruby and hope to to use it more often in the future.&lt;br /&gt;&lt;br /&gt;My current RoR project has several models with bi-directional &lt;code&gt;has_many :through&lt;/code&gt; relationships where two models link to a third join model that has one or more additional property fields.  For example, Users link to Albums through a Selections join model.  The selections table includes fields for user_id and album_id, with an additional field that contains the rating each user gives to each album.  (This example uses different entity names to protect the client.)  &lt;br /&gt;&lt;br /&gt;These relationships are illustrated (with simplication) here:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class User &lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;has_many :selections, :dependent =&gt; :destroy&lt;br /&gt;&amp;nbsp;&amp;nbsp;has_many :albums, :through =&gt; :selections  &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Album &lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;has_many :selections, :dependent =&gt; :destroy&lt;br /&gt;&amp;nbsp;&amp;nbsp;has_many :users, :through =&gt; :selections  &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Selection &lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;belongs_to :user&lt;br /&gt;&amp;nbsp;&amp;nbsp;belongs_to :album  &lt;br /&gt;end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I needed to be able to list all the albums chosen by a user, sorted by album name.  I tried this:  &lt;br /&gt;&lt;code&gt;&lt;br /&gt;@user.selections.each do |selection|&lt;br /&gt;  # ...&lt;br /&gt;end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;but the album names were not sorted.  I thought that the nifty option &lt;code&gt;:order =&gt; 'name'&lt;/code&gt; would work on the &lt;code&gt;has_many :through&lt;/code&gt;, but alas, it didn't.  Then, my Java experience reminded me of the Comparable interface.  It turns out that Ruby includes a very similar pattern, but it comes complete with Ruby nice-ness.&lt;br /&gt;&lt;br /&gt;When you sort an Array (or anything Enumerable), you can override the &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; method of the Object class to provide your own custom sorting code.  (Just like the equals() method in Java.)  The &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; method returns -1, 0, or 1 to indicate whether the instance is smaller, equal, or greater than the other object.  Here's the method I added to the Selection model to tell it how to sort:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  def &lt;=&gt;(o)  &lt;br /&gt;&amp;nbsp;&amp;nbsp; # Compare album name&lt;br /&gt;&amp;nbsp;&amp;nbsp; album_name_cmp = self.album.name &lt;=&gt; o.album.name&lt;br /&gt;&amp;nbsp;&amp;nbsp; return album_name_cmp unless album_name_cmp == 0&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp; # Compare user last name&lt;br /&gt;&amp;nbsp;&amp;nbsp; user_ln_cmp = self.user.last_name &lt;=&gt; o.user.last_name&lt;br /&gt;&amp;nbsp;&amp;nbsp; return user_ln_cmp unless user_ln_cmp == 0&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp; # Compare user first name&lt;br /&gt;&amp;nbsp;&amp;nbsp; user_fn_cmp = self.user.first_name &lt;=&gt; o.user.first_name&lt;br /&gt;&amp;nbsp;&amp;nbsp; return user_fn_cmp unless user_fn_cmp == 0&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp; # Otherwise, compare IDs&lt;br /&gt;&amp;nbsp;&amp;nbsp; return self.id &lt;=&gt; o.id&lt;br /&gt;  end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then, just change the block slightly to use the sorted version:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;@user.selections.sort.each do |selection|&lt;br /&gt;  # ...&lt;br /&gt;end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If the model you're trying to sort is not already Comparable, you should add the line &lt;code&gt;include Comparable&lt;/code&gt; to include the Comparable mixin.  ActiveRecord objects are already Comparable.  &lt;br /&gt;&lt;br /&gt;Have fun writing your own custom sorting rules in Ruby!  Leave a comment if you need more details.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-116778189983402971?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/116778189983402971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=116778189983402971' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116778189983402971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116778189983402971'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2007/01/custom-sorting-in-ruby.html' title='Custom Sorting in Ruby'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-116198270001566480</id><published>2006-10-27T13:57:00.000-07:00</published><updated>2007-04-28T13:37:24.111-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEO'/><title type='text'>Popups a Spider Can Follow</title><content type='html'>While talking with our team about SEO best practices, we discovered that the way &lt;a href="http://www.rubyonrails.org" alt="Ruby on Rails"&gt;Rails&lt;/a&gt; handles popups makes them not only more user-friendly (like allowing the ability to open the popup in a new full window or in a tab), but also allows a search engine spider to see the link and follow it to index the content of the popup.&lt;br /&gt;&lt;br /&gt;Here is an example of what you may be used to seeing from a &lt;a href="http://en.wikipedia.org/wiki/WYSIWYG" title="What does WYSIWYG mean?"&gt;WYSIWYG&lt;/a&gt; editor when popping a new window:&lt;br /&gt;&lt;code&gt;&lt;br /&gt; &amp;lt;script type="text/JavaScript"&amp;gt;&lt;br /&gt; &amp;lt;!--&lt;br /&gt; function MM_openBrWindow(theURL,winName,features) { //v2.0&lt;br /&gt;   window.open(theURL,winName,features);&lt;br /&gt; }&lt;br /&gt; //--&amp;gt;&lt;br /&gt; &amp;lt;/script&amp;gt;&lt;br /&gt; &amp;lt;a href="#"&amp;gt;&amp;lt;img src="path/to/a.gif" width="124" height="45" border="0" onClick="MM_openBrWindow('path/to/page','','width=900,height=570')"&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;And this is how we are popping new windows (with the help of Rails):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;a href="/path/to/page" onclick="window.open(this.href,'event_detail','height=450,width=700');return false;"&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Since the HREF is intact, spiders are able to follow the links. Easy, peasy.&lt;br /&gt;&lt;br /&gt;Bobby Uhlenbrock, Application Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-116198270001566480?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/116198270001566480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=116198270001566480' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116198270001566480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/116198270001566480'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/10/popups-spider-can-follow.html' title='Popups a Spider Can Follow'/><author><name>Bobby</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/29/buddyicons/77418348@N00.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115522658144699611</id><published>2006-08-10T09:16:00.000-07:00</published><updated>2007-04-28T13:37:05.543-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Flash'/><title type='text'>Flash sortOn coolness ...</title><content type='html'>It's always fun to discover nice little surprises in function lists.  I had such an experience when I recently found the &lt;code&gt;sortOn()&lt;/code&gt; function within Flash's Array class.&lt;br /&gt;&lt;br /&gt;I come from a Java background, and Java's Collections API includes very sophisticated hooks to sort objects in any number of ways.  I didn't expect to find that in ActionScript.  So, when I had a list of objects that I needed to sort, I was pleasantly surprised to find &lt;code&gt;sortOn()&lt;/code&gt;, which sorts an Array of objects.&lt;br /&gt;&lt;br /&gt;I frequently create value objects to represent things in Flash applications.  In one application, I'm placing a number of animals on the stage.  The environment is kinda 3D, so animals that have a lower Y axis value are farther away in the distance, while higher Y values are closer to the viewer.  The Y axis value is randomly determined, so the order is not pre-determined.&lt;br /&gt;&lt;br /&gt;Here's a simplified version of the Animal value object with only the properties relevant to this post:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Animal &lt;br /&gt;{&lt;br /&gt;   var initX:Number;&lt;br /&gt;   var initY:Number;&lt;br /&gt;   var initScale:Number;&lt;br /&gt;   var animalType:String;&lt;br /&gt;   var animalName:String;&lt;br /&gt;   // more props&lt;br /&gt;&lt;br /&gt;   function init():Void &lt;br /&gt;   { &lt;br /&gt;      // Init stuff ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The application reads user-defined animals from the server, and randomly places these animals on the stage.  I keep all the Animal objects in an array for later use.&lt;br /&gt;&lt;br /&gt;So, I knew that in Java I could sort these Animal objects by implementing the Comparable interface.  I was pretty sure I couldn't write a Comparator in ActionScript.  But, I decided to check out the Array object just in case.&lt;br /&gt;&lt;br /&gt;Then, I found the &lt;a href="http://livedocs.macromedia.com/flash/8/main/00001916.html" target="_blank"&gt;sortOn() function&lt;/a&gt;.  Its whole purpose is to let you sort arrays of objects.  It has several flavors which you can read about at the link above, but the one relevant to this lets you pass in one or more fields (object properties), and an optional sort type parameter.  &lt;br /&gt;&lt;br /&gt;Here's an example:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var animals:Array = loadAnimals();&lt;br /&gt;animals.sortOn(["initY"], Array.NUMERIC);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Since initY is a property of every object in the animals array, the &lt;code&gt;sortOn()&lt;/code&gt; function sorts the objects on that field.  By default, &lt;code&gt;sortOn()&lt;/code&gt; sorts in ASCII order, which is fine for strings, but bad for numbers since 100 would be sorted before 99.  So, ActionScript offers options like the Array.NUMERIC constant shown above, which tells &lt;code&gt;sortOn()&lt;/code&gt; to sort the properties as numbers.&lt;br /&gt;&lt;br /&gt;So, thanks ActionScript team for putting that in there -- it was a nice surprise!&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115522658144699611?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115522658144699611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115522658144699611' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115522658144699611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115522658144699611'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/08/flash-sorton-coolness.html' title='Flash sortOn coolness ...'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115267422946888913</id><published>2006-07-11T19:52:00.000-07:00</published><updated>2007-04-27T18:52:11.930-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Automating Rails deployment without Capistrano</title><content type='html'>Certainly Capostrano is the most elegant way to deploy your Rails apps, but here's an easy way to automate Rails deployment using just rync and rake.  And the best part is that it borrows from a &lt;a href="http://barefootdevelopment.blogspot.com/2006/05/public-key-ssh-authentication.html"&gt;previous blog entry&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This first step is optional, but boy will it make your life easier.  Start by setting up public key authentication between your local development machine and your server.  &lt;a href="http://barefootdevelopment.blogspot.com/2006/05/public-key-ssh-authentication.html"&gt;We've covered this ground before&lt;/a&gt;, so just make sure you're doing it for the account on your server that you'd normally use to FTP to your Rails application.&lt;br /&gt;&lt;br /&gt;The second, and final, step:  using rync with rake.  To see full coverge of this topic (or how to do it if you're developing on a Windows machine, &lt;a href="http://wiki.rubyonrails.com/rails/pages/HowtoUseRsyncToDeployYourApplication"&gt;see the HOWTO&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Add the following block to the Rakefile in your local application directory:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;desc "Deploy basic application directories"&lt;br /&gt;task :deploy =&gt; :environment do&lt;br /&gt;dirs = %w{ app lib test public config}&lt;br /&gt;onserver = "login@remotehost:/home/rails-app-directory/"&lt;br /&gt;dirs.each do | dir|&lt;br /&gt; `rsync -avz -e ssh "#{RAILS_ROOT}/#{dir}" "#{onserver}" --exclude ".svn"`&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;You'll need to change the onserver line to use your actual username and hostname.  Then at the command line, change to your Rails application directory and run:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$ rake deploy&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;That's it.  Enoy your new found productivity.&lt;br /&gt;&lt;br /&gt;Sean Brown, Partner, Technology at &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115267422946888913?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115267422946888913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115267422946888913' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115267422946888913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115267422946888913'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/07/automating-rails-deployment-without.html' title='Automating Rails deployment without Capistrano'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115220334815000403</id><published>2006-07-06T09:28:00.000-07:00</published><updated>2007-11-27T12:50:16.068-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Actionscript'/><category scheme='http://www.blogger.com/atom/ns#' term='Flash'/><title type='text'>Concerning Media Components and Cue Points</title><content type='html'>On a recent Flash project (version 7) with talking characters, it became necessary to start and stop animations at specific points during the playback of a sound file. We decided that the most efficient way to manage the calls was via CuePoints which are available in MX 2004 Pro through the Media components. &lt;br /&gt;&lt;br /&gt;I placed an instance of the MediaDisplay component on the stage and initialized it thusly:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;var myMedia = _root.my_media_component;&lt;br /&gt;myMedia.autoPlay = false;&lt;br /&gt;&lt;br /&gt;// Load some sound into the component&lt;br /&gt;// which has been preloaded&lt;br /&gt;myMedia.setMedia("some.mp3", MP3);&lt;br /&gt;&lt;br /&gt;// Add CuePoints to the component&lt;br /&gt;myMedia.addCuePoint("start", 7);&lt;br /&gt;myMedia.addCuePoint("stop", 19);&lt;br /&gt;&lt;br /&gt;// Create a Cue Point Listener object&lt;br /&gt;var cuePointListener:Object = new Object();&lt;br /&gt;&lt;br /&gt;// Call this method when a CuePoint is "heard"&lt;br /&gt;cuePointListener.cuePoint = function(eventObject):Void {&lt;br /&gt; // call the function with the CuePoint's name&lt;br /&gt; handleCuePoint(eventObject.target.mostRecentCuePointName);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Call this method when the sound is complete&lt;br /&gt;cuePointListener.complete = function(eventObject):Void {&lt;br /&gt; trace("I'm all done.");&lt;br /&gt; // do some stuff&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Associate/attach the listers to the component&lt;br /&gt;myMedia.addEventListener("cuePoint",cuePointListener);&lt;br /&gt;myMedia.addEventListener("complete", cuePointListener);&lt;br /&gt;&lt;br /&gt;// Method to handle each CuePoint&lt;br /&gt;// @param myCue: The name of the "heard" CuePoint&lt;br /&gt;function handleCuePoint(myCue:String):Void {&lt;br /&gt; switch (myCue) {&lt;br /&gt;  case "start" :&lt;br /&gt;   trace("Start me up.");&lt;br /&gt;   // start some stuff&lt;br /&gt;   break;&lt;br /&gt;  case "stop" :&lt;br /&gt;   trace("Stop it, now.");&lt;br /&gt;   // stop some stuff&lt;br /&gt;   break;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Now that everything is ready to go, let's play!&lt;br /&gt;myMedia.play();&lt;br /&gt;trace("The beginning.");&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Okay, that was pretty simple -- the sound is playing great and the CuePoints are called in time. But wait, what was that? The sound finished and my CuePoints just fired again. Let's take a look at the output:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;The beginning&lt;br /&gt;Start me up.&lt;br /&gt;Stop it, now.&lt;br /&gt;Start me up.&lt;br /&gt;Stop it, now.&lt;br /&gt;I'm all done.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So, something is amiss between the completion of the sound and our listener "hearing" the &lt;code&gt;complete&lt;/code&gt;. If we trace &lt;code&gt;myMedia.playheadTime&lt;/code&gt; in the complete function, it returns &lt;code&gt;0&lt;/code&gt;. The Media component is automatically &lt;strong&gt;rewinding&lt;/strong&gt; the sound when it finishes but &lt;em&gt;before&lt;/em&gt; triggering the complete listener. Blockbuster would be proud. This wouldn't be so bad, but the act of rewinding fires the CuePoints as the sound scrubs back to 0. Luckily, the solution is simple. After a CuePoint is heard, just remove it:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt; // Call this method when a CuePoint is "heard"&lt;br /&gt; cuePointListener.cuePoint = function(eventObject):Void {&lt;br /&gt;  // call the function with the CuePoint's name&lt;br /&gt;  handleCuePoint(eventObject.target.mostRecentCuePointName);&lt;br /&gt;  // now that the function above has been called, remove the CuePoint&lt;br /&gt;  myMedia.removeCuePoint(mediaStreamer.getCuePoint(eventObject.target.mostRecentCuePointName));&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Not much to it, but the behavior of the MediaDisplay component is so odd that I just had to share.&lt;br /&gt;&lt;br /&gt;Bobby Uhlenbrock, Application Developer, &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115220334815000403?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115220334815000403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115220334815000403' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115220334815000403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115220334815000403'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/07/concerning-media-components-and-cue.html' title='Concerning Media Components and Cue Points'/><author><name>Bobby</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/29/buddyicons/77418348@N00.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115168815025990797</id><published>2006-06-30T10:13:00.000-07:00</published><updated>2006-06-30T10:30:53.220-07:00</updated><title type='text'>Custom Class Casting in Flash</title><content type='html'>While developing a Flash 8 project using ActionScript 2 classes, I recently created a Tile class that extended MovieClip.  The Tile is a custom scrolling background element that tiles itself for a seamless, endless horizontal scrolling.  &lt;br /&gt;&lt;br /&gt;I created a couple variables, typed as Tile, in my AppState class, the class I use to keep the current state of the Flash application.  They are like so:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// Background movie clip tile references&lt;br /&gt;var tile1:Tile = null;&lt;br /&gt;var tile2:Tile = null;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Now, since we're not yet using ActionScript 3, the only way to place new Tile instances onto the stage is with a call to &lt;code&gt;attachMovie()&lt;/code&gt;.  However, the following call won't compile in AS2:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;app.tile1 = _root.attachMovie(bgndMcName, "bgnd1_mc", app.depth++);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Why does it fail?  Well, the compiler says this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Type mismatch in assignment statement: found MovieClip where com.barefoot.ui.comp.Tile is required.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;So, I thought to myself, I wonder if you can cast objects like you can with the &lt;code&gt;String()&lt;/code&gt; and &lt;code&gt;Number()&lt;/code&gt; functions?  It turns out you can!  This works:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;app.tile1 = Tile(_root.attachMovie(bgndMcName, "bgnd1_mc", app.depth++));&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I'm looking forward to ActionScript 3, where we will be able to instantiate objects to be placed on the stage like you can for any other object:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;app.tile1 = new Tile();&lt;br /&gt;addChild(app.tile1);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;But until then, I'll use this casting method.  It's not rocket science, but I thought it was cool nonetheless.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com" target="_blank"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115168815025990797?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115168815025990797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115168815025990797' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115168815025990797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115168815025990797'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/06/custom-class-casting-in-flash.html' title='Custom Class Casting in Flash'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115051640375141062</id><published>2006-06-16T20:52:00.000-07:00</published><updated>2006-06-16T20:53:23.906-07:00</updated><title type='text'>HTML e-mails</title><content type='html'>OK, so we don't have all of the best ideas.  Here's a great article with some do's and don'ts for HTML e-mail, which e-mail clients support CSS, and techniques to handle image blocking.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.thinkvitamin.com/features/design/html-emails"&gt;HTML Emails - Taming the Beast&lt;/a&gt; by David Greiner&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115051640375141062?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115051640375141062/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115051640375141062' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115051640375141062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115051640375141062'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/06/html-e-mails.html' title='HTML e-mails'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-115032508887159058</id><published>2006-06-14T15:26:00.000-07:00</published><updated>2006-07-10T08:53:17.760-07:00</updated><title type='text'>Flash &amp; SEO, A Home Run for Barefoot - Part 2</title><content type='html'>This post will describe more details about how &lt;a href="http://www.thinkbarefoot.com" target="_blank"&gt;Barefoot&lt;/a&gt; accomplished a high level of search engine optimization (SEO) with a recent all-Flash web site, &lt;a href="http://www.pitchinforbaseball.com" target="_blank"&gt;Pitch In For Baseball&lt;/a&gt;.  (The &lt;a href="http://barefootdevelopment.blogspot.com/2006/05/flash-seo-home-run-for-barefoot-part-1.html"&gt;previous article is here&lt;/a&gt;.)  One unique aspect of this site is not only that the content is indexed in search engines, but that the links that are returned by search engines take you into subsections within the all-Flash web site.  Typical all-Flash web sites don't provide this level of access into the content.&lt;br /&gt;&lt;br /&gt;To start, each "page" or sub-section of content is stored in HTML in a content manager.  In our case, we're using a custom solution we developed using MySQL and JSP.  This HTML includes CSS to format the text, along with links that can navigate within the site and external to the site.  &lt;br /&gt;&lt;br /&gt;Each "page" within the site is indexed using a section &amp; subsection reference.  The structure is &lt;sectionID&gt;_&lt;subsectionID&gt;, so "1_2" points to the content for section 1, subsection 2.&lt;br /&gt;&lt;br /&gt;The site is architected in a Model-View-Controller pattern.  So, all content is accessed by passing data to a single controller. &lt;br /&gt;&lt;br /&gt;The controller can interpret URLs to view pages in two forms:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.pitchinforbaseball.org/pifbweb/?is=2&amp;iss=2" target="_blank"&gt;/pifbweb/?is=2&amp;iss=2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.pitchinforbaseball.org/pifbweb/ui/step_up_to_the_plate/pitch_in_some_green/"&gt;/pifbweb/ui/step_up_to_the_plate/pitch_in_some_green/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Notice, the first URL above passes a section ID and subsection ID directly, while the second URL uses an easily readable, SEO-friendly path.&lt;br /&gt;&lt;br /&gt;The controller parses URLs in either of these two forms so it can locate the correct HTML content from the database.&lt;br /&gt;&lt;br /&gt;To match the SEO-friendly URLs with the section/subsection scheme, all of the possible paths are stored in multi-dimensional arrays.  The controller searches the arrays to find the position of the section and subsection within the arrays.  In the above example, it would locate "step_up_to_the_plate" as the third item in the SECTIONS array, and "pitch_in_some_green" as the third item in the SUB_SECTION array for the chosen section.  (That's where we get is=2, iss=2, zero-based indexing).&lt;br /&gt;&lt;br /&gt;Once the section &amp; subsection IDs are determined, the controller retrieves the HTML content from the database.  We then move to rendering the retrieved content inside the Flash, and in HTML for indexing by search engines.&lt;br /&gt;&lt;br /&gt;To understand how we achieve this, it's important to understand &lt;a href="http://blog.deconcept.com/swfobject/" target="_blank"&gt;SWFObject&lt;/a&gt; at a high level.  SWFObject is the premier method of embedding Flash applications into web pages.  It is a very well-crafted Javascript library that optimizes process of determining the Flash Player version of the current browser, and hiding all that complexity from Flash developers.&lt;br /&gt;&lt;br /&gt;To use SWFObject, you place a &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt; block on the HTML page where you'd like the Flash to display.  If the user has the correct version of Flash Player and all is well, the Flash application will replace any content you put into the &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt;.  Otherwise, content in the &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt; will display and the user will not see the Flash.  This includes "users" of the web page like search engine spiders and other automated processes.  &lt;br /&gt;&lt;br /&gt;Now, back to our application.  Once the controller has retrieved the HTML content, it puts it into the &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt; block where the Flash will display if the user has the right version of the player installed.  It also passes the input section and subsectionID as flashVars to the SWFObject instance which will render the Flash if all is well.  The Flash application will then append the section and subsection IDs to a URL that reads the HTML from the content manager into an HTML text field within the Flash.  &lt;br /&gt;&lt;br /&gt;With this content in the &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt; of each unique URL, we create a site map that includes every possible link, and embed that into the &lt;code&gt;&amp;lt;DIV&amp;gt;&lt;/code&gt; block of the home page URL.  Search engines start there, and can find and index every other page.  You can see all of this if you view &lt;a href="http://www.pitchinforbaseball.com" target="_blank"&gt;http://www.pitchinforbaseball.com&lt;/a&gt; with Javascript turned off -- &lt;a href="http://www.getfirefox.com" target="_blank"&gt;Firefox&lt;/a&gt; makes that very easy.  &lt;br /&gt;&lt;br /&gt;Another detail is that links within the HTML content need to work correctly whether clicked inside the Flash or in the HTML-only version.  To accomplish this, all links to other sections within the site use full URL structure above, like &lt;code&gt;/pifbweb/ui/step_up_to_the_plate/pitch_in_some_green/&lt;/code&gt;.  If the content is being retrieved from Flash, we append a parameter &lt;code&gt;&amp;switchLinksToFlash=true&lt;/code&gt; to the URL which tells the controller to change all these URLS from standard links to an asfunction call.  In this way, all links inside the Flash call an internal function that animates the section/subsection change without reloading the whole Flash application.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com" target="_blank"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-115032508887159058?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/115032508887159058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=115032508887159058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115032508887159058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/115032508887159058'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/06/flash-seo-home-run-for-barefoot-part-2.html' title='Flash &amp; SEO, A Home Run for Barefoot - Part 2'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114866728757763886</id><published>2006-05-26T11:13:00.000-07:00</published><updated>2006-05-30T09:14:29.500-07:00</updated><title type='text'>Designing emails for the inbox</title><content type='html'>At Barefoot, we help our clients with email marketing strategies and in doing so need to know the successes and pitfalls of HTML email practices. HTML email is arguably more difficult than HTML standards compliance in a browser. There are more email clients out there than browsers, and very few of the clients are held to any sort of standards compliance benchmarking.&lt;br /&gt;&lt;br /&gt;For example, Lotus Notes may be great in some of the features it offers and corporations may really benefit from them. However it is absolutely terrible at rendering HTML email. Tables do not display correctly, especially when nested. However some email clients are starting to use the HTML rendering engines that their counterpart browsers are using. For example, Apple Mail uses the &lt;a href="http://webkit.opendarwin.org/"&gt;Webkit&lt;/a&gt; rendering engine that Apple's browser, Safari, uses. There may be small differences, but overall we know that HTML in the Mail client will look similar to what it looks like in Safari. The same could be said about Outlook and Internet Explorer to a degree.&lt;br /&gt;&lt;br /&gt;However Mail clients don't support everything that browsers do, such as full JavaScript implementation, full CSS implementation, and most don't support full plug-in implementation.&lt;br /&gt;&lt;br /&gt;Because of these things, designing an HTML email is not like designing an HTML file to be used on a Web site. We'll discuss some of the differences and key points below:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Use:  Maximum width of whole message: 500 - 640 pixel wide table&lt;/li&gt;&lt;li&gt;Use:  HTML Title in heading&lt;/li&gt;&lt;li&gt;Avoid: Hot red and blue colors&lt;/li&gt;&lt;li&gt;Avoid: Large font sizes&lt;/li&gt;&lt;li&gt;Avoid: Borders around tables&lt;/li&gt;&lt;li&gt;Avoid: Background color other than white.  Some email clients ignore background color.  If background color must be set change it in a &amp;lt;table&amp;gt;, not the &amp;lt;body&amp;gt;&lt;/li&gt;&lt;li&gt;Don't use Javascript&lt;/li&gt;&lt;li&gt;Don't use style sheets&lt;/li&gt;&lt;li&gt;Use absolute references to all web content:  start with http://&lt;/li&gt;&lt;li&gt;Use only standard web fonts  (Arial, Verdana, Times New Roman, etc.)&lt;/li&gt;&lt;li&gt;No DHTML Layers, use Tables instead&lt;/li&gt;&lt;li&gt;No image maps, use complete links on images instead&lt;/li&gt;&lt;/ul&gt;More technically specific points:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;HTML should be written in HTML 3.2 and include the DOCTYPE declaration.&lt;/li&gt;&lt;li&gt;Include a charset in the header.&lt;/li&gt;&lt;li&gt;Keep in mind that HTML Headers get stripped when viewed in Web based email clients like &lt;a href="http://gmail.com"&gt;GMail&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;Other Links:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.emaillabs.com/articles/email_articles/email_marketing_success_22_imperatives.html"&gt;22 Imperatives for Email Marketing Success&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.benchmarkemail.com/res_designing_email.asp"&gt;Create Dynamic Email Marketing Campaigns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.eventme.com/StyleGuide.aspx"&gt;Event Me's Style Guide for Invitations&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114866728757763886?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114866728757763886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114866728757763886' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114866728757763886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114866728757763886'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/designing-emails-for-inbox.html' title='Designing emails for the inbox'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114841430035916482</id><published>2006-05-23T11:52:00.000-07:00</published><updated>2006-05-23T12:58:20.393-07:00</updated><title type='text'>design + development = flash workflow</title><content type='html'>At Barefoot, we do a lot of Flash application development, everything from &lt;a href="http://www.mickeys.com"&gt;interactive Web sites with video&lt;/a&gt; to Flash based games.  Flash projects typically take more time than traditional Web projects, simply because of the robust nature of the medium. Flash introduces motion and statelessness. Flash can certainly be more engaging than "page" based sites. However, because Flash adds more complexity, and usually time, it also adds more need for a solid workflow amongst team members.&lt;br /&gt;&lt;br /&gt;No workflow process is ever complete. They seem to be constantly evolving, partially due to the fact that the technology is constantly evovling. Our workflows revolve around the team members and who starts the project files off. We have experimented with designers starting the project or developers starting the project. Let me explain further. One approach is to let the designers create the FLA and all of it parts, including layout, animation, and some interactivity, then handing off to the developer to tie everything together. The other approach has the developer architecting the application, creating the setup, pieces, and logic, and then handing off to the designer to work their magic on the individual pieces.&lt;br /&gt;&lt;br /&gt;A couple of factors are key to the decision. Is your application going to practice &lt;a href="http://www.google.com/search?q=object+oriented+programming&amp;start=0&amp;amp;amp;ie=utf-8&amp;oe=utf-8&amp;amp;client=firefox-a&amp;rls=org.mozilla:en-US:official"&gt;Object Oriented Programming&lt;/a&gt; theory or more specifically a design pattern like &lt;a href="http://www.google.com/search?q=MVC+design+pattern&amp;amp;start=0&amp;ie=utf-8&amp;amp;amp;oe=utf-8&amp;client=firefox-a&amp;amp;rls=org.mozilla:en-US:official"&gt;MVC&lt;/a&gt;? Or is your application going to use &lt;a href="http://en.wikipedia.org/wiki/Procedural_code"&gt;procedural code&lt;/a&gt; where you could easily add logic along the timeline during key events, matching up with any motion or animation?&lt;br /&gt;&lt;br /&gt;Here are some common scenarios and reasons why you would choose one workflow over another. If you were building a game, you would probably decide to practice OOP. Because of this reason, it would make sense for the developer to start with the project, architect the solution, set up the Classes, and any corresponding symbols. The designer could then work with the library symbols and add any graphics and motion to the piece. A key factor in the ability to do this is specifying the Class for any given symbol in the library, using either the "linkage" property and assigning the ActionScript 2.0 Class or through a process known as &lt;a href="http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&amp;amp;file=00002587.html#wp399250"&gt;registering the object with a class&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, say you were creating an online product demo. This demo, doesn't really have a lot of  interactivity, more like events will need to fire during certain times throughout the demo. Because of this, procedural code can certainly fit the bill and may make development of the application easier. This scenario may lend itself to the designer initiating the project and identifying where logic needs to be added. Our designers know to create an actions layer and identify the first frame where all code will be added so hand off to a developer is very straight forward and logic can be added to this frame in functions and simply called throughout the movie.&lt;br /&gt;&lt;br /&gt;Team members and their understanding of Flash is also very important. At Barefoot we are very lucky to have designers that also know a very good amount of ActionScript. Because of this we can be flexible in the workflow we choose.&lt;br /&gt;&lt;br /&gt;Choosing one workflow for all projects may very well be impossible, so narrowing down the choices to just two may make agency life a whole lot easier. The more familiar your workflow becomes the faster you can deliver for your clients, bring new team members into the fold, and minimize the debugging in the middle.&lt;br /&gt;&lt;br /&gt;Michael Krisher, Senior Developer, &lt;a href="http://www.thinkbarefoot.com/" target="_blank"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114841430035916482?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114841430035916482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114841430035916482' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114841430035916482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114841430035916482'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/design-development-flash-workflow.html' title='design + development = flash workflow'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114832100252597962</id><published>2006-05-22T10:33:00.000-07:00</published><updated>2006-05-22T15:06:15.953-07:00</updated><title type='text'>Automating Development Website Setup</title><content type='html'>Over the years, setting up development and staging environments for our websites had become a tiresome chore, filled with "opportunities" for typos.  So one Saturday evening, I sat down, brushed the dust off my bash scripting skills and wrote a handy little tool for Barefoot.  I know that there are other tools out there, but I found none that acomplished all of our goals:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The tool should be interactive, rather than configuration-driven.&lt;/li&gt;&lt;li&gt;The tool should enforce naming policies our company had put in place.&lt;/li&gt;&lt;li&gt;The tool should integrate with &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt;, our chosen source control system.&lt;/li&gt;&lt;li&gt;To help cut down on misconfiguration caused by typos, some sane defaults should be presented.&lt;/li&gt;&lt;li&gt;The tool should automate the setup of both &lt;a href="http://php.net"&gt;PHP-based&lt;/a&gt; and &lt;a href="http://www.rubyonrails.org/"&gt;Rails-based&lt;/a&gt; applications.&lt;/li&gt;&lt;li&gt;The tool should make developers' lives easier by pre-populating dev sites with Barefoot standard code libraries.  Once the site has been prepopulated, a new Subversion entry is created for the new site.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;I think that's probably enough to ask of any tool.  The script is far too long to go through every line, so I'll just give an overview here, and then &lt;a href="http://www.thinkbarefoot.com/bloghelp/dev_setup.txt"&gt;make the entire script available for download&lt;/a&gt;.  It's pretty well commented.&lt;br /&gt;&lt;br /&gt;Overview of what the script does, in the order it does it:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Ask the user to enter a new hostname, check it against or naming policies using regular expressions.  Fail with useful error if needed.&lt;/li&gt;&lt;li&gt;Ask the user to enter the domain name, give the user a sane default (thinkbarefoot.com), and check it using regular expressions.  We separate the hostname and the domain name becuase Barefoot's standard directory structure for Apache-based sites is &lt;code&gt;/www/domainname/host.domainname&lt;/code&gt;.  Whenever we say we're giving a "sane default" we mean that the user can simply hit the enter key to select it.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Choose an FTP username and password for the dev site.   GSD, FWUEIN (give sane default, fail with useful error if needed).&lt;/li&gt;&lt;li&gt;Choose a Subversion repository name.  Check it against our naming convention. GSD, FWUEIN.&lt;/li&gt;&lt;li&gt;Choose either a Rails-based project or PHP-based.&lt;/li&gt;&lt;li&gt;We have a "skeleton" directory for each of the two development types.  Depending which one is chosen, we create a new user, make that user's home directory be the result of #2 above, and populate that home directory with the contents of the correct skeleton directory.&lt;/li&gt;&lt;li&gt;Add a new virtual host to the Apache configuration.  The correct entries are put in place for each of the two project types.  This is done using sed to take a template file we've created for each and replacing the info gathered above.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Next, we check out our standard librabries from Subversion and place them in appropriate places in the new directory structure.&lt;/li&gt;&lt;li&gt;Now that the new site is put together, we wrap it all up into the Barefoot standard directory structure for Subversion, and check the new module into our Subversion server.  Keen observers will note that we use the &lt;a href="http://barefootdevelopment.blogspot.com/2006/05/public-key-ssh-authentication.html"&gt;password-less authentication for SSH discussed in our blog here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Finally, we set the correct ownership on the files and clean up some extraneous directories made simply to make a nice Subversion module.&lt;/li&gt;&lt;li&gt;As the script exits, it reminds the user to add the new host to our internal DNS and spits out the login information for easy copy and paste into your preferred login notes.&lt;/li&gt;&lt;/ol&gt;While this hasn't revolutionized our lives, it has certainly cut down on the time spent setting up new development sites.  By using a couple of choice templates, skeleton directories, and Subversion we've been able to keep our setup &lt;span style="font-style: italic;"&gt;much&lt;/span&gt; more consistent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114832100252597962?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114832100252597962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114832100252597962' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114832100252597962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114832100252597962'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/automating-development-website-setup.html' title='Automating Development Website Setup'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114772847295967018</id><published>2006-05-15T14:23:00.000-07:00</published><updated>2006-05-15T14:28:50.360-07:00</updated><title type='text'>Debugging J2EE Technology apps on JRun and Eclipse</title><content type='html'>I'm currently using &lt;a href="http://www.eclipse.org/" target="_blank"&gt;Eclipse 3.1.1&lt;/a&gt; on Adobe (formerly Macromedia) &lt;a href="http://www.adobe.com/products/jrun/" target="_blank"&gt;JRun 4.0&lt;/a&gt; for a database-intensive project update.  In order to debug some complex Java class business logic, I wanted to run the JRun app inside Eclipse's debugger.  Here's how you can set it up in a similar way:&lt;br /&gt;&lt;br /&gt;1. Choose the project in Eclipse that contains the class files to debug&lt;br /&gt;2. Make sure that the jrun.jar file is on your project's classpath&lt;br /&gt;3. Choose Run -&gt; Run...&lt;br /&gt;4. Click the Java Application configuration, then the New button.&lt;br /&gt;5. In the main class, enter "jrunx.kernel.JRun".&lt;br /&gt;6. In the Arguments tab, enter "-start &lt;jrun server name&gt;", so for a jrun server named "default" you'd enter "-start default".  &lt;br /&gt;7. Enter any JVM arguments you have in Jrun's jvm.config file into the VM arguments field:  "-Xms32m -Xmx128m -Dsun.io.useCanonCaches=false".&lt;br /&gt;&lt;br /&gt;You can then set breakpoints in your Java classes, and run JRun in Debug mode from within Eclipse during development.  &lt;br /&gt;&lt;br /&gt;One downside is that you can't debug JSP pages, only Java classes that you use.  This application is a Flash Rich Internet Application that uses Flash Remoting to access a J2EE middleware backend, so I don't have to debug JSP for this one.  I may review alternative debug methods that include JSP debugging later.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com" target="_blank"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114772847295967018?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114772847295967018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114772847295967018' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114772847295967018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114772847295967018'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/debugging-j2ee-technology-apps-on-jrun.html' title='Debugging J2EE Technology apps on JRun and Eclipse'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114710830187590745</id><published>2006-05-08T09:22:00.000-07:00</published><updated>2006-06-12T09:45:10.820-07:00</updated><title type='text'>Flash &amp; SEO, A Home Run for Barefoot, Part 1</title><content type='html'>Barefoot recently completed &lt;a href="http://www.adobe.com/go/flash" target="_blank"&gt;Flash&lt;/a&gt; site for &lt;a href="http://www.pitchinforbaseball.org" target="_blank"&gt;Pitch In For Baseball&lt;/a&gt;, a non-profit organization dedicated to providing baseball equipment to communities around the world who couldn't afford to purchase it otherwise.&lt;br /&gt;&lt;br /&gt;We created a site that targeted Flash 8 so we could take advantage of great new features like high quality font rendering and improved transition effects.  The client also wanted the ability to update content about current news, projects, and donor recognition.  So, we stored all of the content in a &lt;a href="http://www.mysql.com" target="_blank"&gt;MySQL&lt;/a&gt; database using a custom J2EE based content manager deployed on &lt;a href="http://tomcat.apache.org" target="_blank"&gt;Apache Tomcat&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;One of the typical drawbacks developers face when creating an all-Flash site is the issue of Search Engine Optimization, or SEO.  Most search engines just ignore .swf files altogether. While &lt;a href="http://tomcat.apache.org" target="_blank"&gt;Google&lt;/a&gt; does index .swf files, the results are very poor because they don't link into a specific navigation point with an all-Flash site.  &lt;br /&gt;&lt;br /&gt;For example, a Flash site may have a FAQ page with the word "FAQ" on it.  Within the Flash, the user clicks the FAQ button and is able to see the FAQ content.  However, if a user finds the FAQ content in a search engine that indexed the .swf itself, the link from the search engine to the site will likely take the user to the home page, or directly to the .swf file outside of its HTML container.  Either way, the user doesn't see the FAQ content from the search engine, even if that content can be found.&lt;br /&gt;&lt;br /&gt;Most solutions to this problem involve creating a completely separate HTML site.  The HTML is indexed by search engines, and the content can be found.  However, this typically requires developers or clients to maintain completely separate content bases, and prevents users who find the content via search engines from seeing the content in its Flash-enabled glory.&lt;br /&gt;&lt;br /&gt;For Pitch In For Baseball, we used a very innovative combination of technology strategies to support search engine links that when clicked, tell the Flash application to go to the page found by the searcher.  I'll detail the high-level strategy here, and write upcoming articles to show the details of each phase.&lt;br /&gt;&lt;br /&gt;The major components of the strategy are:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Store the content in a database.  This provides one place for everyone to update the content, while allowing developers to render that content in multiple ways.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Read the content dynamically into a Flash HTML field.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Code the Flash app so that it can read URLs in order to open sub-content areas.  Here are a couple example links:&lt;br&gt;   &lt;br /&gt;&lt;a href="http://www.pitchinforbaseball.org/pifbweb/ui/our_dugout/game_plan/" target="_blank"&gt;http://www.pitchinforbaseball.org/pifbweb/ui/our_dugout/game_plan/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.pitchinforbaseball.org/pifbweb/ui/where_we_help/faq/" target="_blank"&gt;http://www.pitchinforbaseball.org/pifbweb/ui/where_we_help/faq/&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use &lt;a href="http://blog.deconcept.com/swfobject/" target="_blank"&gt;SWFObject&lt;/a&gt; to embed the Flash app on the page.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Populate the SWFObject &amp;lt;div&amp;gt; block with the same dynamic content that is read into each Flash page.  If the user doesn't have Flash 6 or higher, or doesn't have Javascript turned on, they will see this content.  Since search engines fall into this category, they index this content.   Try out the links in the previous point with JavaScript turned off, and you'll see the flat content.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dynamically change the links within the content so that they navigate correctly whether inside the Flash, or on Flash HTML.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;The result of this design is that the site is completely indexed correctly by Google.  &lt;a href="http://www.google.com/search?hl=en&amp;lr=&amp;q=site%3Awww.pitchinforbaseball.org+baseball" target="_blank"&gt;Check out this search&lt;/a&gt; to see how well it is indexed, and how the Flash automatically goes to the right page.&lt;br /&gt;&lt;br /&gt;More details to come!&lt;br /&gt;&lt;br /&gt;by Doug Smith, Senior Developer, &lt;a href="http://www.thinkbarefoot.com" target="_blank"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114710830187590745?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114710830187590745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114710830187590745' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114710830187590745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114710830187590745'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/flash-seo-home-run-for-barefoot-part-1.html' title='Flash &amp; SEO, A Home Run for Barefoot, Part 1'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114667448965584168</id><published>2006-05-03T09:29:00.000-07:00</published><updated>2007-04-27T18:52:11.931-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Installing Ruby on Rails with mod_fcgi for Apache 2</title><content type='html'>After a few hours of trial and error using advice from many different sites/posts, this is the process that I found successful in getting &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; working with the &lt;a href="http://www.apache.org"&gt;Apache 2&lt;/a&gt; fcgi module on linux.  I hope this helps someone hang onto a few more hair follicles.&lt;br /&gt;&lt;br /&gt;Before we begin, I can say that this was only successful when I did the setup &lt;em&gt;in this order&lt;/em&gt;.  Perhaps others have done it in a different way, but this worked for me.  BTW, we're using &lt;a href="www.redhat.com"&gt;Redhat Enterprise 3&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;First, we'll install &lt;a href="http://www.ruby-lang.org/"&gt;ruby&lt;/a&gt;.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.4.tar.gz&lt;br /&gt;tar xvfz ruby-1.8.4.tar.gz&lt;br /&gt;cd ruby-1.8.4&lt;br /&gt;./configure --prefix=/usr/local&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Just to make sure everything installed properly try this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ruby --version&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It should return something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ruby 1.8.4 (2005-12-24) [i686-linux]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Now let's install ruby gems.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -O http://rubyforge.org/frs/download.php/5207/rubygems-0.8.11.tgz&lt;br /&gt;tar xvfz rubygems-0.8.11.tgz&lt;br /&gt;cd rubygems-0.8.11&lt;br /&gt;ruby setup.rb&lt;br /&gt;cd ..&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Now we need to install the FastCGI development kit.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -O http://www.fastcgi.com/dist/fcgi-2.4.0.tar.gz&lt;br /&gt;tar xvfz fcgi-2.4.0.tar.gz&lt;br /&gt;cd fcgi-2.4.0&lt;br /&gt;./configure --prefix=/usr/local&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Allrighty then.  We're moving now.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Install the bindings between Ruby and FastCGI&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -O http://sugi.nemui.org/pub/ruby/fcgi/ruby-fcgi-0.8.6.tar.gz&lt;br /&gt;tar xvfz ruby-fcgi-0.8.6.tar.gz&lt;br /&gt;cd ruby-fcgi-0.8.6&lt;br /&gt;ruby install.rb config&lt;br /&gt;ruby install.rb setup&lt;br /&gt;ruby install.rb install&lt;br /&gt;cd ..&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Next, install the mod_fcgi Apache 2 module.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -O http://fastcgi.coremail.cn/mod_fcgid.1.09.tar.gz&lt;br /&gt;tar xvfz mod_fcgid.1.09.tar.gz&lt;br /&gt;cd mod_fcgid.1.09&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Check the Makefile to make sure &lt;code&gt;top_dir&lt;/code&gt; points to the right directory.  Edit if it doesn't.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;make&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;It's finally time to install Rails.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;gem install rails --include-dependencies&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This will take a few minutes.  Grab a &lt;a href="http://www.dietpepsi.com"&gt;refreshing beverage&lt;/a&gt;.  If you really want to be entertained while improving your ruby skills, read &lt;a href="http://poignantguide.net/ruby/"&gt;why's (poignant) guide to ruby&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Time to get back to work.  Before we go nuts trying to see if Rails and Apache are on speaking terms, let's make sure Rails' built in server, WEBrick works.  We'll make a quick dummy application to try it out.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;rails dummyapp&lt;br /&gt;cd dummyapp&lt;br /&gt;ruby script/server&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You should see output similar to this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;=&gt; Booting WEBrick...&lt;br /&gt;=&gt; Rails application started on http://0.0.0.0:3000&lt;br /&gt;=&gt; Ctrl-C to shutdown server; call with --help for options&lt;br /&gt;[2006-05-03 11:56:50] INFO  WEBrick 1.3.1&lt;br /&gt;[2006-05-03 11:56:50] INFO  ruby 1.8.4 (2005-12-24) [i686-linux]&lt;br /&gt;[2006-05-03 11:56:50] INFO  WEBrick::HTTPServer#start: pid=8747 port=3000&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, let's see what the application shows us.  I usually do this in a new terminal window.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;lynx http://localhost:3000&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Hopefully, you can see a page that has "Welcome aboard, You're riding the Rails!" on it, along with links to documentation and other goodies.  If you're not, begin abusing hair follicles.  Assuming you did see it, quit out of lynx, then shutdown WEBrick in the other terminal by hitting ctrl-c on your keyboard.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Rails and Apache.  Joanie and Chachi.  &lt;del&gt;Nick and Jessica&lt;/del&gt;.  Peas and Carrots.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Let's make a super couple.  To test Rails and Apache, we need to enter the appropriate lines into our Apache configuration file.  I was doing this as an additional virtual host, so mine looks like so:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;VirtualHost 10.10.10.10:80&amp;gt;&lt;br /&gt;   ServerAdmin me@gmail.com&lt;br /&gt;   ServerName website.com&lt;br /&gt;   UseCanonicalName Off&lt;br /&gt;&lt;br /&gt;   # Set to development, test, or production&lt;br /&gt;   DefaultInitEnv RAILS_ENV production&lt;br /&gt;   # set to the "public" directory of your app&lt;br /&gt;   DocumentRoot /root/dummyapp/public  &lt;br /&gt;&lt;br /&gt;   &amp;lt;Directory "/root/dummyapp/public"&amp;gt;&lt;br /&gt;       RewriteEngine On&lt;br /&gt;       RewriteRule ^$ index.html [QSA]&lt;br /&gt;       RewriteRule ^([^.]+)$ $1.html [QSA]&lt;br /&gt;       RewriteCond %{REQUEST_FILENAME} !-f&lt;br /&gt;       RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]&lt;br /&gt;       Options Indexes ExecCGI FollowSymLinks&lt;br /&gt;       AllowOverride None&lt;br /&gt;       Order allow,deny&lt;br /&gt;       Allow from all&lt;br /&gt;       AddHandler fcgid-script .fcgi&lt;br /&gt;   &amp;lt;/Directory&amp;gt;&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now that you've added the block to your Apache config, restart Apache and browse to you site.  You should once again see the familiar "Welcome aboard, You're riding the Rails!"&lt;br /&gt;&lt;br /&gt;And there was much rejoicing.&lt;br /&gt;&lt;br /&gt;Sean Brown, Partner, Technology at &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114667448965584168?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114667448965584168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114667448965584168' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114667448965584168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114667448965584168'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/installing-ruby-on-rails-with-modfcgi.html' title='Installing Ruby on Rails with mod_fcgi for Apache 2'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114667414699767475</id><published>2006-05-03T08:57:00.000-07:00</published><updated>2006-05-08T08:35:34.080-07:00</updated><title type='text'>Flash ExpressInstall Challenge</title><content type='html'>We have standardized on using the &lt;a href="http://blog.deconcept.com/swfobject/"&gt;SWFObject&lt;/a&gt; method of embedding Flash applications onto web pages.  It has a whole host of advantages -- most recently it has proven to overcome the new IE "Activate an ActiveX Control" issue, where users must click to activate any ActiveX control that is embedded with an applet, embed, or object tag.  &lt;br /&gt;&lt;br /&gt;SWFObject also interfaces with &lt;a href="http://www.adobe.com/products/flashplayer/download/detection_kit/"&gt;Adobe's ExpressInstall&lt;/a&gt; feature very nicely.  You can embed ExpressInstall code into a Flash app and if the user doesn't have the required version of Flash (but they have at least Flash v6.0.65), the user can automatically update the Flash plugin.  &lt;br /&gt;&lt;br /&gt;I ran into a challenge while adding the ExpressInstall to a recent app.  I was publishing a .swf as a Flash 8 file, with a text message that appeared if the user didn't have Flash 8 that explained the ExpressInstall process.  The problem was that when I tested on Flash Player 6 or 7, the text message was invisible.  I even tested using the so_tester file provided with (previously named) FlashObject, and when published as Flash 8, the upgrade message and button were also invisible.&lt;br /&gt;&lt;br /&gt;Fortunately, the latest version of SWFObject (1.4) alerts us to the solution I discovered before its release: you &lt;span style="font-weight:bold;"&gt;must use Device Fonts for any text that could be displayed by a Flash Player &lt; version 8, if you are publishing to Flash 8&lt;/span&gt;.  Once I changed my text message to using Device Fonts, I could see the text in Flash Player 6 or 7 and all was well.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer at &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114667414699767475?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114667414699767475/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114667414699767475' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114667414699767475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114667414699767475'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/flash-expressinstall-challenge.html' title='Flash ExpressInstall Challenge'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114660814700864717</id><published>2006-05-02T15:12:00.000-07:00</published><updated>2006-05-09T13:21:36.493-07:00</updated><title type='text'>Add properties to already existing Classes in Flash</title><content type='html'>This is a simple way to store a property in the Sound Class of Flash for use in the onSoundComplete method (or any built-in method with any built-in class). The theory is called casting:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var my_sound: Object = new Sound();&lt;br /&gt;my_sound.id = this.id;&lt;br /&gt;my_sound.attachSound("someSound");&lt;br /&gt;my_sound.start();&lt;br /&gt;my_sound.onSoundComplete = function()&lt;br /&gt;{&lt;br /&gt;// stop the animation&lt;br /&gt;_root["person" + id].stop();&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Notice the declaration of &lt;code&gt;Object&lt;/code&gt;, in the first line. Should &lt;code&gt;my_sound&lt;/code&gt; be defined as &lt;code&gt;Sound&lt;/code&gt;, the second line would fail because &lt;code&gt;id&lt;/code&gt; is not a predefined property of the &lt;code&gt;Sound&lt;/code&gt; class. By declaring &lt;code&gt;my_sound&lt;/code&gt; as a generic Object, adding properties is allowed. All &lt;code&gt;Sound&lt;/code&gt; properties and methods are still available even with the generic Object definition, hence the use of the &lt;code&gt;onSoundComplete&lt;/code&gt; method on line 5. Notice the use of the &lt;code&gt;id&lt;/code&gt; property to dynamically define an object on &lt;code&gt;the _root&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;More can be read about casting in Flash in Colin Moock's latest book from O'Reilly called "&lt;a href="http://www.oreilly.com/catalog/0596006527/index.html"&gt;Essential Actionscript 2.0&lt;/a&gt;."&lt;br /&gt;&lt;br /&gt;Michael Krisher, Senior Developer at &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114660814700864717?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114660814700864717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114660814700864717' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114660814700864717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114660814700864717'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/add-properties-to-already-existing.html' title='Add properties to already existing Classes in Flash'/><author><name>Mike</name><uri>http://www.blogger.com/profile/03660862347691755922</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114658928741090455</id><published>2006-05-02T09:32:00.000-07:00</published><updated>2008-02-18T13:42:47.188-08:00</updated><title type='text'>Public Key SSH Authentication</title><content type='html'>This implementation will give you "passwordless SSH authentication."  This can be set up between two machines on which you already have an SSH account.  This example uses DSA encryption, but it works the same for RSA.&lt;br /&gt;&lt;br /&gt;On the &lt;span style="font-weight: bold;"&gt;local&lt;/span&gt; machine (i.e., your laptop):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ssh-keygen -t dsa &lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You'll get three prompts.  To all three, simply hit &lt;code&gt;enter&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Enter file in which to save the key(/home/youraccount/.ssh/id_dsa):&lt;br /&gt;Enter passphrase (empty for no passphrase):&lt;br /&gt;Enter same passphrase again:&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This creates two files in your local home directory's .ssh folder, &lt;em&gt;id_dsa&lt;/em&gt; and &lt;em&gt;id_dsa.pub&lt;/em&gt;.  &lt;em&gt;id_dsa&lt;/em&gt; is your private key file.  Guard it closely.  &lt;em&gt;id_dsa.pub&lt;/em&gt; is your public key file;  you'll use this in a moment.&lt;br /&gt;&lt;br /&gt;Change into the .ssh folder in your home directory.  For me, that meant&lt;br /&gt;&lt;br /&gt;&lt;code&gt;cd /home/sbrown/.ssh&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Still on your local machine, we want to put a copy your &lt;em&gt;id_dsa.pub&lt;/em&gt; file onto your server.  I am using secure copy to do it, but you can substitute FTP if you like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;scp id_dsa.pub remote_user@remote_server.com:id_dsa.pub&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Obviously, substitute your actual username and machine name into the code above.  Now ssh into your account on the remote machine.&lt;br /&gt;&lt;br /&gt;Now we're on your &lt;span style="font-weight: bold;"&gt;remote&lt;/span&gt; machine (in remote_user's home directory).&lt;br /&gt;&lt;br /&gt;A copy of your local machine's &lt;em&gt;id_dsa.pub&lt;/em&gt; file should be in your home directory.  We need to copy its contents into a file called authorized_keys in the .ssh directory on your remote account.  This assumes the .ssh folder already exists.  If not create it first (&lt;code&gt;mkdir .ssh&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;&lt;code&gt;cat id_dsa.pub &gt;&gt; .ssh/authorized_keys&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, set the permissions on your files and folders properly:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;chmod 700 .ssh&lt;br /&gt;chmod 600 .ssh/authorized_keys&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And don't forget to remove the copy of &lt;em&gt;id_dsa.pub&lt;/em&gt; in your home directory.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;rm id_dsa.pub&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And that's it.  Log out of your remote machine, then back on your local machine, ssh back into your remote machine and you should get in with no password.&lt;br /&gt;&lt;br /&gt;Sean Brown, Partner, Technology at &lt;a href="http://www.thinkbarefoot.com/"&gt;Barefoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114658928741090455?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114658928741090455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114658928741090455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114658928741090455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114658928741090455'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/public-key-ssh-authentication.html' title='Public Key SSH Authentication'/><author><name>Sean Brown</name><uri>http://www.blogger.com/profile/10018498282709787995</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_389-YbXwzuA/Sz9_P6M2NGI/AAAAAAAAASY/AYKHmFwQPm0/S220/picture_72dpi.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27393356.post-114652403975014629</id><published>2006-05-01T15:51:00.000-07:00</published><updated>2006-05-08T08:35:49.696-07:00</updated><title type='text'>SQL to find missing linked records</title><content type='html'>I'm working on a database project that has two tables.  One table contains document information, with one record per document.  Another table contains parties, the people/companies linked to the documents.  There can be many parties for each document.  There are two kinds of parties -- party 1 and party 2.  &lt;br /&gt;&lt;br /&gt;I needed to find all of the documents of a certain type that didn't have any party 1's.  To start, I created SQL like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT d.document_id&lt;br /&gt;FROM documents d&lt;br /&gt;LEFT OUTER JOIN parties p &lt;br /&gt;&amp;nbsp;&amp;nbsp;on d.document_id = p.document_id &lt;br /&gt;WHERE p.party_id is null;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This query simply returns all of the document IDs where there is no party linked to each document.  This only gets me 1/2 way to my goal, however, because I only want to see the documents that have no linked party 1's.   This returns documents that have &lt;span style="font-weight:bold;"&gt;any&lt;/span&gt; parties linked to them, whether party 1 or 2.&lt;br /&gt;&lt;br /&gt;To find only those that are missing party 1's, I used the following SQL:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT d.document_id&lt;br /&gt;FROM documents d&lt;br /&gt;LEFT OUTER JOIN parties p &lt;br /&gt;&amp;nbsp;&amp;nbsp;on d.document_id = p.document_id &lt;span style="font-weight:bold;"&gt;AND p.party_num = 1&lt;/span&gt;&lt;br /&gt;WHERE p.party_id is null &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The interesting thing here is that I added conditions you'd normally think to put in the WHERE clause to the condition of the LEFT OUTER JOIN clause.  By doing this, the database only tries to link records between the two tables from the type of party I'm looking for -- &lt;code&gt;party_num = 1&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Doug Smith, Senior Developer at &lt;a href="http://www.thinkbarefoot.com"&gt;Barefoot&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27393356-114652403975014629?l=barefootdevelopment.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://barefootdevelopment.blogspot.com/feeds/114652403975014629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27393356&amp;postID=114652403975014629' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114652403975014629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27393356/posts/default/114652403975014629'/><link rel='alternate' type='text/html' href='http://barefootdevelopment.blogspot.com/2006/05/sql-to-find-missing-linked-records.html' title='SQL to find missing linked records'/><author><name>Dr. B.</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
