<?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" xml:lang="en-US">
  <title>Depixelate - Home</title>
  <id>tag:www.depixelate.com,2009:mephisto/</id>
  <generator version="0.7.3" uri="http://mephistoblog.com">Mephisto Noh-Varr</generator>
  
  <link href="http://www.depixelate.com/" rel="alternate" type="text/html" />
  <updated>2009-01-24T06:53:14Z</updated>
  <link rel="self" href="http://feeds.feedburner.com/depixelate" type="application/atom+xml" /><entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2009-01-24:5487</id>
    <published>2009-01-24T06:51:00Z</published>
    <updated>2009-01-24T06:53:14Z</updated>
    <category term="Quickbooks" />
    <category term="Rails" />
    <link href="http://www.depixelate.com/2009/1/24/rails-quickbooks-integration-without-actionwebservice" rel="alternate" type="text/html" />
    <title>Rails / QuickBooks integration without ActionWebService</title>
<content type="html">
            &lt;p&gt;I will try to do a full writeup later but for now know that ActionWebService is now neither required nor desired for QuickBooks integration.&lt;/p&gt;

&amp;lt;script src="http://gist.github.com/51372.js"&gt;&amp;lt;/script&gt;

&lt;p&gt;Views&lt;/p&gt;

&amp;lt;script src="http://gist.github.com/51370.js"&gt;&amp;lt;/script&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/oYo_DIvUZ20" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-11-18:5485</id>
    <published>2008-11-18T06:47:00Z</published>
    <updated>2008-11-18T06:50:48Z</updated>
    <category term="Quickbooks" />
    <category term="Rails" />
    <link href="http://www.depixelate.com/2008/11/18/qbwc-mini" rel="alternate" type="text/html" />
    <title>qbwc-mini</title>
<content type="html">
            &lt;h3&gt;What is it?&lt;/h3&gt;

&lt;p&gt;Sinatra ruby server implementing QBWC callbacks in &amp;lt; 75 LOC&lt;/p&gt;

&lt;h3&gt;Introduction&lt;/h3&gt;

&lt;p&gt;I've blogged extensively on QuickBooks integration in Rails.&lt;/p&gt;

&lt;p&gt;The short story is that working with ActionWebService sucks.&lt;/p&gt;

&lt;p&gt;mini-qbwc is an experiment in simplicity and non-suckiness.&lt;/p&gt;

&lt;h3&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sinatra&lt;/li&gt;
&lt;li&gt;Hpricot&lt;/li&gt;
&lt;li&gt;fast_xs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Setup&lt;/h3&gt;

&lt;p&gt;1) Install prerequisites&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo gem install sinatra hpricot fast_xs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2) Clone project&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git clone git://github.com/zackchandler/qbwc-mini.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3) Start server&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby qbwc.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4) Install app.qwc in QBWC and run sync&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/wiPwLa2Ee78" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-10-31:5483</id>
    <published>2008-10-31T06:59:00Z</published>
    <updated>2008-10-31T07:00:23Z</updated>
    <category term="Merb" />
    <link href="http://www.depixelate.com/2008/10/31/coming-home-to-merb" rel="alternate" type="text/html" />
    <title>Coming home to Merb</title>
<content type="html">
            &lt;p&gt;The highlight of RailsConf 2007 for me was discovering Merb on the plane ride up to Portland.&lt;/p&gt;

&lt;p&gt;I remember reading the router code and thinking, wow, I can understand this (as opposed to alias_method_chain madness of Rails)!  I wrote a &lt;a href="http://depixelate.com/2007/5/31/taking-merb-for-a-spin"&gt;sample app&lt;/a&gt; and enjoyed playing with this new framework.&lt;/p&gt;

&lt;p&gt;Fast-forward to today and my life has been mainly consumed with &lt;a href="http://www.servicesidekick.com"&gt;my startup&lt;/a&gt; which is written in Rails.  I love Rails but I've had the Merb itch ever since...&lt;/p&gt;

&lt;p&gt;Luckily a recent micro app allowed me to reacquaint myself with Merb.&lt;/p&gt;

&lt;p&gt;Here are a few notes I made while deploying a merb app to Debian Etch using Capistrano and Passenger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apache &amp;amp; Passenger&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get install apache2 apache2-prefork-dev
$ sudo gem install passenger
$ sudo passenger-install-apache2-module
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add to /etc/apache2/apache2.config&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /usr/bin/ruby1.8
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create /etc/apache2/sites-available/myproject&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
  ServerName www.myproject.com
  DocumentRoot /var/www/apps/myproject/current
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enable site&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo a2ensite myproject
$ sudo /etc/init.d/apache2 restart
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Capistrano&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;deploy.rb&lt;/p&gt;

&amp;lt;script src="http://gist.github.com/21236.js"&gt;&amp;lt;/script&gt;

&lt;pre&gt;&lt;code&gt;$ sudo deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Other details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I installed all gems locally for the app like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install -i gems/ merb-core --no-ri --no-rdoc
$ gem install -i gems/ rest-client --no-ri --no-rdoc
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;config.ru&lt;/p&gt;

&amp;lt;script src="http://gist.github.com/21245.js"&gt;&amp;lt;/script&gt;

&lt;p&gt;That's it.  Passenger was super easy to install and configure.&lt;/p&gt;

&lt;p&gt;Thanks to Ezra for hacking up Merb way back when and to all the core team for making Merb a very viable and solid framework.&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/RTwZMWZ45fo" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-07-22:5479</id>
    <published>2008-07-22T06:19:00Z</published>
    <updated>2008-07-22T06:19:30Z</updated>
    <category term="javascript" />
    <category term="jQuery" />
    <link href="http://www.depixelate.com/2008/7/22/jquery-crud-deletion-patterns" rel="alternate" type="text/html" />
    <title>jQuery CRUD deletion patterns</title>
<content type="html">
            &lt;p&gt;Like a lot of rubyists, I've been playing with &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt; and lov'in it.  Here is a little pattern I've been using to  generically handle ajax deletion of resources through jQuery.&lt;/p&gt;

&lt;p&gt;How are you doing this?  Is there a better, cleaner or simpler way?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// ajaxify CRUD delete links
$('a.crud_delete_link').livequery('click', function(){
  if (!confirm('Are you sure?'))
    return false
  var self = this
  $.ajax({
    // window.location.href should be ok most of the time unless the resource is nested...
    // in that case use the rel attribute 
    url: $(self).attr('rel') != '' ? $(self).attr('rel') : window.location.href, 
    data: { _method: 'delete' },
    type: 'POST',
    success: function() {
      if ($(self).hasClass('hide_li')) {
        $(self).parents('li').fadeOut('fast')
      } else {
        // trim off record identifer and return to "index" page or refresh page
        // example: /admin/clusters/1 =&amp;gt; /admin/clusters
        // example: /account/logo =&amp;gt; /account/logo
        var parts = window.location.pathname.match(/^(.*)\/(\d+)$/)
        window.location = (parts == null) ? window.location : parts[1]
      }
    }
  })
  return false
})
&lt;/code&gt;&lt;/pre&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/Rum0ddIQQeU" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-07-22:5478</id>
    <published>2008-07-22T06:07:00Z</published>
    <updated>2008-07-22T06:07:51Z</updated>
    <category term="Misc" />
    <link href="http://www.depixelate.com/2008/7/22/hacking-with-the-master" rel="alternate" type="text/html" />
    <title>Hacking with the master</title>
<content type="html">
            &lt;p&gt;&lt;img src="http://thechandlers.smugmug.com/photos/336275452_xBQdW-O.jpg" alt="Hacking with the master" /&gt;&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/A9SWtNXngN8" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-05-24:5475</id>
    <published>2008-05-24T17:16:00Z</published>
    <updated>2008-05-24T17:18:46Z</updated>
    <category term="Rails" />
    <link href="http://www.depixelate.com/2008/5/24/rails-migrations-for-large-text-columns" rel="alternate" type="text/html" />
    <title>Rails migrations for large text columns</title>
<content type="html">
            &lt;p&gt;Sometimes you need big text columns in your database.&lt;/p&gt;

&lt;p&gt;For example, I'm using &lt;a href="http://codeforpeople.rubyforge.org/svn/bj/trunk/README"&gt;bj&lt;/a&gt; to asynchronously process large xml files.&lt;/p&gt;

&lt;p&gt;By default, the bj database migrations create :text columns types which map to mysql TEXT columns.  In mysql, TEXT columns are limited to 64K of data.  Luckily there are the MEDIUMTEXT and LONGTEXT column types for holding large data.&lt;/p&gt;

&lt;p&gt;So how do you get these types when using Rails migrations?&lt;/p&gt;

&lt;p&gt;Just specify the limit:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class AdjustBjColumnTypes &amp;lt; ActiveRecord::Migration
  def self.up
    # this bumps col from TEXT to MEDIUMTEXT in mysql
    change_column :bj_job, :stdin, :text, :limit =&gt; 64.kilobytes + 1
    change_column :bj_job_archive, :stdin, :text, :limit =&gt; 64.kilobytes + 1
  end

  def self.down
    # this bumps col from MEDIUMTEXT to TEXT in mysql
    change_column :bj_job, :stdin, :text
    change_column :bj_job_archive, :stdin, :text
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html"&gt;Mysql Data Type Storage Requirements&lt;/a&gt;&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/qKU7rj6-z58" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-05-10:5473</id>
    <published>2008-05-10T01:23:00Z</published>
    <updated>2008-05-10T01:26:45Z</updated>
    <category term="Misc" />
    <link href="http://www.depixelate.com/2008/5/10/nathan-walker-chandler" rel="alternate" type="text/html" />
    <title>Nathan Walker Chandler</title>
<content type="html">
            &lt;p&gt;&lt;img src="http://thechandlers.smugmug.com/photos/292064236_VuPvM-S.jpg" alt="Nathan Walker Chandler" /&gt;&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/q5TknpK2Ul0" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-05-02:5471</id>
    <published>2008-05-02T07:17:00Z</published>
    <updated>2008-05-02T07:17:34Z</updated>
    <category term="Plugins" />
    <category term="Quickbooks" />
    <category term="Rails" />
    <link href="http://www.depixelate.com/2008/5/2/acts_as_quickbooks_model" rel="alternate" type="text/html" />
    <title>acts_as_quickbooks_model</title>
<content type="html">
            &lt;p&gt;Announcing the &lt;a href="http://github.com/zackchandler/acts_as_quickbooks_model"&gt;acts_as_quickbooks_model plugin&lt;/a&gt; for simplifying the parsing of qbXML messages into ActiveRecord model attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Customer &amp;lt; ActiveRecord::Base
  acts_as_quickbooks_model
end

xml = &amp;lt;&amp;lt;-XML
&amp;lt;CustomerRet&amp;gt;
  &amp;lt;ListID&amp;gt;150000-933272658&amp;lt;/ListID&amp;gt;
  &amp;lt;Name&amp;gt;Abercrombie, Kristy&amp;lt;/Name&amp;gt;
  &amp;lt;FullName&amp;gt;Abercrombie, Kristy&amp;lt;/FullName&amp;gt;
  &amp;lt;FirstName&amp;gt;Kristy&amp;lt;/FirstName&amp;gt;
  &amp;lt;LastName&amp;gt;Abercrombie&amp;lt;/LastName&amp;gt;`
  &amp;lt;BillAddress&amp;gt;
    &amp;lt;Addr1&amp;gt;Kristy Abercrombie&amp;lt;/Addr1&amp;gt;
    &amp;lt;Addr2&amp;gt;5647 Cypress Hill Rd&amp;lt;/Addr2&amp;gt;
    &amp;lt;City&amp;gt;Bayshore&amp;lt;/City&amp;gt;
    &amp;lt;State&amp;gt;CA&amp;lt;/State&amp;gt;
    &amp;lt;PostalCode&amp;gt;94326&amp;lt;/PostalCode&amp;gt;
  &amp;lt;/BillAddress&amp;gt;
  &amp;lt;Phone&amp;gt;415-555-6579&amp;lt;/Phone&amp;gt;
  &amp;lt;Email&amp;gt;kristy@samplename.com&amp;lt;/Email&amp;gt;
&amp;lt;/CustomerRet&amp;gt;
XML

customer = Customer.new(:qbxml =&amp;gt; xml)
customer.list_id # =&amp;gt; "150000-933272658"
customer.name # =&amp;gt; "Abercrombie, Kristy"
...
&lt;/code&gt;&lt;/pre&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/84-UWF0LLds" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-04-23:5465</id>
    <published>2008-04-23T21:28:00Z</published>
    <updated>2008-04-24T15:28:22Z</updated>
    <category term="Misc" />
    <link href="http://www.depixelate.com/2008/4/23/ruby-xml-parsing-benchmarks" rel="alternate" type="text/html" />
    <title>Ruby XML Parsing Benchmarks</title>
<content type="html">
            &lt;p&gt;Benchmark.  Benchmark.  Benchmark.&lt;/p&gt;

&lt;p&gt;I had heard it a million times.&lt;/p&gt;

&lt;p&gt;So rather than just blindly &lt;em&gt;trust&lt;/em&gt; that &lt;a href="http://code.whytheluckystiff.net/hpricot/"&gt;Hpricot&lt;/a&gt; was faster than &lt;a href="http://www.germane-software.com/software/rexml/docs/tutorial.html"&gt;REXML&lt;/a&gt; for my next project I whipped up a quick benchmark.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby test/benchmarks/xml_parsing.rb 
Rehearsal -------------------------------------------
REXML     3.300000   1.190000   4.490000 (  4.637418)
Hpricot   9.010000   3.370000  12.380000 ( 12.645412)
--------------------------------- total: 16.870000sec

              user     system      total        real
REXML     3.320000   1.100000   4.420000 (  4.456072)
Hpricot   8.870000   3.340000  12.210000 ( 12.438566)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;lt;strike&gt;Wow - at least with the type of XML data I'll be working with, REXML is massively faster.&amp;lt;/strike&gt;
&amp;lt;strike&gt;See the &lt;a href="http://pastie.caboo.se/185724"&gt;full benchmark code&lt;/a&gt;.&amp;lt;/strike&gt; See update below...&lt;/p&gt;

&lt;p&gt;P.S. &lt;a href="http://libxml.rubyforge.org/"&gt;libxml&lt;/a&gt; claims to be much faster than REXML.  But I found it poorly documented and very unstable (at least on Leopard it would continually crash IRB with bus errors - yikes).  If anyone else has had better luck with libxml drop a note in the comments...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 4-24-08&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://onrails.org/"&gt;Lee&lt;/a&gt; commented with a nice diff showing a much cleaner way to use Hpricot which makes it much faster than REXML!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby test/benchmarks/xml_parsing.rb 
Result are identical
Rehearsal -------------------------------------------
REXML     3.370000   1.160000   4.530000 (  4.712390)
Hpricot   1.280000   0.390000   1.670000 (  1.703311)
---------------------------------- total: 6.200000sec

              user     system      total        real
REXML     3.360000   1.090000   4.450000 (  4.716999)
Hpricot   1.270000   0.380000   1.650000 (  1.684723)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here is the &lt;a href="http://pastie.caboo.se/186218"&gt;new benchmark code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks Lee!&lt;/p&gt;

&lt;p&gt;Now, anyone want to add a libxml section to the benchmark?&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/EiE5DE9R9us" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-04-18:5462</id>
    <published>2008-04-18T00:02:00Z</published>
    <updated>2008-05-02T07:17:52Z</updated>
    <category term="Git" />
    <category term="Plugins" />
    <category term="Quickbooks" />
    <link href="http://www.depixelate.com/2008/4/18/quickbooks-integration-plugin-moves-to-github" rel="alternate" type="text/html" />
    <title>QuickBooks Integration Plugin moves to GitHub</title>
<content type="html">
            &lt;p&gt;I've just moved my &lt;a href="http://github.com/zackchandler/quickbooks_integration_plugin"&gt;QuickBooks Integration Plugin&lt;/a&gt;  over to &lt;a href="http://github.com/zackchandler/quickbooks_integration_plugin"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've also added callback support for the newer serverVersion method...&lt;/p&gt;

&lt;p&gt;Clone it here:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/zackchandler/quickbooks_integration_plugin.git
&lt;/code&gt;&lt;/pre&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/Luq6hKrSUPs" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-03-31:5457</id>
    <published>2008-03-31T04:31:00Z</published>
    <updated>2008-03-31T04:31:50Z</updated>
    <category term="Git" />
    <link href="http://www.depixelate.com/2008/3/31/trustcommerce-gem-moves-to-github" rel="alternate" type="text/html" />
    <title>TrustCommerce gem moves to github</title>
<content type="html">
            &lt;p&gt;I've just moved the &lt;a href="http://trustcommerce.rubyforge.org/"&gt;TrustCommerce Subscription gem&lt;/a&gt; repository over to &lt;a href="http://github.com/zackchandler/trustcommerce"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Get it while it's hot:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/zackchandler/trustcommerce.git
&lt;/code&gt;&lt;/pre&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/OvatYBb-CPI" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-03-18:5455</id>
    <published>2008-03-18T00:00:00Z</published>
    <updated>2008-04-16T20:23:37Z</updated>
    <category term="Quick tips" />
    <category term="Rails" />
    <link href="http://www.depixelate.com/2008/3/18/recurring-billing-with-trustcommerce" rel="alternate" type="text/html" />
    <title>Recurring Billing with TrustCommerce</title>
<content type="html">
            &lt;p&gt;I recently refactored some very old billing code where I used API calls from my &lt;a href="http://trustcommerce.rubyforge.org"&gt;TrustCommerce gem&lt;/a&gt; directly in my controllers - wow, that's ugly!&lt;/p&gt;

&lt;p&gt;Knowing that &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;skinny controller fat model&lt;/a&gt; is the way to go I ramped up for a quick refactor...&lt;/p&gt;

&lt;p&gt;Here are a few snippets that came out of the refactor that may be of use to others designing a subscription-based site:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accounts&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class Account &amp;lt; ActiveRecord::Base
  ...
  before_create :build_dependencies

  has_one :billing_detail, :dependent =&gt; :destroy
  delegate :pricing_plan, :to =&gt; :billing_detail
  ...

  private

    def build_dependencies
      # accounts start off on the free trial plan
      build_billing_detail(:pricing_plan_id =&gt; PricingPlan.find_by_cents(0).id)
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Pricing Plans schema&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class CreatePricingPlans &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :pricing_plans do |t|
      t.string    :name,  :null =&gt; false
      t.integer   :cents, :null =&gt; false
      t.datetime  :deleted_at
      t.timestamps
      # add booleans here that reflect allowances of plan
      # Example: If accounts on plan are able to use SSL
      t.integer :ssl, :null =&gt; false
      ...
    end

    # create new plans
    PricingPlan.create!(:name =&gt; 'Free', :cents =&gt; 0, :ssl =&gt; true, ...)
    ...
  end

  def self.down
    drop_table :pricing_plans
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Pricing Plans model&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class PricingPlan &amp;lt; ActiveRecord::Base
  acts_as_paranoid
  has_many :billing_detail
  has_many :accounts, :through =&gt; :billing_detail

  def paying_plan?
    cents &gt; 0
  end

  def free?
    cents &amp;lt;= 0
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Billing Details schema&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class AddBillingDetails &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :billing_details do |t|
      t.integer   :account_id, :null =&gt; false
      t.integer   :pricing_plan_id, :null =&gt; false
      t.string    :billing_id
      t.string    :cardholder_name
      t.string    :card_type
      t.string    :credit_card
      t.datetime  :expiration_date
      t.timestamps
    end
  end

  def self.down
    drop_table :billing_details
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Billing Details model&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;
class BillingDetail &amp;lt; ActiveRecord::Base
  before_validation :update_trustcommerce_account_if_needed
  before_validation :create_trustcommerce_account_if_needed
  before_save :hash_credit_card

  belongs_to :account
  belongs_to :pricing_plan, :with_deleted =&gt; true

  validates_presence_of :account_id, :pricing_plan_id
  validates_associated :account, :pricing_plan

  validates_presence_of :billing_id,      :if =&gt; :paying_plan?, :message =&gt; 'Billing id required for paid plan'
  validates_presence_of :cardholder_name, :if =&gt; :paying_plan?, :message =&gt; 'Cardholder name required for paid plan'
  validates_presence_of :credit_card,     :if =&gt; :paying_plan?, :message =&gt; 'Credit card required for paid plan'

  # always reload pricing plan so before_validation can check the new plan
  def paying_plan?
    pricing_plan(true).paying_plan?
  end

  private

    def update_trustcommerce_account_if_needed
      if paying_plan? &amp;&amp; !billing_id.blank?
        params = if credit_card =~ /^\*+/
          # no change to credit card, just update amount
          trustcommerce_parameters.only(:billingid, :amount)
        else
          trustcommerce_parameters
        end
        logger.debug "TrustCommerce::Subscription.update(#{params.inspect})"
        response = TrustCommerce::Subscription.update(params)
        logger.debug "TrustCommerce::Subscription.update response: #{response.inspect}"
        if !response.respond_to?(:[]) || response[:status] != 'accepted'
          errors.add(:billing_id, 'Your billing or credit card information appears to be incomplete or incorrect.')
          false
        end
      end
    end

    def create_trustcommerce_account_if_needed
      if paying_plan? &amp;&amp; billing_id.blank?
        params = trustcommerce_parameters.except(:billingid)
        logger.debug "TrustCommerce::Subscription.create(#{params.inspect})"
        response = TrustCommerce::Subscription.create(params)
        logger.debug "TrustCommerce::Subscription.create response: #{response.inspect}"
        if response.respond_to?(:[]) &amp;&amp; response[:status] == 'approved'
          self.billing_id = response[:billingid]
        else
          errors.add(:billing_id, 'Your billing or credit card information appears to be incomplete or incorrect.')
          false
        end
      end
    end

    def trustcommerce_parameters
      {
        :billingid  =&gt; billing_id,
        :name       =&gt; cardholder_name,
        :cc         =&gt; credit_card,
        :exp        =&gt; expiration_date.strftime('%m%y'),
        :amount     =&gt; pricing_plan(true).cents,
        :cycle      =&gt; '30d'
      }
    end

    def hash_credit_card
      self.credit_card = "************#{credit_card[-4..-1]}" if !credit_card.blank?
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: There is the use of at least one of the &lt;a href="http://pastie.caboo.se/10707"&gt;hash extensions&lt;/a&gt; I use in these snippets.&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/yhURBhkjjtY" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-02-16:138</id>
    <published>2008-02-16T23:07:00Z</published>
    <updated>2008-02-17T00:39:32Z</updated>
    <category term="Git" />
    <link href="http://www.depixelate.com/2008/2/16/squawk-micro-micro-blogging-engine" rel="alternate" type="text/html" />
    <title>Squawk Micro - micro blogging engine</title>
<content type="html">
            &lt;p&gt;What does it take to write a micro blogging engine leveraging &lt;a href="http://git.or.cz"&gt;Git&lt;/a&gt; for version control and &lt;a href="http://ozmm.org/posts/git_post_commit_for_profit.html"&gt;auto-publishing&lt;/a&gt; of the blog and &lt;a href="http://daringfireball.net/projects/markdown"&gt;Markdown&lt;/a&gt; and &lt;a href="http://www.kuwata-lab.com/erubis"&gt;Erubis&lt;/a&gt; for the views?&lt;/p&gt;

&lt;p&gt;Not much.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="http://github.com/zackchandler/squawk-micro"&gt;Squawk Micro&lt;/a&gt; or&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/zackchandler/squawk-micro.git
&lt;/code&gt;&lt;/pre&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/tOglBTBdQ5w" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2008-02-16:139</id>
    <published>2008-02-16T16:45:00Z</published>
    <updated>2008-02-21T15:18:38Z</updated>
    <category term="Misc" />
    <link href="http://www.depixelate.com/2008/2/16/yoga-one-year-later" rel="alternate" type="text/html" />
    <title>Yoga - one year later</title>
<content type="html">
            &lt;p&gt;I've done yoga every day for the past year.&lt;/p&gt;

&lt;p&gt;I first mentioned &lt;a href="http://www.depixelate.com/2007/4/18/yoga-on-rails"&gt;how I got started&lt;/a&gt; last April when I completed the eight week program in &lt;a href="http://amazon.com/o/ASIN/0875969216/"&gt;Moving Toward Balance: 8 Weeks of Yoga with Rodney Yee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I do the amazing &lt;a href="http://www.yogatoday.com"&gt;Yoga Today&lt;/a&gt; videos 3-5 times a week and the other days do my own routine.&lt;/p&gt;

&lt;p&gt;Tip: Currently the &lt;a href="http://www.yogatoday.com"&gt;Yoga Today&lt;/a&gt; site doesn't allow for full screen viewing so I stream the videos directly from the &lt;a href="http://www.brightcove.tv/channel.jsp?channel=1114200956"&gt;Yoga Today Channel on BrightCove&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For other software developers out there I highly recommend Yoga to counteract the effects of our daily work positions (sitting in a chair in front of a computer) and promotion of overall health.&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/P8RN46M6WFw" height="1" width="1"/&gt;</content>  </entry>
  <entry xml:base="http://www.depixelate.com/">
    <author>
      <name>zackchandler</name>
    </author>
    <id>tag:www.depixelate.com,2007-12-13:136</id>
    <published>2007-12-13T16:48:00Z</published>
    <updated>2008-01-11T01:31:41Z</updated>
    <category term="Quick tips" />
    <category term="Rails" />
    <link href="http://www.depixelate.com/2007/12/13/actionwebservice-with-rails-2-0" rel="alternate" type="text/html" />
    <title>ActionWebService with Rails 2.0</title>
<content type="html">
            &lt;p&gt;As of Rails 2.0 ActionWebService has been swapped out for the sexier ActiveResource.
This is a great thing for most people as we'd all rather to use AR and HTTP as opposed to AWS and SOAP
but sometimes third-party software integrations require SOAP...&lt;/p&gt;

&lt;p&gt;The first thing to know is that AWS has been &lt;a href="http://svn.rubyonrails.org/rails/ousted"&gt;ousted&lt;/a&gt; to it's
&lt;a href="http://svn.rubyonrails.org/rails/ousted/actionwebservice"&gt;new location&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So if you're going to run a project requiring AWS on Edge Rails:&lt;/p&gt;

&lt;p&gt;1) Freeze to Edge Rails&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rake rails:freeze:edge
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2) Freeze ousted AWS to &lt;a href="http://errtheblog.com/posts/50-vendor-everything"&gt;vendor/gems&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mkdir -p vendor/gems
$ svn export http://svn.rubyonrails.org/rails/ousted/actionwebservice vendor/gems/actionwebservice
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3) Load AWS and dependent directories on boot&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;environment.rb&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Rails::Initializer.run do |config|
  config.load_paths += %W( #{RAILS_ROOT}/app/apis )
  config.load_paths += Dir["#{RAILS_ROOT}/vendor/gems/**"].map do |dir|
    File.directory?(lib = "#{dir}/lib") ? lib : dir
  end
  ...
end

require 'actionwebservice'
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4) Load lib needed for testing using :invoke&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;test/test_helper.rb&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'action_web_service/test_invoke.rb'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Happy SOAPing...&lt;/p&gt;
          &lt;img src="http://feeds.feedburner.com/~r/depixelate/~4/YPSslLWF7GY" height="1" width="1"/&gt;</content>  </entry>
</feed>
