<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DkcERn44eCp7ImA9WhRaE0o.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239</id><updated>2012-02-16T00:33:27.030-06:00</updated><category term="ruby" /><category term="javascript" /><category term="documentation" /><category term="movies" /><category term="swing" /><category term="apple" /><category term="development" /><category term="nfjs" /><category term="maven" /><category term="flock" /><category term="wtf" /><category term="validation" /><category term="airfail" /><category term="chrome" /><category term="webstart" /><category term="css" /><category term="resque" /><category term="opensource" /><category term="wicket" /><category term="swingwork" /><category term="spring" /><category term="rails" /><category term="email" /><category term="performance" /><category term="eclipse" /><category term="productivity" /><category term="backgrounded" /><category term="database" /><category term="linux" /><category term="socialcast" /><category term="hibernate" /><category term="springrcp" /><category term="java" /><category term="air" /><category term="process" /><category term="ajax" /><category term="zillow" /><category term="security" /><category term="ubiquity" /><category term="scm" /><category term="jsp" /><category term="multitenant" /><category term="games" /><category term="music" /><category term="scriptaculous" /><category term="blog" /><category term="lifemotto" /><category term="swt" /><category term="oracle" /><category term="regex" /><category term="jquery" /><category term="rspec" /><category term="cargo" /><category term="shard" /><category term="jroller" /><category term="search" /><category term="design" /><category term="testing" /><category term="email_preview" /><category term="lean_startup" /><title>Life at the Speed of Green</title><subtitle type="html">Life is too short for failing tests...</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.codecrate.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.codecrate.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>250</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/code_poet" /><feedburner:info uri="code_poet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>code_poet</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;D0MERXY9fSp7ImA9WhRRFEQ.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-393086369716623127</id><published>2011-11-28T09:00:00.000-06:00</published><updated>2011-11-28T09:50:04.865-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-28T09:50:04.865-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="performance" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>callback_skipper for faster factories</title><content type="html">&lt;p&gt;&lt;a href="http://www.flickr.com/photos/brentbat/2137221211"&gt;&lt;img src="http://farm3.staticflickr.com/2194/2137221211_6d705baeae.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rails community has taken a strong stance that &lt;a href="http://railscasts.com/episodes/158-factories-not-fixtures"&gt;test fixtures are evil and factories are the new hotness&lt;/a&gt;.  The most compelling reason for this shift being that factories greatly reduce maintance costs as your application grows over time.&lt;/p&gt;

&lt;p&gt;The largest drawback when replacing fixtures with factories is the additional performance overhead when initializing your test state.  This is due to the fact that factories utilize the full ActiveRecord lifecycle (initialize object, validate, save to database) compared to fixtures which are a glorified database bulk import which bypass object creation/validation.&lt;/p&gt;

&lt;p&gt;Take this example ActiveRecord model and testcase (using &lt;a href="https://github.com/thoughtbot/factory_girl"&gt;FactoryGirl&lt;/a&gt; factories):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Foo &amp;lt; ActiveRecord::Base
  after_create :do_something_expensive
end

def test_new_instance_has_bar
  foo = Factory.create :foo

  assert foo.bar?
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a classic example where using factories will be much slower than fixtures.  It may not seem like a big deal, but it becomes a real issue when you have a sizable application testsuite, and I'm a firm believer that if a testsuite isn't fast, it doesn't get run.&lt;/p&gt;

&lt;p&gt;Sending email with &lt;code&gt;after_save&lt;/code&gt; callback is another great usecase.  How can we get the benefits of factories without the additional overhead?  Can we have our cake and eat it too?&lt;/p&gt;

&lt;p&gt;Since 99% of our testcases rely on the basic factory instance and are not dependent upon the expensive callback being fired, it would be ideal to skip the expensive callback for most of your testcases and only fire it for testscases that are explicitly asserting it's behavior.&lt;/p&gt;

&lt;p&gt;The newly released &lt;a href="https://github.com/wireframe/callback_skipper"&gt;callback_skipper gem&lt;/a&gt; fulfills this exact usecase.  The goal is to make it trivial to skip a particular model callback for a specific instance.  This is a homerun for test factories to setup a default factory which skip non-critical callbacks and still have the flexibility to create a factory that &lt;em&gt;does&lt;/em&gt; fire the slow-ass callbacks for specific tests.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# spec/factories/foo_factory.rb
Factory.define :foo do |f|
  f.after_build do |o|
    o.skip_callback :save, :after, :do_something_expensive
  end
end
Factory.define :foo_with_expensive_callback, :class =&amp;gt; 'Foo' do |f|
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Oh yes, we &lt;em&gt;can&lt;/em&gt; have our cake and eat it too...&lt;/p&gt;

&lt;p&gt;The callback_skipper gem is equivalent to the core &lt;a href="https://github.com/wireframe/callback_skipperhttp://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-skip_callback"&gt;&lt;code&gt;ActiveRecord.skip_callback&lt;/code&gt;&lt;/a&gt; method with the added benefit of only skipping the callback for a specific instance instead of globally for all invocations.&lt;/p&gt;

&lt;p&gt;As always, the gem is 100% opensource and suggestions are welcome!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-393086369716623127?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/uQ5l_ij7kMU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/393086369716623127/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/11/callbackskipper-for-faster-factories.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/393086369716623127?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/393086369716623127?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/uQ5l_ij7kMU/callbackskipper-for-faster-factories.html" title="callback_skipper for faster factories" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/11/callbackskipper-for-faster-factories.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYBRXg5fCp7ImA9WhRTGEg.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8158222717550473053</id><published>2011-11-09T09:55:00.000-06:00</published><updated>2011-11-09T09:55:54.624-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-09T09:55:54.624-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="resque" /><title>Cron Backgrounded Resque Jobs</title><content type="html">&lt;p&gt;&lt;a href="http://www.flickr.com/photos/jeremy-g/1512405671/" title="Analog Time Sand by Jeremy-G, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2144/1512405671_efd8d1ad14.jpg" width="500" height="500" alt="Analog Time Sand"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html"&gt;Cron&lt;/a&gt;
is still the de facto standard when it comes to scheduling execution
of jobs.  It does one thing, and it does it well.&lt;/p&gt;

&lt;p&gt;Using cron to fire off heavyweight Ruby/Rails jobs is fairly trivial to
get up and running.  When each cron job fires up a full Rails process,
it won't be long until you accrue enough jobs that your cron
server will be brought to it's knees.  Since you already have a pool of
&lt;a href="http://github.com/defunkt/resque"&gt;Resque&lt;/a&gt; background workers running to process async jobs, why not leverage dormant workers
to process jobs that are kicked off via cron?&lt;/p&gt;

&lt;p&gt;Here is a simple solution that can be used to have cron enqueue jobs into
&lt;a href="http://blog.codecrate.com/2010/09/backgrounded-resque-support.html"&gt;Backgrounded Resque jobs&lt;/a&gt;
without loading the entire Rails environment.&lt;br&gt;
This is an excellent optimization to increase the availability
of your cron server and avoid running out of memory when multiple jobs
fire up at the same time.&lt;/p&gt;

&lt;p&gt;crontab invocation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/backgrounded enqueue Foo.bar --queue baz
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/1351867"&gt;script/backgrounded&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env ruby
require 'rubygems'
require 'thor'

module Backgrounded
  class CLI &amp;lt; Thor

    desc 'enqueue', 'enqueue a clazz.method invocation for resque backgrounded workers'
    method_option :queue, :aliases =&amp;gt; "-q", :desc =&amp;gt; "resque queue to enqueue the operation to", :default =&amp;gt; 'backgrounded'
    method_option :rails_env, :aliases =&amp;gt; '-e', :desc =&amp;gt; 'control which rails env used to load the redis config', :default =&amp;gt; 'production'
    # operation Clazz.method to enqueue into resque backgrounded queue (ex: Foo.bar)
    def enqueue(operation)
      require 'bundler'
      Bundler.setup
      require "thread"
      require "active_support/inflector"
      require "resque"
      require "yaml"
      clazz, method = operation.to_s.split('.')
      raise 'invalid operation' unless clazz &amp;amp;&amp;amp; method
      Resque.redis = YAML.load_file(File.join("config", "resque.yml"))[options[:rails_env]]
      Resque::Job.create(options[:queue], 'Backgrounded::Handler::ResqueHandler', clazz, -1, method)
    end
  end
end

Backgrounded::CLI.start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use cron for it's excellent scheduling capabilities and
take advantage of your background processing infrastructure to handle
the incoming request!&lt;/p&gt;
          &lt;div&gt;
        
      
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8158222717550473053?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/E5VEG9eBUms" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8158222717550473053/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/11/cron-backgrounded-resque-jobs.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8158222717550473053?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8158222717550473053?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/E5VEG9eBUms/cron-backgrounded-resque-jobs.html" title="Cron Backgrounded Resque Jobs" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm3.static.flickr.com/2144/1512405671_efd8d1ad14_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/11/cron-backgrounded-resque-jobs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYHRnk6eyp7ImA9WhdUGUQ.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8366584829575743784</id><published>2011-10-04T21:00:00.000-05:00</published><updated>2011-10-07T09:02:17.713-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-07T09:02:17.713-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="rspec" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Cleaner RSpec/FactoryGirl Integration</title><content type="html">&lt;a href="http://www.flickr.com/photos/dawilson/3389046866/" title="Think Different..."&gt;&lt;img alt="Think Different..." height="332" src="http://farm4.static.flickr.com/3593/3389046866_c642884f48.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;
For the past few days, I've been re-thinking how to integrate
&lt;a href="https://github.com/thoughtbot/factory_girl"&gt;FactoryGirl&lt;/a&gt; fixtures into my
&lt;a href="https://www.relishapp.com/rspec"&gt;RSpec&lt;/a&gt; tests.  The transition from
Test::Unit to RSpec has taken me some time and I'm still
"Unlearning what I have learned" and adapting to the RSpec way of doing things.
It's not that Test::Unit style tests are wrong, it just that I need
to think a bit differently in order to take full advantage of the
expressiveness of RSpec tests.&lt;br /&gt;
&lt;br /&gt;
One common pattern for Rails application tests is to instantiate fixture data
and perform assertions on the instances:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;describe User do
  context 'basic user' do
    before do
      @user = Factory.create :user
    end
    it { @user.should be_inactive }
    it { @user.should_not be_happy }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
According to &lt;a href="http://blog.codecrate.com/2010/12/anatomy-of-effective-unit-test.html"&gt;The Anatomy of an Effective Unit
Test&lt;/a&gt;
the &lt;code&gt;before&lt;/code&gt; block in this example is playing the role of both fixture
setup &lt;em&gt;and&lt;/em&gt; test execution.  The ambiguous definition of the &lt;code&gt;before&lt;/code&gt;
method is analogous to how horrible it is to use API's with incredibly
useful methods such as &lt;code&gt;run&lt;/code&gt; or &lt;code&gt;execute&lt;/code&gt;.  Sure, we know &lt;em&gt;when&lt;/em&gt; the block is executed, but
method names are key to understanding their purpose as well.&lt;br /&gt;
&lt;br /&gt;
I'm a strong proponent of &lt;a href="http://blog.codecrate.com/2005/02/document-code-with-units-of-work.html"&gt;expressive method names&lt;/a&gt; as a way to self
document your code and that's exactly what we need in this scenario.  We
need a clear and concise way to encapsulate our fixture data initialization outside of the &lt;code&gt;before&lt;/code&gt; method
and hook it into the RSpec test lifecycle.  This will help remove the ambiguous purpose of the &lt;code&gt;before&lt;/code&gt; method
in this example.&lt;br /&gt;
&lt;br /&gt;
Luckily, The RSpec DSL can be easily be extended. &amp;nbsp;The new &lt;a href="https://github.com/wireframe/factory_girl_rspec"&gt;factory_girl_rspec gem&lt;/a&gt; adds the &lt;code&gt;with&lt;/code&gt; method which is a seriously clean and concise DSL for initializing your fixtures:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;describe User do
  context 'basic user' do
    with :user
    it { user.should be_inactive }
    it { user.should_not be_happy }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
The &lt;code&gt;with&lt;/code&gt; method is a simple utility that maps directly to the
FactoryGirl fixtures that you have defined for your test environment.
Each execution of &lt;code&gt;with&lt;/code&gt; will create your fixture instance and define
a local method within your test context retrieve the instance.  Compare the readability
of this test with the previous incarnation.  It reads like a story right?  Watch out &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt;,
who needs text processing stories when you've got human readable ruby code!&lt;br /&gt;
&lt;br /&gt;
Deviations of fixture data are extremely common in unit tests as well,
and the &lt;code&gt;with&lt;/code&gt; method expresses differences in an equally concise manner:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;describe User do
  context 'user with first_name == "Jim"' do
    with :user, :first_name =&amp;gt; "Jim"
    it { user.should be_inactive }
    it { user.should be_happy }
  end
end&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/7648837050901945239-8366584829575743784?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/phmVxKUF-3s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8366584829575743784/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/10/cleaner-rspecfactorygirl-integration.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8366584829575743784?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8366584829575743784?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/phmVxKUF-3s/cleaner-rspecfactorygirl-integration.html" title="Cleaner RSpec/FactoryGirl Integration" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm4.static.flickr.com/3593/3389046866_c642884f48_t.jpg" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/10/cleaner-rspecfactorygirl-integration.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UGSHw8fCp7ImA9WhdVEEg.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-3472739784156941285</id><published>2011-09-14T22:30:00.000-05:00</published><updated>2011-09-14T23:00:29.274-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-14T23:00:29.274-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Guard JSLint on Rails Released!</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-87kA7x9PKz8/TnFuNzks7jI/AAAAAAAAADA/ihGp062mQu0/s1600/HolyGrail117_edit.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="220" src="http://4.bp.blogspot.com/-87kA7x9PKz8/TnFuNzks7jI/AAAAAAAAADA/ihGp062mQu0/s400/HolyGrail117_edit.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
Continuous testing has changed my life and &lt;a href="https://github.com/guard/guard"&gt;Guard&lt;/a&gt; is top dog when it comes to continuous testing for Rails development. &amp;nbsp;It's simple, reliable and &lt;a href="https://github.com/guard/guard/wiki/List-of-available-Guards"&gt;&lt;i&gt;extremely&lt;/i&gt; easy to extend&lt;/a&gt;. &amp;nbsp;&lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;Autotest&lt;/a&gt; and &lt;a href="https://github.com/mynyml/watchr"&gt;Watchr&lt;/a&gt; don't hold a candle to Guard.&lt;/div&gt;
&lt;div class="p3"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
It is imperative when building Javascript heavy applications that you add &lt;a href="http://jslint.com/"&gt;JSLint&lt;/a&gt; to your workflow. &amp;nbsp;The excellent&amp;nbsp;&lt;a href="https://github.com/psionides/jslint_on_rails"&gt;jslint-on-rails gem&lt;/a&gt;&amp;nbsp;makes it absolutely trivial, so if you haven't done so already, do it now! &amp;nbsp;(Don't worry. &amp;nbsp;I'll wait for you. &amp;nbsp;Seriously, it only takes a few minutes.)&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
Some people whine and complain that &lt;a href="http://webreflection.blogspot.com/2010/02/jslint-bad-part.html"&gt;JSLint is evil&lt;/a&gt;&amp;nbsp;and&amp;nbsp;there may be some&amp;nbsp;legitimacy&amp;nbsp;to that claim, but the simple fact is that JSLint has saved my ass more than once. &amp;nbsp;Before JSLint, a single missing semicolon would take down an entire application for IE6/7 users, but ever since we've introduced JSLint into our development flow, we've had zero issues.&lt;/div&gt;
&lt;div class="p3"&gt;
&lt;br /&gt;
Integrating JSLint into your continuous testing flow is now possible with the new&amp;nbsp;&lt;a href="https://github.com/wireframe/guard-jslint-on-rails"&gt;guard-jslint-on-rails plugin&lt;/a&gt;. &amp;nbsp;Catch those pesky lint issues before you deploy to production. &amp;nbsp;Catch them even before your continuous integration process runs. &amp;nbsp;Heck, now you'll catch them before you commit them to your version control repository!&lt;/div&gt;
&lt;div class="p3"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;

First: install the gem...&lt;/h3&gt;
&lt;pre&gt;$ gem install guard-jslint-on-rails&lt;/pre&gt;
&lt;div class="p3"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;

Next: configure guard...&lt;/h3&gt;
&lt;pre&gt;$ guard init jslint-on-rails&lt;/pre&gt;
&lt;div class="p3"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
And voila! &amp;nbsp;Each time you tweak your Javascript files, your changes will be validated against your JSLint config!&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-3472739784156941285?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/azqlsfpOmFo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/3472739784156941285/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/09/guard-jslint-on-rails-released.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/3472739784156941285?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/3472739784156941285?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/azqlsfpOmFo/guard-jslint-on-rails-released.html" title="Guard JSLint on Rails Released!" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-87kA7x9PKz8/TnFuNzks7jI/AAAAAAAAADA/ihGp062mQu0/s72-c/HolyGrail117_edit.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/09/guard-jslint-on-rails-released.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcCQHo8fip7ImA9WhZaEk4.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4681449710922377250</id><published>2011-06-27T23:37:00.002-05:00</published><updated>2011-06-27T23:41:01.476-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-27T23:41:01.476-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>JSLint Rails Javascript Responses</title><content type="html">Unit testing Javascript has quickly become an absolute requirement for building a modern web application. &amp;nbsp;It's not uncommon for most web applications to have more Javascript code/libraries than serverside logic. &amp;nbsp;There are plenty of frameworks javascript testing frameworks available for unit testing your standalone javascript components&amp;nbsp;(&lt;a href="http://www.jsunit.net/"&gt;JSUnit&lt;/a&gt;, &lt;a href="http://docs.jquery.com/Qunit"&gt;QUnit&lt;/a&gt;, etc), but how do you test Javascript responses generated by Rails?&lt;br /&gt;
&lt;br /&gt;
Let's start by describing problem in a bit more detail. &amp;nbsp;Here are a few snippits of code that you'll find in pretty much any AJAX heavy Rails application.&lt;br /&gt;
&lt;br /&gt;
Example Rails form for creating a new blog post via AJAX:&lt;br /&gt;
&lt;pre class="code"&gt;# app/views/blogs/new.html.haml
= form_for Blog.new, :remote =&amp;gt; true do |f|
&amp;nbsp;&amp;nbsp;= f.text_area :body
&amp;nbsp;&amp;nbsp;= f.submit&lt;/pre&gt;&lt;br /&gt;
Example Rails controller to create blog post:&lt;br /&gt;
&lt;pre class="code"&gt;# app/controllers/blog_controller.rb
class BlogController &amp;lt; ApplicationController
&amp;nbsp;&amp;nbsp;def create
&amp;nbsp;&amp;nbsp; &amp;nbsp;@blog = Blog.create params[:blog]
&amp;nbsp;&amp;nbsp; &amp;nbsp;respond_with @blog
&amp;nbsp;&amp;nbsp;end
end&lt;/pre&gt;&lt;br /&gt;
Example Rails Javascript ERB response to return new blog post:&lt;br /&gt;
&lt;pre class="code"&gt;# app/views/blogs/create.js.erb
$('#new_blog_form').trigger('reset');
$('body').append('&amp;lt;h2&amp;gt;New Blog Post has been created!&amp;lt;/h2&amp;gt;');
$('body').append('&amp;lt;%= escape_javascript(@blog.body) %&amp;gt;');&lt;/pre&gt;&lt;br /&gt;
Now this is a very simple example, but how can you effectively assert that the Javascript response is doing what you expect? &amp;nbsp;Sure, you can use selenium or some other heavyweight testing library to boot up a browser and exercise your Javascript, but there's got to be a quicker way to get the job done.&lt;br /&gt;
&lt;br /&gt;
With a few simple solutions, you can get a lot of bang for your buck without introducing more complex and finicky end to end testing frameworks.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Move Javascript Logic to Static Component&lt;/h2&gt;Refactoring your Javascript response to use the "Fat Component, Skinny Response" pattern is the &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;same pattern we've been using for building our controllers&lt;/a&gt;, and it moves all of the logic out of your ERB templates and into a standalone (and testable) Javascript component.&lt;br /&gt;
&lt;br /&gt;
Example refactored Javascript response:&lt;br /&gt;
&lt;pre class="code"&gt;# app/views/blogs/create.js.erb
myApp.createBlog(&amp;lt;%= @blog.to_json %&amp;gt;);&lt;/pre&gt;&lt;br /&gt;
Example Javascript component:&lt;br /&gt;
&lt;pre class="code"&gt;# public/javascripts/application.js
myApp = {
&amp;nbsp;&amp;nbsp;createBlog: function(data) {
&amp;nbsp;&amp;nbsp; &amp;nbsp;$('#new_blog_form').trigger('reset');
&amp;nbsp;&amp;nbsp; &amp;nbsp;$('body').append('&amp;lt;h2&amp;gt;New Blog Post has been created!&amp;lt;/h2&amp;gt;');
&amp;nbsp;&amp;nbsp; &amp;nbsp;var content = $('p').text(data.body);
&amp;nbsp;&amp;nbsp; &amp;nbsp;$('body').append(content);
&amp;nbsp;&amp;nbsp;}
};&lt;/pre&gt;&lt;br /&gt;
Now you can effectively unit test your component using traditional Javascript testing libraries.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;JSLint the Rails Response&lt;/h2&gt;&lt;br /&gt;
&lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt; is a simple and very effective smoketest to ensure that any Javascript response is valid and without syntax errors.  This prevents any extra semi-colons or simple syntax errors from breaking your user's browser.  All "modern" browsers are pretty forgiving when dealing with Javascript errors, but one browser in particular *cough* IE *cough* can really freak out if it encounters invalid Javascript&lt;br /&gt;
&lt;br /&gt;
Rspec users can drop this &lt;a href="https://gist.github.com/1042920"&gt;be_lintable helper&lt;/a&gt; into there application for a quick sanity check that their JS responses should be operational.  Not using RSpec?  The &lt;a href="https://github.com/psionides/jslint_on_rails"&gt;JSLint on Rails plugin&lt;/a&gt; is pretty simple to integrate into any application.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="code"&gt;describe 'xhr post :create' do
&amp;nbsp;&amp;nbsp;before { xhr :post, :create, :blog =&amp;gt; {:body =&amp;gt; 'test'} }
&amp;nbsp;&amp;nbsp;it { response.body.should be_lintable }
end&lt;/pre&gt;&lt;br /&gt;
Now go forth and test those Javascript Rails responses!&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4681449710922377250?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/yil5vIzukU4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4681449710922377250/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/06/jslint-rails-javascript-responses.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4681449710922377250?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4681449710922377250?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/yil5vIzukU4/jslint-rails-javascript-responses.html" title="JSLint Rails Javascript Responses" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/06/jslint-rails-javascript-responses.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcGSH87eip7ImA9WhZbEUk.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-2494908748793364010</id><published>2011-06-15T08:52:00.004-05:00</published><updated>2011-06-15T09:10:29.102-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-15T09:10:29.102-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="email_preview" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="email" /><title>Email Preview + Transactions and Factories</title><content type="html">&lt;a href="http://www.flickr.com/photos/pacdog/4968422200/" title="Mail by Pacdog, on Flickr"&gt;&lt;img alt="Mail" height="349" src="http://farm5.static.flickr.com/4126/4968422200_1ab99c61ef.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;a href="http://github.com/wireframe/email_preview"&gt;email_preview&lt;/a&gt; gem has undergone a serious overhaul in the last week.  After some extensive real-world project use, a new version has been released with some major improvements.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;

Transactional Fixtures&lt;/h2&gt;
&lt;br /&gt;
At a high level, email_preview fixtures are very similar in nature to Rails test fixtures.  They are a snapshot of data with a particular configuration and are not intended to be long lived outside of the email_preview.  The setup and teardown of previewed emails should follow the same setup/teardown of fixture data used during unit testing.  Now each email_preview action is wrapped in a database transaction and rolled back after completion to avoid polluting your sandbox with unnecessary data!&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="code"&gt;# config/initializers/email_preview.rb
EmailPreview.register 'User activation email' do
  u = User.create :email =&amp;gt; 'foo@example.com'
  UserMailer.activation(u)
end
&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;

Factories&lt;/h2&gt;
&lt;br /&gt;
Why duplicate the setup of fixture data when building your email preview templates?  You already use a factory library for your unit tests right?  The email_preview gem now works great with any factory framework you choose (&lt;a href="https://github.com/thoughtbot/factory_girl"&gt;FactoryGirl&lt;/a&gt;, &lt;a href="https://github.com/notahat/machinist"&gt;Machinist&lt;/a&gt;, etc).&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="code"&gt;# config/initializers/email_preview.rb
# example FactoryGirl fixture
EmailPreview.register 'User activation email' do
  u = Factory.create :user
  UserMailer.activation u
end
&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;

Categories&lt;/h2&gt;
&lt;br /&gt;
Email is still a critical component of most web applications, and it's very easy to rack up lots of templates when working with a large application.  To help organize your fixtures, you can now use an optional :category option to group related fixtures.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="code"&gt;# config/initializers/email_preview.rb
EmailPreview.register 'User activation email', :category =&amp;gt; :user do
  u = User.new :email =&amp;gt; 'foo@example.com'
  UserMailer.activation(u)
end
&lt;/pre&gt;
&lt;br /&gt;
The email_preview UI received a considerable overhaul as well and any suggestions are greatly welcome.  A solid update all around, and special thanks go to Lars Klevan for &lt;a href="https://gist.github.com/1013155"&gt;inspiring&lt;/a&gt; several of these updates!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-2494908748793364010?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/FdhcOeTUQUI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/2494908748793364010/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/06/email-preview-transactions-and.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/2494908748793364010?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/2494908748793364010?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/FdhcOeTUQUI/email-preview-transactions-and.html" title="Email Preview + Transactions and Factories" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm5.static.flickr.com/4126/4968422200_1ab99c61ef_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/06/email-preview-transactions-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYDQ3w7fip7ImA9WhZVGEg.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8079124528352346393</id><published>2011-05-31T11:09:00.000-05:00</published><updated>2011-05-31T11:09:32.206-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-31T11:09:32.206-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="search" /><title>Realtime Search: Solr vs Elasticsearch</title><content type="html">&lt;img src="http://3.bp.blogspot.com/_JPG8ZAkcP0o/TLNrJoBHFaI/AAAAAAAAARQ/f_g7_YLfBjA/s320/burger_king_crown-281x269.jpg" /&gt;&lt;br /&gt;
&lt;h2&gt;Who will win the crown for the King of Realtime Search?&lt;/h2&gt;&lt;br /&gt;
Oh the suspense! &amp;nbsp;The results are available on the Socialcast Engineering blog...&lt;br /&gt;
&lt;a href="http://engineering.socialcast.com/2011/05/realtime-search-solr-vs-elasticsearch/"&gt;http://engineering.socialcast.com/2011/05/realtime-search-solr-vs-elasticsearch/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8079124528352346393?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/ThagI2UuOz4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8079124528352346393/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/05/realtime-search-solr-vs-elasticsearch.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8079124528352346393?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8079124528352346393?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/ThagI2UuOz4/realtime-search-solr-vs-elasticsearch.html" title="Realtime Search: Solr vs Elasticsearch" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_JPG8ZAkcP0o/TLNrJoBHFaI/AAAAAAAAARQ/f_g7_YLfBjA/s72-c/burger_king_crown-281x269.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/05/realtime-search-solr-vs-elasticsearch.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUFQngzeyp7ImA9Wx9aEkw.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8298260409647853145</id><published>2011-03-03T23:04:00.002-06:00</published><updated>2011-03-03T23:23:33.683-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-03T23:23:33.683-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="multitenant" /><category scheme="http://www.blogger.com/atom/ns#" term="security" /><title>Multitenant Ruby Gem</title><content type="html">&lt;img src="http://farm3.static.flickr.com/2177/2208105981_00668ccc54.jpg" /&gt;&lt;br /&gt;
&lt;i&gt;Multitenant: locking down your app and making cross tenant data leaks a thing of the past...since 2011&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Designing multi-tenant applications has increasingly becoming the norm for software developers.  The benefits of building an application with a multi-tenant architecture are almost too many to list (cost savings, data mining, simplified operations, etc).  But, let there be no doubt that building a multi-tenant application increases the overall complexity of the application.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href="https://github.com/wireframe/multitenant"&gt;Multitenant gem&lt;/a&gt; helps alleviate that complexity and ensures that all SQL queries executed adhere to the scoping of the current tenant.  It works with any Rails3 compatible application without any modifications to your existing schema.&lt;br /&gt;
&lt;br /&gt;
To configure, add the &lt;code&gt;belongs_to_multitenant&lt;/code&gt; declaration to any model that has an association to a particular tenant. &amp;nbsp;This will inject a new SQL scope into your model.&lt;br /&gt;
&lt;pre&gt;class User &amp;lt; ActiveRecord::Base
  belongs_to :tenant
  belongs_to_multitenant
end&lt;/pre&gt;&lt;br /&gt;
Now, all queries executed within the &lt;code&gt;Multitenant.with_tenant&lt;/code&gt; block will automatically be scoped to the current tenant.  No longer must you worry about a stray &lt;code&gt;MyModel.all&lt;/code&gt; call exposing data to the wrong users!  Also, any new records created within this block will automatically be associated to the current tenant.  Another win for ensuring data separation!&lt;br /&gt;
&lt;pre&gt;# use an Rails around_filter to setup this block
Multitenant.with_tenant current_tenant do
  # queries within this block are automatically
  # scoped to the current tenant
  User.all

  # records created within this block are
  # automatically assigned to the current tenant
  User.create :name =&amp;gt; 'Bob'
end&lt;/pre&gt;&lt;br /&gt;
Unintentionally leaking data between tenants should be the &lt;b&gt;NUMBER ONE&lt;/b&gt; concern when building multi-tenant apps and it surprises me how few resources are available to assist developers in building multi-tenant apps and ensuring data separation between tenants.  So, if you're building a multi-tenant Rails app, make sure the &lt;a href="https://github.com/wireframe/multitenant"&gt;Multitenant gem&lt;/a&gt; is in your toolbox!&lt;br /&gt;
&lt;br /&gt;
Source code is 100% opensource and available on Github:&lt;br /&gt;
&lt;a href="https://github.com/wireframe/multitenant"&gt;https://github.com/wireframe/multitenant&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Gem is available on rubygems:&lt;br /&gt;
&lt;a href="https://rubygems.org/gems/multitenant"&gt;https://rubygems.org/gems/multitenant&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8298260409647853145?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/TOoiG1fN56U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8298260409647853145/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/03/multitenant-locking-down-your-app-and.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8298260409647853145?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8298260409647853145?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/TOoiG1fN56U/multitenant-locking-down-your-app-and.html" title="Multitenant Ruby Gem" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm3.static.flickr.com/2177/2208105981_00668ccc54_t.jpg" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/03/multitenant-locking-down-your-app-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ECQ3k5fSp7ImA9Wx9VFU4.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8022449933452160152</id><published>2011-01-31T21:41:00.000-06:00</published><updated>2011-01-31T21:41:02.725-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-31T21:41:02.725-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="resque" /><title>Stay Connected with the ensure_connected gem</title><content type="html">&lt;img src="http://farm5.static.flickr.com/4134/4926598654_981f0fea9e.jpg" /&gt;&lt;br /&gt;
&lt;i&gt;This is your application when "MYSQL has gone away"&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Most developers working with long running background processes will eventually run into the infamous "&lt;b&gt;MySQL server has gone away&lt;/b&gt;" error.  This error is thrown when the background processes are idle for an extended period of time and lose their active connection to the database.&lt;br /&gt;
&lt;br /&gt;
There are a few &lt;a href="http://gist.github.com/238999"&gt;workarounds&lt;/a&gt; floating around the interwebs to deal with this exception, but the &lt;a href="https://github.com/wireframe/ensure_connected"&gt;ensure_connected gem&lt;/a&gt; is a clean and programmatic solution to take care of this issue once and for all.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href="https://github.com/wireframe/ensure_connected"&gt;ensure_connected gem&lt;/a&gt; wraps any method with a check to ensure that you are connected to the database before performing work.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;class Worker
  def do_something
    #do your work here.  
    #you're connected to the database baby!
  end
  ensure_connected :do_something
end&lt;/pre&gt;&lt;br /&gt;
And Resque users have it even better with the &lt;a href="https://github.com/wireframe/resque-ensure-connected"&gt;resque-ensure-connected plugin&lt;/a&gt;.  Simply drop this gem into your project and &lt;b&gt;all&lt;/b&gt;&amp;nbsp;of your background job invocations will be wrapped with the ensure_connected check.  This works with the Backgrounded Resque integration as well!&lt;br /&gt;
&lt;br /&gt;
As always, sourcecode is 100% opensource and available on Github.&lt;br /&gt;
&lt;a href="https://github.com/wireframe/ensure_connected"&gt;https://github.com/wireframe/ensure_connected&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/wireframe/resque-ensure-connected"&gt;https://github.com/wireframe/resque-ensure-connected&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Gems are available for download through Gemcutter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8022449933452160152?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/o0Hpudpd0i0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8022449933452160152/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/01/stay-connected-with-ensureconnected-gem.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8022449933452160152?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8022449933452160152?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/o0Hpudpd0i0/stay-connected-with-ensureconnected-gem.html" title="Stay Connected with the ensure_connected gem" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm5.static.flickr.com/4134/4926598654_981f0fea9e_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/01/stay-connected-with-ensureconnected-gem.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0EARXw9fyp7ImA9Wx9WFEw.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-1732636161834597496</id><published>2011-01-18T23:38:00.001-06:00</published><updated>2011-01-18T23:40:44.267-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-18T23:40:44.267-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="email" /><title>Send Mail with Backgrounded Mail</title><content type="html">&lt;a href="http://1.bp.blogspot.com/_yocpuDtbm4c/TTZ3K2kg0MI/AAAAAAAAABc/2qB_m-gopNE/s1600/mailroom_sendmail.jpg"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_yocpuDtbm4c/TTZ3K2kg0MI/AAAAAAAAABc/2qB_m-gopNE/s400/mailroom_sendmail.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;This is what life was like &lt;i&gt;before&lt;/i&gt; backgrounded_mail...&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Don't tie up your precious resources blocking on a synchronous call to sendmail or SMTP! &amp;nbsp;Use the &lt;a href="https://github.com/wireframe/backgrounded_mail"&gt;backgrounded_mail gem&lt;/a&gt; to make your application more responsive and deliver email using your background processing library.&lt;br /&gt;
&lt;br /&gt;
The background_mail API is very straightforward. &amp;nbsp;Instead of invoking '&lt;code&gt;deliver&lt;/code&gt;' on the mail object, simply use the '&lt;code&gt;deliver_backgrounded&lt;/code&gt;' method to push the email into your background processing queue.&lt;br /&gt;
ex:&lt;br /&gt;
&lt;pre&gt;UserNotifier.welcome(user).deliver_backgrounded&lt;/pre&gt;&lt;br /&gt;
The backgrounded_mail gem is compatible with Rails3 or any project using the &lt;a href="https://github.com/mikel/mail"&gt;mail gem&lt;/a&gt;. &amp;nbsp;It is built on the &lt;b&gt;world class&lt;/b&gt;&amp;nbsp;&lt;a href="https://github.com/wireframe/backgrounded"&gt;backgrounded library&lt;/a&gt; to enqueue email delivery using your background processing library of choice (ex: resque, delayed job, workling, etc). &lt;br /&gt;
&lt;br /&gt;
Sourcecode is available on Github.&lt;br /&gt;
&lt;a href="https://github.com/wireframe/backgrounded_mail"&gt;https://github.com/wireframe/backgrounded_mail&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-1732636161834597496?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/371gKmbI_J8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/1732636161834597496/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/01/send-mail-with-backgrounded-mail.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1732636161834597496?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1732636161834597496?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/371gKmbI_J8/send-mail-with-backgrounded-mail.html" title="Send Mail with Backgrounded Mail" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_yocpuDtbm4c/TTZ3K2kg0MI/AAAAAAAAABc/2qB_m-gopNE/s72-c/mailroom_sendmail.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/01/send-mail-with-backgrounded-mail.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04FQnY-eyp7ImA9Wx9WEEs.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-31629420246522652</id><published>2011-01-14T21:25:00.000-06:00</published><updated>2011-01-14T21:25:13.853-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-14T21:25:13.853-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="process" /><category scheme="http://www.blogger.com/atom/ns#" term="development" /><title>The 3 E's of Engineering</title><content type="html">&lt;img src="http://farm4.static.flickr.com/3208/2959055526_d3307d8c70.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
Anyone that calls themself an engineer knows "the look".  The one that strangers give when you first tell them that you're a Software Engineer.  The blank stare, the glazed eyes and the usual lame response about how smart, lucky or rich you must be.&lt;br /&gt;
&lt;br /&gt;
I've had the great privilege over the years to train new developers and mentor developers of the future, and the most important event of someone's first day is for me to burst that great big pink bubble and bring them back to reality.&lt;br /&gt;
&lt;br /&gt;
Software development is &lt;b&gt;hard&lt;/b&gt;.  It's equal parts art and science, and being a real engineer is anything but easy. &amp;nbsp;Engineer has three e's and every single engineer needs to master &lt;b&gt;The 3 E's of Engineering&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Estimate&lt;/h2&gt;A good software engineer needs to estimate tasks accurately.  It takes a &lt;b&gt;lot&lt;/b&gt; of practice and gets easier the more experience and domain knowledge you have with a given product or area of expertise.  &lt;br /&gt;
&lt;br /&gt;
Estimation is about giving a level of effort "guesstimate" for how difficult a task will be. An estimate should be stated in difficulty, not duration.  Estimating using hours is an extremely common and &lt;a href="http://scrum.jeffsutherland.com/2010/04/story-points-why-are-they-better-than.html"&gt;disastrous&lt;/a&gt; &lt;a href="http://www.agilegamedevelopment.com/2006/03/hours-vs-story-points.html"&gt;mistake&lt;/a&gt; and all estimates should be given in points or a it is as simple as stating a low, medium, or high level of effort for a given task.  &lt;br /&gt;
&lt;br /&gt;
An estimate is almost always resource agnostic meaning that if a task that is estimated as "difficult", it will be difficult regardless of who actually does the work.  For example, an experienced developer may be able to complete a difficult task quicker than a less experienced developer, but this is explained by each developer having a different personal velocity and does &lt;b&gt;not&lt;/b&gt; change the fact that the task was labeled as "difficult".&lt;br /&gt;
&lt;br /&gt;
An estimate is an agreement for how difficult a task will be, but it is &lt;b&gt;not&lt;/b&gt;&amp;nbsp;a contract.  It is not intended to answer, "How long will this take?" or "When will this be done?".  The estimate is only one part of the equation necessary for building an effective product roadmap and you need to understand all the other variables before you can see the whole picture (prioritization and resource allocation are the other critical components, but that topic is for another blog post!). :)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Evaluate&lt;/h2&gt;A good software engineer needs to evaluate risk and more importantly to minimize unnecessary risk whenever possible.  This is another area where practice and experience are the best ways to improve, but there are a few rules of thumb that should help every developer.&lt;br /&gt;
&lt;br /&gt;
Be fluid, react quickly, and over-communicate changes or risks as they occur.  If waterfall software development has taught me anything (other than how horribly broken it is), it is that you can &lt;b&gt;never&lt;/b&gt;&amp;nbsp;plan every aspect of a project before starting implementation.  Even the best planned and estimated project will still encounter bumps along the way and need adjust accordingly.  Over-communicating possible risks to the rest of your team and business is the perfect place for &lt;a href="http://www.socialcast.com/"&gt;Socialcast&lt;/a&gt;.  Avoid excessive meetings and endless email threads when raising a red flag.&lt;br /&gt;
&lt;br /&gt;
Use &lt;a href="http://code.flickr.com/blog/2009/12/02/flipping-out/"&gt;feature flags&lt;/a&gt; to roll out new features and test changes incrementally.  Support turning on (and off) features at a granular level for an individual user or subset of users before going live for everyone.&lt;br /&gt;
&lt;br /&gt;
Without monitoring, success is just a guess.  How do you know that releasing a feature actually improved your product?  How do you rollback a change if it negatively impacts your user experience?&lt;br /&gt;
&lt;br /&gt;
Beware the word "re-write" and never &lt;b&gt;ever&lt;/b&gt;&amp;nbsp;listen to someone that says you need to "re-write and start over".  It's a &lt;a href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;death sentence&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
If something doesn't feel right, it probably isn't.  Put the breaks on when changes make the task no longer relevant.  Failing fast is actually a &lt;b&gt;huge&lt;/b&gt;&amp;nbsp;success!  Don't linger on the time you've invested on something that get's thrown away.  Dwell on the time saved not finishing the irrelevant project.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Execute&lt;/h2&gt;A good software engineer needs to execute and complete tasks quickly and efficiently.  Excellence in this area boils down to following a few very simple rules.&lt;br /&gt;
&lt;br /&gt;
Build the absolute simplest thing that could possibly work.  No more.  No less&lt;br /&gt;
&lt;br /&gt;
It's not done until it's tested and if it's not tested, it's already broken (you just don't know it yet).&lt;br /&gt;
&lt;br /&gt;
Don't check it off as complete until it's in production.  Minimize risk to get that shit done and out the door!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-31629420246522652?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/kW1mxDbqalA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/31629420246522652/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2011/01/3-es-of-engineering.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/31629420246522652?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/31629420246522652?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/kW1mxDbqalA/3-es-of-engineering.html" title="The 3 E's of Engineering" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm4.static.flickr.com/3208/2959055526_d3307d8c70_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2011/01/3-es-of-engineering.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcHQHs4eCp7ImA9Wx9SGU8.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-6636736786145703925</id><published>2010-12-08T17:05:00.001-06:00</published><updated>2010-12-09T14:37:11.530-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-09T14:37:11.530-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Anatomy of an Effective Unit Test</title><content type="html">&lt;a href="http://1.bp.blogspot.com/_yocpuDtbm4c/TQBR1QacfVI/AAAAAAAAABU/FqFtno1aIbE/s1600/doogietest.png"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/_yocpuDtbm4c/TQBR1QacfVI/AAAAAAAAABU/FqFtno1aIbE/s400/doogietest.png" width="382" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Writing an effective and expressive unit test is just as important as writing clean application code.  Unfortunately, there are not nearly enough resources for &lt;b&gt;how&lt;/b&gt;&amp;nbsp;to write good unit tests. &lt;br /&gt;
&lt;br /&gt;
Dr. Sonnek is in the house today with a prescription. &amp;nbsp;Let's start by defining the "&lt;b&gt;Anatomy of an Effective Unit Test&lt;/b&gt;".&lt;br /&gt;
&lt;br /&gt;
Every single unit test should be composed of 4 parts: &lt;b&gt;Description, Setup, Execution, Assertion&lt;/b&gt;. &lt;br /&gt;
&lt;br /&gt;
To be perfectly clear, I'm &lt;i&gt;not&lt;/i&gt; encouraging writing complex unit tests in any way, shape or form. &amp;nbsp;These 4 aspects of a unit test can, and should be, fulfilled in as few as 5 lines of code. &amp;nbsp;Each testing library will have a slightly different feel, but the majority of old school testing frameworks rely on procedural execution or conventions to express these rules (JUnit, JSUnit, TestUnit, etc).&lt;br /&gt;
&lt;pre&gt;#this is teh old school way to write tests
#test description
def test_creating_blog_sends_email
  #setup
  user = User.create!

  #execute
  blog = user.blogs.create!

  #assert
  assert Mail.sent?
end
&lt;/pre&gt;&lt;br /&gt;
Several &lt;i&gt;modern&lt;/i&gt; testing frameworks have introduced a clearer separation between these important aspects and use structural separation between setup/execution and assertion which makes it easier to scan tests and read what they are trying to accomplish.  Here's a basic example, and I'll break down each section afterwards.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#this is teh new hotness for writing tests
#describe the execution context (data, environment, etc)
context "creating blog post with new user" do
  setup do
    #setup
    user = User.create!

    #execute
    blog = user.blogs.create!
  end
  #assert
  should 'send email to author' do
    assert Mail.sent?
  end
end
&lt;/pre&gt;&lt;br /&gt;
I'll use shoulda syntax for these examples, but the same principles apply to RSpec, JSspec, etc. &amp;nbsp;Now, let's dive into the Anatomy of an Effective Unit Test...&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Description&lt;/h3&gt;Separating the setup/execution/assertions into separate blocks creates self documenting and highly readable tests. &amp;nbsp;The "description" component for each part of your test (setup, execute, assertion) is used to construct a sentence for your test. &amp;nbsp;This is a welcome change from the uglified test naming convention used by legacy test frameworks.  The previous example would read:&lt;br /&gt;
&lt;b&gt;"creating blog post with new user should send email to author"&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Setup&lt;/h3&gt;Test setup is used to initialize environmental or data dependencies before execution and assertion. &amp;nbsp;The description for your setup block should clearly describe these dependencies.&lt;br /&gt;
&lt;pre&gt;context "with new user" do
  setup do
    @user = User.new
  end
end
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Execute&lt;/h3&gt;Execution is just another &lt;i&gt;form&lt;/i&gt; of setup, and lives alongside other environmental or data setup. &amp;nbsp;The description for your execution context should match the method you are invoking and what parameters are being used for this particular invocation.&lt;br /&gt;
&lt;pre&gt;context "creating blog" do
  setup do
    blog = @user.blogs.create!
  end
end
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Assert&lt;/h3&gt;One assertion per test.  No more...no less. Old school unit tests very commonly have multiple assertions per test but Shoulda makes it &lt;b&gt;super &lt;/b&gt;simple&amp;nbsp;to encapsulate each assertion and re-use common setup/execution contexts.&lt;br /&gt;
&lt;pre&gt;should 'send email to author' do
  assert Mail.sent?
end
&lt;/pre&gt;&lt;br /&gt;
Writing effective unit tests is never easy, but structuring your unit tests correctly and understanding the importance of each aspect should make your job just a little bit easier.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-6636736786145703925?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/JIwbLglC76I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/6636736786145703925/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/12/anatomy-of-effective-unit-test.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/6636736786145703925?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/6636736786145703925?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/JIwbLglC76I/anatomy-of-effective-unit-test.html" title="Anatomy of an Effective Unit Test" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_yocpuDtbm4c/TQBR1QacfVI/AAAAAAAAABU/FqFtno1aIbE/s72-c/doogietest.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/12/anatomy-of-effective-unit-test.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUNSHY8eSp7ImA9Wx5bFUk.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-1248855476474433770</id><published>2010-10-30T22:22:00.002-05:00</published><updated>2010-10-31T12:31:39.871-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-31T12:31:39.871-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><title>Backgrounded Class Methods</title><content type="html">&lt;img src="http://4.bp.blogspot.com/_j2c0QbFdzXg/SjumeWI9JHI/AAAAAAAAAPc/nPG6rdz9S7k/s320/stay_classy.jpg" /&gt;&lt;br /&gt;
&lt;h2&gt;Stay "Class-y" Backgrounded!&lt;/h2&gt;&lt;br /&gt;
Backgrounded was originally intended to only run ActiveRecord instance methods in the background but as the library has matured, its focus has broadened into an &lt;strong&gt;elegant proxy to arbitrary background processors&lt;/strong&gt;.&lt;br /&gt;
&lt;br /&gt;
There are lots of usecases where it's necessary to run a background method on a non-ActiveRecord instance, and the latest Backgrounded release now supports running any class level method with backgrounded!  All out of the box back-ends have been updated to respect this new configuration.&lt;br /&gt;
&lt;br /&gt;
Backgrounded class methods work exactly the same as instance methods and there are no client API changes required to use them.  Just reference your class level method with a class level backgrounded declaration and you're all set.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;class User
  class &amp;lt;&amp;lt; self
    backgrounded :my_instance_method
    def my_instance_method
    end
  end
end

#run the instance method in the background
=&amp;gt; User.my_instance_method_backgrounded&lt;/pre&gt;&lt;br /&gt;
Sourcecode on Github.&lt;br /&gt;
&lt;a href="http://github.com/wireframe/backgrounded"&gt;http://github.com/wireframe/backgrounded&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Latest gem on Gemcutter.&lt;br /&gt;
&lt;a href="https://rubygems.org/gems/backgrounded"&gt;https://rubygems.org/gems/backgrounded&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-1248855476474433770?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/gRVOSJiDm2o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/1248855476474433770/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/10/stay-class-y-backgrounded-backgrounded.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1248855476474433770?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1248855476474433770?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/gRVOSJiDm2o/stay-class-y-backgrounded-backgrounded.html" title="Backgrounded Class Methods" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_j2c0QbFdzXg/SjumeWI9JHI/AAAAAAAAAPc/nPG6rdz9S7k/s72-c/stay_classy.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/10/stay-class-y-backgrounded-backgrounded.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYEQXY7eSp7ImA9Wx5UEEQ.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4907581557828797688</id><published>2010-10-14T18:15:00.000-05:00</published><updated>2010-10-14T18:15:00.801-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-14T18:15:00.801-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="performance" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="resque" /><title>New Relic + Backgrounded + Resque = Awesome</title><content type="html">If you're writing a rails app, you're already using new relic to monitor your app right?  Wouldn't it be great to get the same performance monitoring of your Backgrounded operations as well?  &lt;br /&gt;
&lt;br /&gt;
Fork it up, now you can!&lt;br /&gt;
&lt;br /&gt;
I've published a fork of the rpm_contrib gem that will automatically instrument and monitor your Backgrounded operations with absolutely &lt;strong&gt;no&lt;/strong&gt; code changes!  That's right, all you need to do is include the wireframe-rpm_contrib gem and your Backgrounded Resque tasks will automatically be tracked!&lt;br /&gt;
&lt;br /&gt;
Sourcecode available on github (fork of &lt;a href="http://github.com/newrelic/rpm_contrib"&gt;rpm_contrib&lt;/a&gt;):&lt;br /&gt;
&lt;a href="http://github.com/wireframe/rpm_contrib"&gt;http://github.com/wireframe/rpm_contrib&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Gem available on gemcutter:&lt;br /&gt;
&lt;a href="https://rubygems.org/gems/wireframe-rpm_contrib"&gt;https://rubygems.org/gems/wireframe-rpm_contrib&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The default rpm_contrib gem adds instrumentation for Resque jobs, but is not aware of Backgrounded jobs so all of the jobs were recorded as Backgrounded::Handler::ResqueHandler#perform.  My little patch fixes that so that Backgrounded Resque jobs have the correct classname/method context.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4907581557828797688?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/aWPHbbuzt5w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4907581557828797688/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/10/new-relic-backgrounded-resque-awesome.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4907581557828797688?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4907581557828797688?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/aWPHbbuzt5w/new-relic-backgrounded-resque-awesome.html" title="New Relic + Backgrounded + Resque = Awesome" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/10/new-relic-backgrounded-resque-awesome.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAHSXY8eip7ImA9Wx5VGEk.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-953479739680737618</id><published>2010-10-11T18:00:00.003-05:00</published><updated>2010-10-11T20:25:38.872-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-11T20:25:38.872-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="resque" /><title>Advanced Backgrounded Configuration</title><content type="html">&lt;img width="500px" src="http://fc07.deviantart.net/fs25/i/2008/076/6/0/Mr_Sparkle_by_GoSco.png" /&gt;&lt;br /&gt;
&lt;h3&gt;Mr. Sparkle says, "Wow!  Backgrounded is super terrific!" (loose translation)&lt;/h3&gt;&lt;br /&gt;
In the wake of the backgrounded ruby gem release, only two complaints have been raised about the library...&lt;br /&gt;
&lt;br /&gt;
First: the backgrounded API is just too damn intuitive.  It is literally so beautiful that every other background processing API feels like a giant turd.  Using backgrounded abstracts implementation details between various frameworks so developers can focus on writing kick ass apps.  Sorry folks, but I have no intention of fixing this issue.  :)&lt;br /&gt;
&lt;br /&gt;
Second: The elegance and simplicity of the backgrounded abstraction has proven too limiting for developers that need greater control 'under the hood'.  The 0.6.0 release of backgrounded should satisfy these power hungary devs by supporting an optional configuration hash for each backgrounded method.  Any options specified in your backgrounded class will be passed through to tweak the specific background processing handler in use.&lt;br /&gt;
&lt;br /&gt;
The first backgrounded handler to honor the optional configuration is the &lt;a href="http://blog.codecrate.com/2010/09/backgrounded-resque-support.html"&gt;Resque integration&lt;/a&gt; which now supports configuration of the particular queue the backgrounded method will be run in.  The default behavior is to run all backgrounded methods in a single default queue (named backgrounded) and this can be overridden by passing in the optional :queue name for any backgrounded method.&lt;br /&gt;
&lt;br /&gt;
It is vitally important that the introduction of these advanced configuration options not break the existing API.  The beauty and simplicity of the original backgrounded API is still very much intact and all existing clients will continue with the default behavior with no change. Here's an example to show how the original backgrounded methods can be configured right alongside with advanced backgrounded methods.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;class Blog
  backgrounded save_to_filesystem, :email_followers =&amp;gt; {:queue =&amp;gt; :low}

  def email_followers
  end
  def save_to_filesystem
  end
end
&lt;/pre&gt;&lt;br /&gt;
Sourcecode on Github.&lt;br /&gt;
&lt;a href="http://github.com/wireframe/backgrounded"&gt;http://github.com/wireframe/backgrounded&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Latest gem on Gemcutter.&lt;br /&gt;
&lt;a href="https://rubygems.org/gems/backgrounded"&gt;https://rubygems.org/gems/backgrounded&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-953479739680737618?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/arrSElfN-mU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/953479739680737618/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/10/advanced-backgrounded-configuration.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/953479739680737618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/953479739680737618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/arrSElfN-mU/advanced-backgrounded-configuration.html" title="Advanced Backgrounded Configuration" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/10/advanced-backgrounded-configuration.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08FQX45fip7ImA9Wx5VGE8.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4800877478381637941</id><published>2010-09-27T19:14:00.001-05:00</published><updated>2010-10-11T14:36:50.026-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-11T14:36:50.026-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="backgrounded" /><category scheme="http://www.blogger.com/atom/ns#" term="resque" /><title>Backgrounded Resque Support</title><content type="html">&lt;a href="http://www.flickr.com/photos/stark23x/55575164/" title="Strongbad sings! by stark23x, on Flickr"&gt;&lt;img alt="Strongbad sings!" height="333" src="http://farm1.static.flickr.com/33/55575164_9e602b4941.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
After an extended rest, Backgrounded is back in action with support for Resque.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://github.com/defunkt/resque"&gt;Resque&lt;/a&gt; is one of the newer background processing frameworks for ruby applications created by the amazing folks at Github.  A weekend of hacking with Resque easily persuaded me to add official support for Resque to the Backgrounded 0.4.1 release.  &lt;br /&gt;
&lt;br /&gt;
After getting Resque up and running, the Backgrounded configuration is simple (as always).&lt;br /&gt;
&lt;pre&gt;#config/initializers/backgrounded.rb
require 'backgrounded/handler/resque_handler'
Backgrounded.handler = Backgrounded::Handler::ResqueHandler.new
&lt;/pre&gt;&lt;br /&gt;
The benefits of Backgrounded really shine when new libraries like Resque pop up.  One of the primary goals of Backgrounded is to make it trivial to swap out different backend implementations and not change a single line of your application code.  Just update your configuration file and restart.  It's a thing of beauty...&lt;br /&gt;
&lt;br /&gt;
Sourcecode on Github.&lt;br /&gt;
&lt;a href="http://github.com/wireframe/backgrounded"&gt;http://github.com/wireframe/backgrounded&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Latest gem on Gemcutter.&lt;br /&gt;
&lt;a href="https://rubygems.org/gems/backgrounded"&gt;https://rubygems.org/gems/backgrounded&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4800877478381637941?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/0Vvo7OpdnHM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4800877478381637941/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/09/backgrounded-resque-support.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4800877478381637941?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4800877478381637941?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/0Vvo7OpdnHM/backgrounded-resque-support.html" title="Backgrounded Resque Support" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm1.static.flickr.com/33/55575164_9e602b4941_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/09/backgrounded-resque-support.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0cNSX47eCp7ImA9WxFVEUk.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8045083180096681089</id><published>2010-06-09T23:44:00.000-05:00</published><updated>2010-06-09T23:44:58.000-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-09T23:44:58.000-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="email_preview" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><title>email_preview Rails Plugin</title><content type="html">&lt;img src="http://farm3.static.flickr.com/2403/2207568967_010eaaa06a.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
As a developer, working with email pretty much sucks.  Crafting emails is a time consuming task where you constantly refine and tweak markup to get the &lt;strong&gt;perfect&lt;/strong&gt; layout for your message.  Heck, it's easier to do cross browser testing of your application for IE6!&lt;br /&gt;
&lt;br /&gt;
The email_preview engine is a new Rails 3 plugin I've released to help developers build kick ass emails for your Rails 3 app.  It provides a quick and easy way to preview sample emails from within your web browser and see how they will look in the end user's email client.&lt;br /&gt;
&lt;br /&gt;
The developer API is extremely simple.  You can use the standard Rails ActionMailer interface to configure each instance, or manually configure each email using the excellent &lt;a href="http://github.com/mikel/mail"&gt;mail library&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
#config/email_preview.rb&lt;br /&gt;
&lt;pre&gt;EmailPreview.register 'user activation email' do
  u = User.new :email =&gt; 'foo@example.com'
  UserNotifier.activation
end

EmailPreview.register 'simple example email' do
  Mail.new do
    to 'tom@example.com'
    from 'me@foo.com'
    body 'check this out'
  end
end

EmailPreview.register 'multipart email (html + text)' do
  Mail.new do
    from 'mikel@test.lindsaar.net'
    to 'you@test.lindsaar.net'
    subject 'This is a test email'

    text_part do
      body 'This is plain text'
    end
    html_part do
      content_type 'text/html; charset=UTF-8'
      body '&amp;lt;h1&amp;gt;This is HTML&amp;lt;/h1&amp;gt;'
    end
  end
end
&lt;/pre&gt;&lt;br /&gt;
Once you've configuring your sample emails, startup your Rails 3 application and preview them in any web browser at &lt;a href="http://localhost:3000/email_preview"&gt;http://localhost:3000/email_preview&lt;/a&gt;.  All email templates are re-rendered with each request so you can tweak/save/refresh to instantly preview how your emails will look for your users.  Talk about quick turnaround time!&lt;br /&gt;
&lt;br /&gt;
Need more assurance that your emails will look correct?  If your dev environment is configured to send emails, you can send any of your email previews to your inbox by filling out the convenient form at the top of each page.&lt;br /&gt;
&lt;br /&gt;
This plugin is 100% opensource and the code is available on github at &lt;a href="http://github.com/wireframe/email_preview"&gt;http://github.com/wireframe/email_preview&lt;/a&gt;.  Patches are *always* welcome, and if you have any questions or suggestions, please drop me a line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8045083180096681089?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/7YDBtBIDrHc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8045083180096681089/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/06/emailpreview-rails-plugin.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8045083180096681089?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8045083180096681089?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/7YDBtBIDrHc/emailpreview-rails-plugin.html" title="email_preview Rails Plugin" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm3.static.flickr.com/2403/2207568967_010eaaa06a_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/06/emailpreview-rails-plugin.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkIMRn8yfCp7ImA9WxFVF0k.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-1242041653171644375</id><published>2010-03-22T22:00:00.001-05:00</published><updated>2010-06-16T23:23:07.194-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-16T23:23:07.194-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="chrome" /><category scheme="http://www.blogger.com/atom/ns#" term="zillow" /><title>Zillow Google Chrome Extension</title><content type="html">Incase you haven't heard, &lt;a href="http://blog.codecrate.com/2010/02/mailinator-google-chrome-extension.html"&gt;I love&lt;/a&gt; &lt;a href="http://blog.codecrate.com/2009/12/delicious-google-chrome-extension.html"&gt;me some&lt;/a&gt; Google Chrome extensions.  And now &lt;a href="https://chrome.google.com/extensions/detail/hpfpmhoefhblnbnipeapenjojpcbifpo/"&gt;the Zillow Google Chrome extension&lt;/a&gt; just made looking for a new house a heck of a lot easier.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://img.skitch.com/20100313-rcag844xg4fsk182t4xp99rp7j.jpg" /&gt;&lt;br /&gt;
Grab an address, pop open the Zillow Google Chrome extension and find the estimated property value in no time! &lt;br /&gt;
&lt;br /&gt;
The Zillow Google Chrome extension is a mashup using &lt;a href="http://code.google.com/apis/maps/documentation/v3/introduction.html"&gt;the Google Maps API&lt;/a&gt; and &lt;a href="http://www.zillow.com/howto/api/GetZestimate.htm"&gt;the Zillow API&lt;/a&gt; to&amp;nbsp;&amp;nbsp;quickly&amp;nbsp;find as much detail about a property while avoiding the cluttered and slow ass real-estate websites.  Consider this the "official" successor to my &lt;a href="http://www.jroller.com/wireframe/entry/zillow_ubiquity_plugin"&gt;original Zillow Ubiquity plugin&lt;/a&gt;.  Ubiquity was cool but this extension is the cat's meow!&lt;br /&gt;
&lt;br /&gt;
The Zillow Google Chrome extension shows the estimated property value along with the estimated high/low range value. &amp;nbsp;A direct link to the detailed estimate on Zillow is also available if you want more information about the property.&lt;br /&gt;
&lt;br /&gt;
The property location is displayed with an embedded Google map along with a direct link to a full property map to dive into the street view&amp;nbsp;(on Google Maps of course). &amp;nbsp;Using Google's geolocation service also makes it possible to quickly find a location even if you don't know the full address!&lt;br /&gt;
&lt;br /&gt;
The &lt;a href="http://github.com/wireframe/zillow-chrome-extension"&gt;code is 100% opensource and available on github&lt;/a&gt;&amp;nbsp;and here&amp;nbsp;are a few ideas for future improvements.  fork away!&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;an extension browser action could detect when the user selects an address on the page&lt;/li&gt;
&lt;li&gt;prefill the popup address based on the user selected address on the page&lt;/li&gt;
&lt;li&gt;hovering over address could display an inline popup bubble with the estimate information&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/7648837050901945239-1242041653171644375?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/r3sO4dzMCKg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/1242041653171644375/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/03/zillow-google-chrome-extension.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1242041653171644375?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/1242041653171644375?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/r3sO4dzMCKg/zillow-google-chrome-extension.html" title="Zillow Google Chrome Extension" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/03/zillow-google-chrome-extension.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4ERn4yeyp7ImA9WxBbFEg.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4146350307670542410</id><published>2010-03-12T21:57:00.002-06:00</published><updated>2010-03-12T22:01:47.093-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-12T22:01:47.093-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><title>Rails "Good Touch"/"Bad Touch"</title><content type="html">&lt;img src="http://ecx.images-amazon.com/images/I/517BEfl3%2BRL._SL600_.jpg" /&gt;&lt;br /&gt;
Rails is bit like a dirty old man. &lt;br /&gt;
&lt;br /&gt;
Consider this example&lt;br /&gt;
&lt;pre&gt;class User &amp;lt; ActiveRecord::Base
&amp;nbsp;&amp;nbsp;after_save :do_something_expensive
end
&lt;/pre&gt;&lt;br /&gt;
Rails makes it trivial to update timestamps, and I found out the hard way that the underlying implementation leaves a lot to be desired.&lt;br /&gt;
&lt;pre&gt;User.first.touch
&lt;/pre&gt;&lt;br /&gt;
Easy, right? But, did you know that this useful little method will fire all of your &lt;code&gt;after_save&lt;/code&gt; callbacks? &amp;nbsp;This led to some nasty performance problems recently, and now you can now stop Rails from having it's way with you! &amp;nbsp;Bad Touch Rails!&lt;br /&gt;
&lt;br /&gt;
The &lt;b&gt;good_touch&lt;/b&gt; project is now available on &lt;a href="http://github.com/wireframe/good_touch"&gt;github&lt;/a&gt; and &lt;a href="http://rubygems.org/gems/good_touch"&gt;gemcutter&lt;/a&gt; and eliminates the overhead for updating a simple timestamp attribute. &amp;nbsp;It's as easy as using the standard/bad &lt;code&gt;touch&lt;/code&gt; method without all the horseplay.&lt;br /&gt;
&lt;pre&gt;User.first.good_touch
&lt;/pre&gt;&lt;br /&gt;
Let the touching continue...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4146350307670542410?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/bEg40DN1UHI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4146350307670542410/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/03/rails-good-touchbad-touch.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4146350307670542410?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4146350307670542410?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/bEg40DN1UHI/rails-good-touchbad-touch.html" title="Rails &quot;Good Touch&quot;/&quot;Bad Touch&quot;" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/03/rails-good-touchbad-touch.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8EQXs7fyp7ImA9WxBWFkU.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-6125890115680375271</id><published>2010-02-08T20:00:00.011-06:00</published><updated>2010-02-08T20:00:00.507-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-08T20:00:00.507-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="apple" /><title>Improvements for apn_on_rails</title><content type="html">&lt;img src="http://img.skitch.com/20100208-jn32ew6kynj5qm4qwqdaw9hcki.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
If you're using Rails and integrate with Apple Push Notifications, you are more than likely familiar with the &lt;a href="http://github.com/markbates/apn_on_rails"&gt;apn_on_rails gem&lt;/a&gt;.  It's a fantastic library and has been critical for getting off the ground with push notifications quickly.  The project is &lt;a href="http://www.metabates.com/2009/12/21/apn-on-rails-needs-a-home/"&gt;currently in flux&lt;/a&gt; and has been missing some critical features, so I decided to dig into the source myself and hack away.&lt;br /&gt;
&lt;br /&gt;
Using&amp;nbsp;&lt;a href="http://developer.apple.com/IPhone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW15"&gt;custom metadata to each push notification&lt;/a&gt;&amp;nbsp;was needed right off the bat.  Basic push notifications support a "badge", "alert", and "sound", but custom attributes can be used to push metadata that would be useful for your native application.  The original apn_on_rails plugin does not support custom attributes, but after building on the &lt;a href="http://github.com/lmarburger/apn_on_rails"&gt;work done by lmarburger&lt;/a&gt;, custom payload attributes are working great.&lt;br /&gt;
&lt;br /&gt;
Truncation of alert text is another critical feature that needed some love.  Apple limits push notifications to a maximum of 255 characters which&amp;nbsp;&lt;b&gt;includes&lt;/b&gt;&amp;nbsp;the alert text and &lt;b&gt;all&lt;/b&gt;&amp;nbsp;custom attributes so it is very important to optimize the data you're sending down and not go over the 255 character limit.  The initial gem used a very simple implementation for truncating the text, which worked fine until you started to push custom attributes as well.  Those valuable characters got eaten up in a hurry and the truncation of the alert text needed to be aware of your entire payload length.&lt;br /&gt;
&lt;br /&gt;
With a few hours of work, the entire truncation algorithm has been rewritten (with tests) and is working great.  This new algorithm will dynamically truncate your alert text based on your entire payload body.  This is a much better algorithm for text truncation and I've seen a &lt;b&gt;20% increase&lt;/b&gt;&amp;nbsp;in the amount of text sent with push alerts before the truncation kicks in (average from 179 to 215 characters).  &lt;br /&gt;
&lt;br /&gt;
I also fixed a pretty major bug when truncating notifications with multibyte characters.  Anyone using apn_on_rails for an internationalized app should definitely consider the update.&lt;br /&gt;
&lt;br /&gt;
My fork of the original apn_on_rails gem is available &lt;a href="http://github.com/wireframe/apn_on_rails"&gt;on github&lt;/a&gt;&amp;nbsp;and the&amp;nbsp;updated gem is published and&amp;nbsp;available &lt;a href="http://gemcutter.org/gems/wireframe-apn_on_rails"&gt;on gemcutter&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-6125890115680375271?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/7t_Cj4mFu2Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/6125890115680375271/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/02/improvements-for-apnonrails.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/6125890115680375271?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/6125890115680375271?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/7t_Cj4mFu2Y/improvements-for-apnonrails.html" title="Improvements for apn_on_rails" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/02/improvements-for-apnonrails.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMFQH84fip7ImA9WxBWFk0.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-5970080138164710192</id><published>2010-02-07T21:40:00.000-06:00</published><updated>2010-02-07T21:40:11.136-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-07T21:40:11.136-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="chrome" /><title>Mailinator Google Chrome Extension</title><content type="html">&lt;img src="http://i548.photobucket.com/albums/ii355/gohitech/no-spam-2x.gif" /&gt;&lt;br /&gt;
&lt;br /&gt;
Have you ever visited a website that required an email registration form, but you really didn't want to give out your primary email address?  &lt;a href="http://www.mailinator.com/"&gt;Mailinator&lt;/a&gt; is a wonderful service that helps rid the world of junk mail, one garbage email address at a time.  &lt;br /&gt;
&lt;br /&gt;
Using a one-time use email inbox is a great way to keep unsolicited email out of your way.  And now the &lt;a href="https://chrome.google.com/extensions/detail/bcnflfkionidoobcineimfoollmbalbp"&gt;Mailinator Google Chrome extension&lt;/a&gt; makes it even easier to generate a one-time email address to get you through those annoying site registration forms.&lt;br /&gt;
&lt;br /&gt;
Features include:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;one click generation of a random email address&lt;/li&gt;
&lt;li&gt;popup widget to check your mailinator email inbox&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;img src="http://img.skitch.com/20100208-8q71dhuqm9eccsdw1qa2akx53n.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
Under the covers, the plugin is very simple.  Mailinator provides a pretty decent &lt;a href="http://www.mailinator.com/widgetmaker.jsp"&gt;iframe integration&lt;/a&gt; out of the box, and generating a &lt;a href="http://www.broofa.com/Tools/Math.uuid.js"&gt;random email inbox in Javascript&lt;/a&gt; is pretty trivial.  &lt;br /&gt;
&lt;br /&gt;
The code is &lt;a href="http://github.com/wireframe/mailinator-chrome-extension"&gt;100% open source and available on github&lt;/a&gt;.  I'd love to add in a "Copy address to clipboard" feature, but it &lt;a href="http://stackoverflow.com/questions/127040/put-text-on-the-clipboard-with-firefox-safari-and-chrome"&gt;looks like Flash is the only way to accomplish that&lt;/a&gt;.  Let me know if you have any other suggestions.&lt;br /&gt;
&lt;br /&gt;
I have no affiliation with Mailinator.  I'm just a hacker that is very sick of sites requiring email addresses and not offering open ID integration.  :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-5970080138164710192?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/jonKrd2OnYY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/5970080138164710192/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/02/mailinator-google-chrome-extension.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/5970080138164710192?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/5970080138164710192?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/jonKrd2OnYY/mailinator-google-chrome-extension.html" title="Mailinator Google Chrome Extension" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/02/mailinator-google-chrome-extension.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIFRHwzfip7ImA9WxBQEk8.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4082274520281151339</id><published>2010-01-10T22:07:00.003-06:00</published><updated>2010-01-11T09:25:15.286-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-11T09:25:15.286-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="airfail" /><category scheme="http://www.blogger.com/atom/ns#" term="air" /><title>#airfail for HTML Developers</title><content type="html">&lt;div style="float: left; width: 100%;"&gt;&lt;script src="http://amplicate.com/sucks/adobe-air/widget/red-i-hate" type="text/javascript"&gt;
&lt;/script&gt;&lt;a ;="" target="_blank" href="http://amplicate.com/sucks/adobe-air" style="color: #333333; float: left; font-size: 10px; text-decoration: underline; width: 100%;" title="See all opinions on ADOBE AIR SUCKS"&gt;Opinions on ADOBE AIR&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Incase you haven't heard, &lt;a href="http://blog.codecrate.com/2009/08/end-of-adobe-air.html"&gt;Adobe AIR is a complete and utter failure for developing desktop applications with HTML/CSS/Javascript technologies&lt;/a&gt;.  It's primary purpose is to provide Flash/Flex developers the ability to exploit the desktop. &amp;nbsp;It's secondary purpose is to simply frustrate and antagonize HTML/CSS developers past the point of frustration.&lt;br /&gt;
&lt;br /&gt;
Here is a &lt;b&gt;short&lt;/b&gt;&amp;nbsp;list of the very real issues I've encountered while building and deploying AIR applications.  This is not simply a list of problems dug up from an issue tracker or forum.  Every one is an issue I've worked through to the point of pulling my hair out. &amp;nbsp;This list should serve as a warning to any potential developers considering Adobe AIR.  &lt;i&gt;You'll regret it...mark my words.  The fact that Adobe advertises AIR as a viable runtime for HTML/CSS developers is one of the greatest crimes against humanity I have witnessed in recent history.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;No HTTP Headers sent with file uploads&lt;/b&gt;&lt;br /&gt;
Dealing with HTTP headers is an extremely standard requirement especially when working with secured services that require Basic Authentication.  The AIR SDK has fairly well documented methods to configure sending HTTP headers with outgoing requests, BUT for some reason &lt;a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7cb2.html"&gt;the SDK fine print states that "custom HTTP request headers are not supported in uploads or downloads"&lt;/a&gt;. Excuse me? You're kidding right?  Multipart HTTP multipart headers are &lt;b&gt;already&lt;/b&gt;&amp;nbsp;being sent with file uploads so why the hell prevent other headers from being added?  Why support HTTP headers for &lt;b&gt;every&lt;/b&gt;&amp;nbsp;remote request &lt;b&gt;&lt;i&gt;except&lt;/i&gt;&lt;/b&gt;&amp;nbsp;for file uploads?  The AIR SDK gives you &lt;b&gt;direct access&lt;/b&gt;&amp;nbsp;to a Socket for God's sake. Do I have to write my own fucking multipart upload?&lt;br /&gt;
&lt;br /&gt;
Every single application that relies on Flash technology, which includes web based SWF projects as well as HTML and Flex AIR apps, suffer from this bug.  This blatant hole is yet another reason Flash will be dead as soon as HTML5 is more widely adopted.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;random "corrupt" installation file&lt;/b&gt;&lt;br /&gt;
Try to install an app and &lt;a href="http://www.bryanbartow.com/wp-content/uploads/2008/06/capture-1.png"&gt;receive a "corrupt file" error&lt;/a&gt;.  reinstall the app and it works fine.  Awesome.  Our QA team has encountered this issue frequently enough that it's become standard issue to just ignore the errors.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Integration with Native OS (Or lack there of)&lt;/h3&gt;AIR offers so little integration with "native" features, that it's hard to call out any particular failures and I guess that's one of my biggest gripes with AIR.  If you're building a runtime for &lt;b&gt;desktop&lt;/b&gt;&amp;nbsp;applications, having integration with the native desktop OS is a pretty necessary and compelling feature.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;No Integration with Native Notification Systems&lt;/b&gt;&lt;br /&gt;
Adobe supposedly was working on this &lt;a href="http://www.mikechambers.com/blog/2008/11/13/growl-support-for-adobe-air-applications/"&gt;back in 2008&lt;/a&gt;.  Seriously?  It takes two years to integrate with one of the most standard core features provided by modern day operating systems?  Growl has been around on Mac for years.  Linux has libnotify and Windows has Snarl.  Titanium has had this built into their SDK since practically day one.&lt;br /&gt;
&lt;br /&gt;
Sure, you can &lt;a href="http://github.com/wireframe/growl-air"&gt;&lt;b&gt;emulate&lt;/b&gt; these notifications with AIR&lt;/a&gt;, but it is such a horrible, horrible hack and forces every single app to reinvent the wheel.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;no integration with dock icons&lt;/b&gt;&lt;br /&gt;
most commonly used on Mac, but applicable to other platforms as well.  Fluid is a perfect example of exposing a simple api to control dock icons.  This is a minor issue and not directly a "bug" but this came up frequently in our business requirements and AIR was unable to perform the expected task.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;sharing cookies with browser&lt;/b&gt;&lt;br /&gt;
This is just plain disgusting.  Why in the world would you build a runtime that is &lt;a href="http://www.12robots.com/index.cfm/2009/9/22/AIR-Tip-Cookie-Sharing"&gt;coupled to the underlying OS session/cookie management system&lt;/a&gt;?  This extremely unexpected behavior which has cost me tons of troubleshooting time where a user could log into a site using their web browser and essentially "hijack" the credentials of the AIR application.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Window Handcuffs&lt;/b&gt;&lt;br /&gt;
The AIR windowing API is clunky and frustrating pile. &amp;nbsp;Not only that, but it also is broken.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://blog.codecrate.com/2009/12/air-lightweight-windows-steal-focus-by.html"&gt;lightweight windows stealing window focus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;hidden application windows on OSX will be activated and focused when receiving popup notification. (blog post coming soon)&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;No global mouse move events&lt;/b&gt;&lt;br /&gt;
With no way to detect mouse pointer location when outside of your application, it is absolutely impossible for you to build headless applications that listen and react to mouse gestures.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Crippled Webkit&lt;/h3&gt;Webkit is an extremely fast moving project.  There are new features being added every day, and when Adobe chose to include Webkit into their runtime, I'd expect them to &lt;a href="http://stackoverflow.com/questions/1569541/what-version-of-webkit-does-adobe-air-use"&gt;include info on the version of Webkit that is included&lt;/a&gt;.  Is CSS3 available?  is HTML5 available?  Well, it's up to the developer to find out with good old trial and error.  Here are some of the more "common" things I was unable to do with the specific version of webkit included in AIR 1.5.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;doesn't support webkit custom scrollbars&lt;/li&gt;
&lt;li&gt;Sporadic support for HTML5 attributes. placeholder attribute available for text input, but not textarea&lt;/li&gt;
&lt;li&gt;doesn't support title attribute for very standard browser tooltips.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
I consider these Adobe's issues because the version of Webkit included in the AIR runtime is ridiculously out of date.  I've been implementing these features in actual web apps for nearly a year now (and there's no reason the title attribute doesn't work).  AIR2 will &lt;a href="http://labs.adobe.com/wiki/index.php/AIR_2:Release_Notes#New%5FFeatures%5Fin%5FAIR%5F2"&gt;"supposedly" be updating it's version of Webkit&lt;/a&gt;, but I highly doubt that Adobe will be smart enough to provide reference documentation for developers to know what features are available.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Cross Platform Issues&lt;/h3&gt;No multiplatform runtime is perfect, but web browsers are by far the best technology available to provide a consistent cross platform experience.  Webkit was an excellent starting point for AIR, but the underlying runtime has &lt;b&gt;plenty&lt;/b&gt;&amp;nbsp;of cross platform issues.  Here are some of the major issues I've run into&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Windows&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://forums.adobe.com/thread/469531?tstart=0"&gt;Antialiasing causes purple text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://forums.adobe.com/thread/525228"&gt;System alerts for Unknown/untrusted certificates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://forums.adobe.com/thread/462791"&gt;autoupdate causes process process to hang on windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;Linux&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://forums.adobe.com/thread/525225"&gt;Images hosted on SSL CDN cause system alerts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Mac&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://forums.adobe.com/thread/529807"&gt;Keychain prompts for PrivateEncryptedData&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Performance&lt;/h3&gt;I'm &lt;b&gt;very&lt;/b&gt; aware that there is a cost to run an application in a runtime environment.  I've built Java applications for years and had to deal with the memory bloat for apps running in a Java runtime, but I don't think I'm asking a lot here. &lt;br /&gt;
&lt;br /&gt;
The majority of AIR applications are mashup/widgets that are intended to be always on background services, but nearly every AIR application sits idle at&amp;nbsp;~10% CPU on a dual core Macbook Pro. &amp;nbsp;It's simply ridiculous and again all fingers point to the underlying flash runtime.  One common trick is to &lt;a href="http://www.gskinner.com/blog/archives/2009/05/idle_cpu_usage.html"&gt;manually override the flash framerate&lt;/a&gt; to improve performance.  It's pretty ridiculous that the runtime isn't smart enough to handle this when your application is minimized or otherwise hidden.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4082274520281151339?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/9sus4IOh2Dg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4082274520281151339/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2010/01/airfail-for-html-developers.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4082274520281151339?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4082274520281151339?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/9sus4IOh2Dg/airfail-for-html-developers.html" title="#airfail for HTML Developers" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2010/01/airfail-for-html-developers.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAFR3w8eSp7ImA9WxBSFk4.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-8859876547564948305</id><published>2009-12-23T23:31:00.000-06:00</published><updated>2009-12-23T23:31:56.271-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-23T23:31:56.271-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="airfail" /><category scheme="http://www.blogger.com/atom/ns#" term="air" /><title>AIR Lightweight Windows Steal Focus By Default</title><content type="html">Lightweight windows are intended for AIR applications to show simple popup windows and notifications (like my &lt;a href="http://github.com/wireframe/growl-air"&gt;emulated growl notification system&lt;/a&gt;). &amp;nbsp;This is pretty standard stuff and there are a number of &lt;a href="http://articles.sitepoint.com/article/html-based-adobe-air"&gt;documented tutorials&lt;/a&gt; and examples that go over this.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;var options = new air.NativeWindowInitOptions();
options.type = air.NativeWindowType.LIGHTWEIGHT;
options.systemChrome = air.NativeWindowSystemChrome.NONE;
var visible = true;
var scrollbars = false;

var bounds = new air.Rectangle(200, 250, 300, 400);

var htmlLoader = air.HTMLLoader.createRootWindow(visible, options, scrollbars, bounds);
htmlLoader.window.nativeWindow.alwaysInFront = true;
htmlLoader.load(new air.URLRequest("myfile.html"));
&lt;/pre&gt;&lt;br /&gt;
Executing this simple example will show one very odd and obvious interaction issue.  Your supposedly "lightweight" window will steal the focus from your main application window. &amp;nbsp;Huh, that doesn't seem very lightweight to me. &amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
The issue is that any window that is created with "visible" set to "true" will automatically be activated once content is loaded. &amp;nbsp;  What a nice little gotcha. &amp;nbsp;Thanks for the info Adobe! &lt;br /&gt;
&lt;br /&gt;
In order to work around this issue and get your lightweight window to behave as expected, you need to initialize it to hidden and then flip it to visible after the content is loaded using a completely unnecessary extra event listener.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;htmlLoader.addEventListener(air.Event.COMPLETE, function() {
&amp;nbsp;&amp;nbsp;htmlLoader.window.nativeWindow.visible = true;
});
&lt;/pre&gt;&lt;br /&gt;
#airfail&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-8859876547564948305?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/vjvom8oIs38" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/8859876547564948305/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2009/12/air-lightweight-windows-steal-focus-by.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8859876547564948305?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/8859876547564948305?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/vjvom8oIs38/air-lightweight-windows-steal-focus-by.html" title="AIR Lightweight Windows Steal Focus By Default" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.codecrate.com/2009/12/air-lightweight-windows-steal-focus-by.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QGQXwzfSp7ImA9WxBSEE0.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-4448108891427982474</id><published>2009-12-16T18:22:00.002-06:00</published><updated>2009-12-16T18:22:00.285-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-16T18:22:00.285-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="chrome" /><title>Delicious Google Chrome Extension</title><content type="html">I use &lt;a href="http://delicious.com/"&gt;Delicious&lt;/a&gt;.  A LOT.&lt;br /&gt;
&lt;br /&gt;
It's one of the few web services that I have used religiously for years.  One of the main reasons I continue to use Delicious is sheer number of great integrations with their &lt;a href="http://delicious.com/help/api"&gt;developer API&lt;/a&gt;.  The Delicious plugin for Firefox is quite good and there is nothing&amp;nbsp;equivalent&amp;nbsp;available for my new default browser, Google Chrome...until now!&lt;br /&gt;
&lt;br /&gt;
I'm excited to officially announce the release of my&amp;nbsp;&lt;a href="https://chrome.google.com/extensions/detail/maegjfahmbklahdfelffbmnkaoicphdm"&gt;Delicious Chrome extension&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://img.skitch.com/20091216-jrcaffr3s7dcft4mw6x57bpukp.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
The Delicious Chrome extension adds a browser action that will save your current page as a Delicious bookmark. The extension will also show popular and recommended tags for the current page and you can add these tags to your bookmark with a simple click.&lt;br /&gt;
&lt;br /&gt;
But that's not all!  The Delicious Chrome extension also integrates with the standard Chrome bookmark stars.  Starring a page will automatically save the page on Delicious as well!  This should greatly help users add Delicious to their existing browsing behavior without having to learn a new tool.&lt;br /&gt;
&lt;br /&gt;
This extension is &lt;a href="http://github.com/wireframe/delicious-chrome-extension"&gt;100% opensource&lt;/a&gt; and contributions are always welcome!  Please leave any feedback here or on the &lt;a href="https://chrome.google.com/extensions/detail/maegjfahmbklahdfelffbmnkaoicphdm"&gt;extension install page&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-4448108891427982474?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/SZ55S7PKrzc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/4448108891427982474/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2009/12/delicious-google-chrome-extension.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4448108891427982474?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/4448108891427982474?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/SZ55S7PKrzc/delicious-google-chrome-extension.html" title="Delicious Google Chrome Extension" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2009/12/delicious-google-chrome-extension.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQBRHg_eCp7ImA9WxNaEUQ.&quot;"><id>tag:blogger.com,1999:blog-7648837050901945239.post-5272871283906498862</id><published>2009-11-25T16:57:00.001-06:00</published><updated>2009-11-25T17:05:55.640-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-25T17:05:55.640-06:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="performance" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>Batch Javascript Execution</title><content type="html">Let's say you're in a bit of a mess with the performance of your fancy web application's Javascript.  Your application needs to do a lot of work on the front end.  I'm talking a &lt;b&gt;lot&lt;/b&gt;&amp;nbsp;of repetitive work that takes a &lt;b&gt;long&lt;/b&gt;&amp;nbsp;time.  Since Javascript is single threaded the end user's experience is pretty horrible.  Their web browser essentially "locks up" while you do all of your work and Firefox users will even be prompted with "Unresponsive script" alerts.  How are you going to optimize this?&lt;br /&gt;
&lt;br /&gt;
The "&lt;b&gt;correct&lt;/b&gt;" answer is to either reduce the number of times you need to do the work, or to make each execution as fast as humanly possible.  Although that is a great suggestion, it's also potentially expensive and time consuming.  There comes a point of diminishing returns when you've fixed all the low-hanging fruit and you're only shaving off milliseconds with each change.  What if we could get a great user experience without going through all that work?  Well, now you can!&lt;br /&gt;
&lt;br /&gt;
The trick is to allow the browser to &lt;b&gt;breathe&lt;/b&gt;&amp;nbsp;between executions.  Executing your work in batches gives the browser time to re-draw and keeps the UI from locking up.  Although we're actually increasing the total execution time for doing your work due to the extra delays, the browser remains much more responsive and it greatly improves the end user experience.&lt;br /&gt;
&lt;br /&gt;
As an example, consider an application that is constructing a *huge* table via javascript.  (Let's ignore the fact that the unit of work is not optimized like it should be)&lt;br /&gt;
&lt;pre&gt;$.each(myHugeArray, appendTable);
function appendTable() {
  $('&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;test&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;').appendTo($('#myTable'));
}
&lt;/pre&gt;&lt;br /&gt;
&lt;img src="http://farm4.static.flickr.com/3392/3578131663_c1a86da79d.jpg" /&gt;&lt;br /&gt;
This is your Javascript...&lt;br /&gt;
&lt;br /&gt;
Given enough iterations, this implementation will cause the browser to grind to a halt until the work is complete.  Instead of doing all the work at once, we can break it up into batches to help the end user experience.  Now, let's look at this solution that does the &lt;b&gt;exact same work&lt;/b&gt;, just broken up into batches.&lt;br /&gt;
&lt;pre&gt;doInBatchesWithDelay(myHugeArray, appendTable, 5, 10);
function appendTable() {
  $('&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;test&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;').appendTo($('#myTable'));
}
function doInBatchesWithDelay(array, callback, batchSize, delay) {
  for (var x = 0; x &amp;lt; batchSize; x++) {
    if (array.length &amp;gt;0) {
      callback(array.pop());
    }
  }
  if (array.length &amp;gt; 0) {
    setTimeout(function() { 
      doInBatchesWithDelay(array, callback, batchSize, delay) 
    }, delay);
  }
}
&lt;/pre&gt;&lt;br /&gt;
&lt;a href="http://www.hulu.com/watch/17201/saturday-night-live-sloths"&gt;&lt;img src="http://img.skitch.com/20091125-rp1sqth55am4iqmagethds2hx7.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;
This is your Javascript in BATCHES!  It's still slow, but it'll kick your face in a punch fight!&lt;br /&gt;
&lt;br /&gt;
This might be considered a &lt;b&gt;hack&lt;/b&gt;, but not so!  The end user experience is all that matters, and they have no concern for what tricks we have to use to keep the UI snappy.  The cost to profile and optimize applications is usually quite high and this is something you can easily drop in &lt;b&gt;today&lt;/b&gt;&amp;nbsp;and quiet the complaints of application sluggishness.  Addressing their problem with this small change will give you some headroom to work on rearchitecting your slow-ass implementation if needed.&lt;br /&gt;
&lt;br /&gt;
Now, go forth and batch!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7648837050901945239-5272871283906498862?l=blog.codecrate.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/code_poet/~4/3qMyUH2i2Bw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.codecrate.com/feeds/5272871283906498862/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.codecrate.com/2009/11/batch-javascript-execution.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/5272871283906498862?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7648837050901945239/posts/default/5272871283906498862?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/code_poet/~3/3qMyUH2i2Bw/batch-javascript-execution.html" title="Batch Javascript Execution" /><author><name>Ryan Sonnek</name><uri>https://profiles.google.com/105080906850660010182</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-gu_CEp2Uql8/AAAAAAAAAAI/AAAAAAAAACM/IWqxczzoJ1c/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm4.static.flickr.com/3392/3578131663_c1a86da79d_t.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.codecrate.com/2009/11/batch-javascript-execution.html</feedburner:origLink></entry></feed>

