<?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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;D0YDQn07cCp7ImA9WxBUGE0.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475</id><updated>2010-03-05T11:06:13.308-05:00</updated><title>Common Sense Software</title><subtitle type="html">Alex's ideas on avoiding over-complicating software development.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.alexrothenberg.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>35</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/CommonSenseSoftware" /><feedburner:info uri="commonsensesoftware" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DUYGR3w9cCp7ImA9WxBXFk0.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5327631245171651344</id><published>2010-01-27T09:10:00.005-05:00</published><updated>2010-01-27T10:45:26.268-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-27T10:45:26.268-05:00</app:edited><title>What to do when ActiveRecord thinks an Oracle key is a decimal</title><content type="html">I recently created a model for an existing database table using the &lt;a href="http://www.alexrothenberg.com/2009/11/generate-models-from-tables-legacy-data.html"&gt;legacy_data gem&lt;/a&gt; and was confused when my primary key showed up in scientific notation.  It turned out the issue was due to sloppiness in the table definition and could be easily fixed once I understood what ActiveRecord was doing.&lt;br /&gt;&lt;br /&gt;I created a &lt;code&gt;Person&lt;/code&gt; model connected to the &lt;code&gt;people&lt;/code&gt; table&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Person &lt; ActiveRecord::Base&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;but when I went into script/console the primary key showed up as a &lt;code&gt;BigDecimal&lt;/code&gt; when I expected an &lt;code&gt;integer&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ script/console &lt;br /&gt;Loading development environment (Rails 2.3.4)&lt;br /&gt;&gt;&gt; Person.first.id&lt;br /&gt;=&gt; #&lt;BigDecimal:37378fc,'0.1002484442 5E11',12(16)&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This wasn't what I wanted and would cause problems in my app when it tried to build a url with that id like &lt;code&gt;http://localhost:3000/people/10024844425.0&lt;/code&gt;.  The rails routing engine would see the .0, treat it as a format (like .xml or .json) and get confused.  Let's look at why this is happening.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt; Person.columns_hash['id']&lt;br /&gt;=&gt; #&lt;ActiveRecord::ConnectionAdapters::OracleEnhancedColumn:0x37391c0 @default=nil, @type=:decimal, @null=true, @name="id", @table_name="people", @scale=nil, @sql_type="NUMBER", @precision=nil, @primary=true, @forced_column_type=nil, @limit=nil&gt;&lt;br /&gt;&gt;&gt; Person.columns_hash['id'].type&lt;br /&gt;=&gt; :decimal&lt;br /&gt;&gt;&gt; Person.columns_hash['id'].sql_type&lt;br /&gt;=&gt; "NUMBER"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We see that ActiveRecord is treating this column as a &lt;code&gt;:decimal&lt;/code&gt; because it's sql_type is &lt;code&gt;NUMBER&lt;/code&gt;.  It turns out this is correct because an Oracle number is a decimal unless you specify it to have 0 digits after the decimal point (scale of 0).  Here's the &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209"&gt;documentation from Oracle&lt;/a&gt; (the last sentence is my bold)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;NUMBER Datatype&lt;br /&gt;&lt;br /&gt;The NUMBER datatype stores fixed and floating-point numbers. Numbers of virtually any magnitude can be stored and are guaranteed portable among different systems operating Oracle, up to 38 digits of precision.&lt;br /&gt;&lt;br /&gt;The following numbers can be stored in a NUMBER column:&lt;br /&gt;&lt;br /&gt;Positive numbers in the range 1 x 10-130 to 9.99...9 x 10125 with up to 38 significant digits&lt;br /&gt;&lt;br /&gt;Negative numbers from -1 x 10-130 to 9.99...99 x 10125 with up to 38 significant digits&lt;br /&gt;&lt;br /&gt;Zero&lt;br /&gt;&lt;br /&gt;Positive and negative infinity (generated only by importing from an Oracle Version 5 database)&lt;br /&gt;&lt;br /&gt;For numeric columns, you can specify the column as:&lt;br /&gt;&lt;br /&gt;column_name NUMBER &lt;br /&gt;&lt;br /&gt;Optionally, you can also specify a precision (total number of digits) and scale (number of digits to the right of the decimal point):&lt;br /&gt;&lt;br /&gt;column_name NUMBER (precision, scale) &lt;br /&gt;&lt;br /&gt;If a precision is not specified, the column stores values as given. If no scale is specified, the scale is zero.&lt;br /&gt;&lt;br /&gt;Oracle guarantees portability of numbers with a precision equal to or less than 38 digits. You can specify a scale and no precision:&lt;br /&gt;&lt;br /&gt;column_name NUMBER (*, scale) &lt;br /&gt;&lt;br /&gt;In this case, the precision is 38, and the specified scale is maintained.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;When you specify numeric fields, it is a good idea to specify the precision and scale. This provides extra integrity checking on input.&lt;/b&gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;Let's look in my database and sure enough the ID is a number&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ sqlplus myusername/mypassword@localhost:1521/mydatabase.world&lt;br /&gt;&lt;br /&gt;SQL*Plus: Release 10.2.0.4.0 - Production on Wed Jan 27 09:15:09 2010&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Connected to:&lt;br /&gt;Oracle Database 10g Release 10.2.0.4.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; desc people;&lt;br /&gt; Name        Null?    Type&lt;br /&gt; ----------------------------------------- -------- ----------------------------&lt;br /&gt; ID          NUMBER&lt;br /&gt; NAME          VARCHAR2(10)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you are allowed to change your database you can create a migration like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ script/generate migration change_person_id_to_integer&lt;br /&gt;STUBBING MckinseyLDAP&lt;br /&gt;      exists  db/migrate&lt;br /&gt;      create  db/migrate/20100127145747_change_person_id_to_integer.rb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;now edit the migration&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ChangePersonIdToInteger &lt; ActiveRecord::Migration&lt;br /&gt;  def self.up&lt;br /&gt;    change_column(:people, :id, :integer)  &lt;br /&gt;  end&lt;br /&gt;  def self.down&lt;br /&gt;    change_column(:people, :id, :decimal)  &lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In my case there were other applications using this table and I was not allowed to change it so I implemented a fix in Ruby to tell my model to treat this column as an integer even though it was defined as a decimal in the database.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#config/initializers/legacy_data_type_coercion.rb&lt;br /&gt;module LegacyDataTypeCoercion&lt;br /&gt;  def set_integer_columns *col_names&lt;br /&gt;    col_names.each do |col_name|&lt;br /&gt;      columns_hash[col_name.to_s].instance_eval do&lt;br /&gt;        @type = :integer&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;ActiveRecord::Base.extend(LegacyDataTypeCoercion)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#app/models/person.rb&lt;br /&gt;class Person &lt; ActiveRecord::Base&lt;br /&gt;  set_integer_columns :id&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We defined a method &lt;code&gt;set_integer_columns&lt;/code&gt; that will force ActiveRecord to treat the columns we specify as integers.  In our Person model we declare  &lt;code&gt;:id&lt;/code&gt; is an integer column.  Let's test it out!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ script/console &lt;br /&gt;Loading development environment (Rails 2.3.4)&lt;br /&gt;&gt;&gt; Person.first.id&lt;br /&gt;=&gt; 10024844425&lt;br /&gt;&gt;&gt; Person.columns_hash['id'].type&lt;br /&gt;=&gt; :integer&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Just as expected id is now an integer and we can go ahead building the rest of our application.&lt;br /&gt;&lt;br /&gt;This is not an issue with all Oracle tables as if the column was defined as &lt;code&gt;NUMBER(10)&lt;/code&gt; (with a precision and implicit scale of 0) then ActiveRecord will interpret it as an integer automatically based on the parentheses in the data type - i.e NUMBER(10) &lt;a href="http://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L224-238"&gt;ActiveRecord&lt;/a&gt; or &lt;a href="http://github.com/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb#L192-197"&gt;Oracle Enhanced&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5327631245171651344?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/vKuIwh7SW84" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5327631245171651344/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5327631245171651344" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5327631245171651344?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5327631245171651344?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/vKuIwh7SW84/what-to-do-when-activerecord-thinks.html" title="What to do when ActiveRecord thinks an Oracle key is a decimal" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2010/01/what-to-do-when-activerecord-thinks.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkABQn04eSp7ImA9WxBQFEw.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-7553984893765104079</id><published>2010-01-13T16:30:00.000-05:00</published><updated>2010-01-13T16:39:13.331-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-13T16:39:13.331-05:00</app:edited><title>Freezing a gem that has native extensions</title><content type="html">I like to freeze all the gems I use as we run in a shared hosting environment and need to our apps isolated from each other.  Deployments are also handled by an operational team that does not intimately understand our applications so keeping our deployments to a single capistrano command &lt;span style="font-family:Courier new"&gt;cap&amp;nbsp;deploy:migrations&lt;/span&gt; has been a big win for us. Freezing most gems is pretty straightforward and has been built in since Rails 2.1.  When dealing with a gem that requires native extensions to be built there's only one additional step to add to your Capfil.&lt;br /&gt;&lt;br /&gt;Let's say we want to localize &lt;a href="http://github.com/whymirror/hpricot"&gt;hpricot&lt;/a&gt; which does include native C extensions.&lt;br /&gt;&lt;br /&gt;First tell Rails about your gem by adding a config.gem line to your environment.rb&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Rails::Initializer.run do |config|&lt;br /&gt;  ...&lt;br /&gt;  config.gem 'hpricot'&lt;br /&gt;  ...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we can ask rails about its configured gems&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ rake gems&lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;   - [I] hpricot &lt;br /&gt;&lt;br /&gt;   I = Installed&lt;br /&gt;   F = Frozen&lt;br /&gt;   R = Framework (loaded before rails starts)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The 'I' means its hpricot is installed on my system but not frozen in the application.  If you see '[]' instead you need to run  &lt;span style="font-family:Courier new"&gt;sudo gem install hpricot&lt;/span&gt; (add '--source http://gemcutter.org' if necessary).  At this point you could write some code to use hpricot and your application will work.  But if hpricot (or the version you're expecting) is not installed on your production server you'll be in trouble.&lt;br /&gt;&lt;br /&gt;To freeze the gem into your vendor directory run &lt;span style="font-family:Courier new"&gt;rake gems:unpack&lt;/span&gt; (optionally you can add 'GEM=hpricot' if you just want to unpack one gem).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ rake gems:unpack &lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;  Unpacked gem: '/Users/alexrothenberg/ruby/my_project/vendor/gems/hpricot-0.8.2'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can ask rails again...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ rake gems&lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;  The following gems have native components that need to be built&lt;br /&gt;    hpricot  &lt;br /&gt;&lt;br /&gt;  You're running:&lt;br /&gt;    ruby 1.8.6.287 at /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby&lt;br /&gt;    rubygems 1.3.2 at /Users/alexrothenberg/.gem/ruby/1.8, /Library/Ruby/Gems/1.8, /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8&lt;br /&gt;&lt;br /&gt;  Run `rake gems:build` to build the unbuilt gems.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Oops our vendored gem is missing hasn't built the native extensions.  Not to worry the message tells us what to do and we run &lt;span style="font-family:Courier new"&gt;rake gems:build&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ rake gems:build&lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;  Built gem: '/Users/alexrothenberg/ruby/mars-admin/vendor/gems/hpricot-0.8.2'&lt;br /&gt;  alex-rothenbergs:mars-admin alexrothenberg$ rake gems&lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;   - [F] hpricot &lt;br /&gt;&lt;br /&gt;  I = Installed&lt;br /&gt;  F = Frozen&lt;br /&gt;  R = Framework (loaded before rails starts)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can ask rails again to see that the gem is now frozen and also look in our vendor folder&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ rake gems&lt;br /&gt;  (in /Users/alexrothenberg/ruby/my_project)&lt;br /&gt;   - [F] hpricot &lt;br /&gt;&lt;br /&gt;  I = Installed&lt;br /&gt;  F = Frozen&lt;br /&gt;  R = Framework (loaded before rails starts)&lt;br /&gt;  &lt;br /&gt;  $ ls vendor/gems/hpricot-0.8.2/&lt;br /&gt;  total 72&lt;br /&gt;  -rw-r--r--   1 alexrothenberg  staff  4672 Jan 13 12:33 CHANGELOG&lt;br /&gt;  -rw-r--r--   1 alexrothenberg  staff  1048 Jan 13 12:33 COPYING&lt;br /&gt;  -rw-r--r--   1 alexrothenberg  staff  9216 Jan 13 12:33 README&lt;br /&gt;  -rw-r--r--   1 alexrothenberg  staff  8242 Jan 13 12:33 Rakefile&lt;br /&gt;  drwxr-xr-x   4 alexrothenberg  staff   136 Jan 13 12:33 ext/&lt;br /&gt;  drwxr-xr-x   3 alexrothenberg  staff   102 Jan 13 12:33 extras/&lt;br /&gt;  drwxr-xr-x   6 alexrothenberg  staff   204 Jan 13 12:39 lib/&lt;br /&gt;  drwxr-xr-x  11 alexrothenberg  staff   374 Jan 13 12:33 test/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Everything looks good and you can check this into git and now have a frozen version of the hpricot gem stored with your application.&lt;br /&gt;But if we stop here, when we deploy to our production server we'd be using the native extensions we built on your laptop which may not work on the server if you have one is 32bit and the other 64bit or you have different OS libraries installed or any number of other reasons.  &lt;br /&gt;&lt;br /&gt;To be safe, we need to rebuild the native extensions on the server when we deploy.  This is not as hard as it sounds as rails gave us the rake task &lt;span style="font-family:Courier new"&gt;rake gems:build&lt;/span&gt;.  We can ask capistrano to run that command on the server by adding the following to your &lt;span style="font-family:Courier new"&gt;Capfile&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;after "deploy:finalize_update" do&lt;br /&gt;  # build the native extensions for hpricot gem&lt;br /&gt;  run "cd #{release_path} &amp;amp;&amp;amp; #{rake} RAILS_ENV=#{rails_env} gems:build GEM=hpricot"&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now when capistrano deploys in with all the other messages you'll see something like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  ...&lt;br /&gt;  * executing "cd /opt/apps/my_project/releases/20100108185109 &amp;&amp; rake RAILS_ENV=production gems:build"&lt;br /&gt;     servers: ["your.server.com"]&lt;br /&gt;     [your.server.com] executing command&lt;br /&gt;  ** [out :: your.server.com] (in /opt/apps/my_project/releases/20100108185109)&lt;br /&gt;  ** [out :: your.server.com] Built gem: '/opt/apps/my_project/releases/20100108185109/vendor/gems/hpricot-0.8.2'&lt;br /&gt;     command finished&lt;br /&gt;  ...   &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So rails give us a few simple patterns to follow to freeze our gems in the vendor folder and with a few lines in you Capfile you can use this pattern to vendor a gem with native extensions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-7553984893765104079?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/nSCjYvNtY00" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/7553984893765104079/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=7553984893765104079" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7553984893765104079?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7553984893765104079?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/nSCjYvNtY00/freezing-gem-that-has-native-extensions.html" title="Freezing a gem that has native extensions" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2010/01/freezing-gem-that-has-native-extensions.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYNQ3k_eyp7ImA9WxNbEUk.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-7392346001872516595</id><published>2009-11-05T15:48:00.005-05:00</published><updated>2009-11-13T16:19:52.743-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-13T16:19:52.743-05:00</app:edited><title>Generate Models from Tables - Legacy Data Gem</title><content type="html">Today I'd like to announce the release of a gem I've been working on &lt;a href="http://github.com/alexrothenberg/legacy_data"&gt;Legacy Data&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Getting started on a Rails project with a large existing database can be daunting.  How to you extract all the information that's&lt;br /&gt;encoded in the database?  Do you have to understand the entire data model before you get started?  The &lt;code&gt;models_from_tables&lt;/code&gt; generator&lt;br /&gt;in the &lt;code&gt;legacy_data&lt;/code&gt; gem can help!  This generator looks into your existing database and generates ActiveRecord models based on the&lt;br /&gt;information encoded in it.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;How to use it&lt;/h2&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;To generate an ActiveRecord model for each table in the database just type&lt;br /&gt;  &lt;code&gt;script/generate models_from_tables&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you don't want all tables in the database tell it which table to model&lt;br /&gt;  &lt;code&gt;script/generate models_from_tables --table-name comments&lt;/code&gt;&lt;br /&gt;  This uses any foreign_key constraints in the database to spider the database and model the comments table and all associated tables.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you &lt;em&gt;really&lt;/em&gt; only want the comments table tell it not to follow any foreign_keys&lt;br /&gt;&lt;code&gt;script/generate models_from_tables --table-name comments --skip-associated&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you use &lt;a href="http://github.com/thoughtbot/factory_girl"&gt;factory girl&lt;/a&gt; (and everyone should) it will generate a simple factory for each model it generates&lt;br /&gt;  &lt;code&gt;script/generate models_from_tables --table-name comments --with-factories&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;(You do need to install the plugin &lt;code&gt;gem install legacy_data&lt;/code&gt; as long as http://gemcutter.org is one of your gem sources)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Examples&lt;/h3&gt;&lt;br /&gt;Several examples come with the gem source in the &lt;a href="http://github.com/alexrothenberg/legacy_data/tree/master/examples/"&gt;examples folder on github&lt;/a&gt;.  These include&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;A simple blog database tested with MySQL and Sqlite3&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Drupal 6.14 database tested with MySQL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The J2EE Petstore example tested with MySQL, Sqlite3 and Oracle&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;What kind of information can it extract from the database?&lt;/h2&gt;&lt;br /&gt;&lt;h3&gt;Associations&lt;/h3&gt;&lt;br /&gt;If the database contains foreign_key constraints it uses them to build &lt;code&gt;has_many&lt;/code&gt; or &lt;code&gt;belongs_to&lt;/code&gt; associations&lt;br /&gt;in your ActiveRecord models&lt;br /&gt;&lt;h3&gt;Validation constraints&lt;/h3&gt;&lt;br /&gt;It will generate the following types of validation constraints in your models&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;validates_uniqueness_of&lt;/strong&gt;   - For columns where the database has an index that enforces uniqueness&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;validates_presence_of&lt;/strong&gt;     - When the database column is non-nullable&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;validates_inclusion_of&lt;/strong&gt;    - For non-nullable boolean columns and custom constraints with a SQL rule &lt;em&gt;flag IN ('Y', 'N')&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;validates_numericality_of&lt;/strong&gt; - For integer columns (nullable and non-nullable)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;custom validation&lt;/strong&gt;         - For custom SQL validation rules in the database it puts a placeholder in your model with the original SQL for you to translate into Ruby&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Non-Rails naming conventions&lt;/h3&gt;&lt;br /&gt;Since the database is existing it's likely that it doesn't follow Rails naming conventions.  Not to worry as the generator will&lt;br /&gt;put the non-standard name into the generated models if it needs to.&lt;br&gt;&lt;br /&gt;&lt;br /&gt;What kinds of non-standard names can it generate?&lt;br /&gt;&lt;br /&gt;Let's look at a sample output&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Post &amp;lt; ActiveRecord::Base&lt;br /&gt;&lt;br /&gt;  set_table_name  :tbpost&lt;br /&gt;  set_primary_key :postid&lt;br /&gt;  &lt;br /&gt;  # Relationships&lt;br /&gt;  has_many :comments, :foreign_key =&amp;gt; :postid&lt;br /&gt;&lt;br /&gt;  # Constraints&lt;br /&gt;  validates_presence_of :title, :body&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Class Names&lt;/strong&gt;  - It named the model &lt;em&gt;Post&lt;/em&gt; instead of the Rails convention &lt;em&gt;Tbpost&lt;/em&gt;. The generator could not do this itself but knowing the conventions will often not apply to legacy databases it pauses after spidering the database giving you a chance to override the table to class name mapping.  It generates a yaml file &lt;code&gt;app/models/table_mappings.yml&lt;/code&gt; where you can verify or change any class name before  proceeding to generate the models.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Table Names&lt;/strong&gt;  - It overrode the table name since the actual name &lt;em&gt;tbpost&lt;/em&gt; does not match the Rails naming convention &lt;em&gt;posts&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Primary Keys&lt;/strong&gt; - It overrode the primary key since the actual column &lt;em&gt;postid&lt;/em&gt; does not match the Rails naming convention &lt;em&gt;id&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Foreign Keys&lt;/strong&gt; - It overrode the foreign key on the comment table to be &lt;em&gt;postid&lt;/em&gt; instead of the Rails naming convention &lt;em&gt;id&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-7392346001872516595?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/7P2gZekgADc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/7392346001872516595/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=7392346001872516595" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7392346001872516595?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7392346001872516595?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/7P2gZekgADc/generate-models-from-tables-legacy-data.html" title="Generate Models from Tables - Legacy Data Gem" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/11/generate-models-from-tables-legacy-data.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8EQ3Y7eip7ImA9WxNXFUw.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-820689130821847422</id><published>2009-10-02T15:39:00.002-04:00</published><updated>2009-10-02T15:46:42.802-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-02T15:46:42.802-04:00</app:edited><title>PL/SQL debugging information now a part of Oracle Enhanced Adapter v1.2.2</title><content type="html">Raimonds Simanovskis has just published a &lt;a href="http://blog.rayapps.com/2009/09/28/new-features-in-activerecord-oracle-enhanced-adapter-version-1-2-2/"&gt;version 1.2.2 of the Oracle Enhanced Adapter&lt;/a&gt; that includes the ability to capture dbms_output debug statements from your pl/sql code in the Rails log file.  This is a bit of code that I &lt;a href="http://github.com/rsim/oracle-enhanced/commit/00084e4e97d94cdab04ca1eb16275e0ed88c9490"&gt;wrote&lt;/a&gt; and &lt;a href="http://www.alexrothenberg.com/2009/08/how-to-capture-oracles-dbmsoutput-in.html"&gt;blogged about&lt;/a&gt; a few months ago so not only do I think its useful but am very excited to have contributed to something many others use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-820689130821847422?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/7H-0aNYeN9M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/820689130821847422/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=820689130821847422" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/820689130821847422?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/820689130821847422?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/7H-0aNYeN9M/plsql-debugging-information-now-part-of.html" title="PL/SQL debugging information now a part of Oracle Enhanced Adapter v1.2.2" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/10/plsql-debugging-information-now-part-of.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQHSHszeyp7ImA9WxNXFUw.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-6650565045193086208</id><published>2009-09-30T13:02:00.004-04:00</published><updated>2009-10-02T15:38:59.583-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-02T15:38:59.583-04:00</app:edited><title>Using the Whenever gem to manage scheduled cron jobs without installing it on the server</title><content type="html">I've been using &lt;a href="http://github.com/javan/whenever"&gt;Javan's Whenever gem&lt;/a&gt; to manage scheduled jobs in my project and its fantastic!!  There are many existing resources where you can learn more (&lt;a href="http://github.com/javan/whenever"&gt;readme&lt;/a&gt;, &lt;a href="http://railscasts.com/episodes/164-cron-in-ruby"&gt;railscast&lt;/a&gt; &lt;a href="http://groups.google.com/group/whenever-gem"&gt;google group&lt;/a&gt;) but I'd like to describe the specific way I'm using it  &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;When the gem is not installed on my server&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;How administrators can use Capistrano to both schedule and unschedule your jobs&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;How to use a library such as the Oracle client that requires certain environment variables&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;br /&gt;At the end of the day we want to have 2 capistrano tasks we can run to have cron call a rake task of ours on the schedule we want.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  cap schedule_jobs&lt;br /&gt;  cap unschedule_jobs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If we want to get fancy and pass a custom configuration argument that will be passed into the rake task.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  cap schedule_jobs SOME_CONFIGURATION=false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;When the gem is not installed on my server&lt;/h2&gt;&lt;br /&gt;Let's start with the Capfile&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  #Capfile&lt;br /&gt;  desc "Schedule the jobs"&lt;br /&gt;  task :schedule_job, :roles =&gt; :app, :only =&gt; { :primary =&gt; true } do&lt;br /&gt;    some_configuration = ENV['SOME_CONFIGURATION'] || true #default to true&lt;br /&gt;    arguments = ["RAILS_ENV=#{rails_env}",&lt;br /&gt;                 "APP_PATH=#{current_path}",&lt;br /&gt;                 "SOME_CONFIGURATION=#{some_configuration}"].join(' ')&lt;br /&gt;    run "cd #{current_path} &amp;amp;&amp;amp; #{rake} whenever:update_crontab #{arguments}"&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  desc "Unschedule the jobs"&lt;br /&gt;  task :unschedule_job, :roles =&gt; :app, :only =&gt; { :primary =&gt; true } do&lt;br /&gt;    run "cd #{current_path} &amp;amp;&amp;amp; #{rake} whenever:update_crontab  UNSCHEDULE=true"&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;How does this differ from the &lt;a href="http://github.com/javan/whenever/blob/master/README.rdoc"&gt;example&lt;/a&gt; on the whenever site?  Since the gem is not installed on the server we cannot call whenever from the command line so invoke the whenever:update_crontab rake task instead and to allow administrators to easily disable the scheduled jobs we define the unschedule_jobs capistrano task.  Let's take a look at the &lt;span style="font-family: 'courier new';"&gt;whenever:update_crontab&lt;/span&gt; Rake task that gets this all done.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  #lib/tasks/whenever.rake&lt;br /&gt;  namespace :whenever do&lt;br /&gt;    desc "updates crontab with our scheduled jobs"  &lt;br /&gt;    task :update_crontab =&gt; :load_whenever_gem do     &lt;br /&gt;      Whenever::CommandLine.execute({:update=&gt;true, :identifier=&gt;'YOUR_APP_NAME'})&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    task :load_whenever_gem do     &lt;br /&gt;      begin&lt;br /&gt;        gem_dir_root = "#{RAILS_ROOT}/vendor/gems/"&lt;br /&gt;        chronic_gem_dir = Dir["#{RAILS_ROOT}/vendor/gems/*"].detect do |subdir|&lt;br /&gt;          subdir.gsub(gem_dir_root,"") =~ /^(\w+-)?chronic-(\d+)/ &amp;&amp; File.exist?("#{subdir}/lib/chronic.rb")&lt;br /&gt;        end&lt;br /&gt;        require "#{chronic_gem_dir}/lib/chronic"&lt;br /&gt;&lt;br /&gt;        whenever_gem_dir = Dir["#{RAILS_ROOT}/vendor/gems/*"].detect do |subdir|&lt;br /&gt;          subdir.gsub(gem_dir_root,"") =~ /^(\w+-)?whenever-(\d+)/ &amp;&amp; File.exist?("#{subdir}/lib/whenever.rb")&lt;br /&gt;        end&lt;br /&gt;        require "#{whenever_gem_dir}/lib/whenever"&lt;br /&gt;&lt;br /&gt;      rescue MissingSourceFile =&gt; e&lt;br /&gt;        raise "Cannot find Whenever or Chronic : #{e}"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The actual whenever:update_crontab task just does the same the command line does but unless you add &lt;span style="font-family: 'courier new';"&gt;config.gem 'whenever'&lt;/span&gt; to your environment (which I don't since whenever is not needed by my app at runtime) we also have the other task that loads whenever and chronic from the vendor/gems directory.&lt;br /&gt;&lt;br /&gt;At this point we've gotten Capistrano calling a rake task to invoke whenever even though the gem is localized in my application but not installed on the server.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;How administrators can use capistrano to both schedule and unschedule your jobs&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;The whenever gem does not have any support for unscheduling but it will schedule whatever is included in your schedule.rb file so if that file tells it to schedule nothing that's the same as unscheduling.  Its easy to do that by wrapping the entire file with &lt;span style="font-family: 'courier new';"&gt;unless ENV['UNSCHEDULE']&lt;/span&gt; (remember the UNSCHEDULE parameter we passed in the Capfile?)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  #config/schedule.rb&lt;br /&gt;  unless ENV['UNSCHEDULE']&lt;br /&gt;&lt;br /&gt;    module MyApp&lt;br /&gt;      module Job&lt;br /&gt;        class CronRakeTask &lt; Whenever::Job::Default&lt;br /&gt;          def output&lt;br /&gt;            path_required&lt;br /&gt;            "cd #{@path} &amp;&amp; /usr/bin/env /usr/local/bin/cron_rake #{task} SOME_CONFIGURATION=#{ENV['SOME_CONFIGURATION']} RAILS_ENV=#{@environment}"&lt;br /&gt;          end      &lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    set :path,        ENV['APP_PATH'] || RAILS_ROOT &lt;br /&gt;    set :environment, RAILS_ENV || 'production'&lt;br /&gt;    every 1.day, :at =&gt; '10:00pm' do&lt;br /&gt;      command 'do_something', :class       =&gt; MyApp::Job::CronRakeTask, &lt;br /&gt;                              :environment =&gt; @environment, &lt;br /&gt;                              :path        =&gt; @path&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What's going on with the weird CronRakeTask?  This brings us to the final point&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;How to use a library such as the Oracle client that requires certain environment variables&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Cron loads its environment settings differently than an interactive shell and typically does not have all the environment variables you may have in your profile file.  You could solve this by adding them to the top of your crontab file but I prefer to leave that file as simple as possible and create a wrapper script to call instead of rake.  Basically the CronRakeTask does the same as Whenever's built in &lt;a href="http://github.com/javan/whenever/blob/master/lib/job_types/rake_task.rb"&gt;RakeTask&lt;/a&gt; except it calls &lt;span style="font-family: 'courier new';"&gt;/usr/local/bin/cron_rake&lt;/span&gt; instead of &lt;span style="font-family: 'courier new';"&gt;rake&lt;/span&gt;.  &lt;br /&gt;&lt;br /&gt;The cron_rake file just sets the environment variables I need then calls rake.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  #!/bin/bash&lt;br /&gt;&lt;br /&gt;  export PATH=/usr/local/lib/ruby-enterprise/bin:$PATH&lt;br /&gt;  export ORACLE_HOME=/opt/oracle&lt;br /&gt;  export LD_LIBRARY_PATH=/opt/oracle:$LD_LIBRARY_PATH&lt;br /&gt;&lt;br /&gt;  rake $@&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As I said in the beginning since I've been using the Whenever gem I no longer need to manually edit my crontab files ever and I can enable my jobs as part of my normal deployment process.  Its wonderful and I think everyone should use it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-6650565045193086208?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/WpvISmZ6Fsw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/6650565045193086208/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=6650565045193086208" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/6650565045193086208?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/6650565045193086208?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/WpvISmZ6Fsw/using-whenever-gem-to-manage-scheduled.html" title="Using the Whenever gem to manage scheduled cron jobs without installing it on the server" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/09/using-whenever-gem-to-manage-scheduled.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEFQ306eSp7ImA9WxNTF0w.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-7197348622671778074</id><published>2009-08-19T09:09:00.006-04:00</published><updated>2009-08-19T17:16:52.311-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-19T17:16:52.311-04:00</app:edited><title>How to capture Oracle's dbms_output in your Rails log file</title><content type="html">I have been writing a rails application on top of a large existing Oracle database where each table has 5+ triggers that each call several stored procedures and each of those PL/SQL stored procedures is hundreds of lines long.  Often a simple update statement fails with an ORA-xxxx exception coming from deep in the PL/SQL code and it can be tough to figure out what's gone wrong.&lt;br /&gt;&lt;br /&gt;The usual way Oracle database folks figure out what's going on is to put print statements in their code.  In oracle this looks like&lt;br /&gt;&lt;div&gt;&lt;pre&gt;  dbms_output.put_line('hi i hit this line of pl/sql');&lt;/pre&gt;&lt;/div&gt;When you're using an Oracle editor like TOAD or SQLDeveloper you have to turn output on and then will see anything that's printed.&lt;br /&gt;&lt;pre&gt;  set serveroutput on;&lt;/pre&gt;This is great if you divide the application between Rails &lt;br /&gt;  and Database developers and assume each group can work independently to write perfect code but what about the real world!  &lt;br /&gt;  Today I want to show you how I monkey patched the &lt;a href="http://github.com/rsim/oracle-enhanced"&gt;Oracle Enhanced Adapter&lt;/a&gt; to stick the dbms_output into &lt;br /&gt;  the rails log file.&lt;br /&gt;&lt;div&gt;Let's start with an simple example of a simple PL/SQL function that tells you if a string is more than 5 characters long (with some simple debugging print statements).&lt;/div&gt;&lt;pre&gt;  CREATE OR REPLACE FUNCTION&lt;br /&gt;  MORE_THAN_FIVE_CHARACTERS_LONG (some_text VARCHAR2) RETURN INTEGER&lt;br /&gt;  AS&lt;br /&gt;  longer_than_five INTEGER;&lt;br /&gt;  BEGIN&lt;br /&gt;    dbms_output.put_line('before the if -' || some_text || '-');&lt;br /&gt;    IF length(some_text) &gt; 5 THEN&lt;br /&gt;      dbms_output.put_line('it is longer than 5');&lt;br /&gt;      longer_than_five := 1;&lt;br /&gt;    ELSE&lt;br /&gt;      dbms_output.put_line('it is 5 or shorter');&lt;br /&gt;      longer_than_five := 0;&lt;br /&gt;    END IF;&lt;br /&gt;    dbms_output.put_line('about to return: ' || longer_than_five);&lt;br /&gt;    RETURN longer_than_five;&lt;br /&gt;  END;&lt;/pre&gt;Now we can run the following in TOAD&lt;br /&gt;&lt;pre&gt;  set serveroutput on;&lt;br /&gt;  select MORE_THAN_FIVE_CHARACTERS_LONG('a long string') from dual;&lt;br /&gt;  select MORE_THAN_FIVE_CHARACTERS_LONG('short') from dual;&lt;/pre&gt;And we get this output&lt;br /&gt;&lt;pre&gt;  MORE_THAN_FIVE_CHARACTERS_LONG('ALONGSTRING') &lt;br /&gt;  --------------------------------------------- &lt;br /&gt;  1                                             &lt;br /&gt;&lt;br /&gt;  1 rows selected&lt;br /&gt;&lt;br /&gt;  before the if -a long string-&lt;br /&gt;  it is longer than 5&lt;br /&gt;  about to return: 1&lt;br /&gt;&lt;br /&gt;  MORE_THAN_FIVE_CHARACTERS_LONG('SHORT') &lt;br /&gt;  --------------------------------------- &lt;br /&gt;  0                                       &lt;br /&gt;&lt;br /&gt;  1 rows selected&lt;br /&gt;&lt;br /&gt;  before the if -short-&lt;br /&gt;  it is 5 or shorter&lt;br /&gt;  about to return: 0&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;To get the same in our Rails app we just need to monkey patch the OracleEnhancedAdapter by copying what's below into your project as &lt;span style="font-family:'courier new';"&gt;config/initializers/oracle_enhanced_adapter.rb&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;  module ActiveRecord&lt;br /&gt;    module ConnectionAdapters&lt;br /&gt;      class OracleEnhancedAdapter &lt; AbstractAdapter&lt;br /&gt;        DBMS_OUTPUT_BUFFER_SIZE = 10000  #can be 1-1000000&lt;br /&gt;        DBMS_LINE_MAX_SIZE      = 1000  &lt;br /&gt;        def enable_dbms_output&lt;br /&gt;          @enable_dbms_output = true&lt;br /&gt;          execute "BEGIN dbms_output.enable(#{DBMS_OUTPUT_BUFFER_SIZE}); END;"&lt;br /&gt;        end&lt;br /&gt;        def disable_dbms_output&lt;br /&gt;          @enable_dbms_output = false&lt;br /&gt;          execute "BEGIN dbms_output.disable(); END;"&lt;br /&gt;        end&lt;br /&gt;        def dbms_output_enabled?&lt;br /&gt;          @enable_dbms_output&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;        protected&lt;br /&gt;          def log(sql, name)&lt;br /&gt;            super(sql, name)&lt;br /&gt;          ensure&lt;br /&gt;            log_all_dbms_output if dbms_output_enabled?&lt;br /&gt;          end&lt;br /&gt;&lt;br /&gt;        private  &lt;br /&gt;          def log_next_line_of_dbms_output&lt;br /&gt;            dbms_output_text, status = @connection.exec "BEGIN dbms_output.get_line(:return, :status); END;", ' '*DBMS_LINE_MAX_SIZE, 1&lt;br /&gt;            got_text = (status == 0)&lt;br /&gt;            @logger.debug "DBMS_OUTPUT: #{dbms_output_text}" if got_text&lt;br /&gt;            got_text&lt;br /&gt;          end&lt;br /&gt;        &lt;br /&gt;          def log_all_dbms_output&lt;br /&gt;            while log_next_line_of_dbms_output do&lt;br /&gt;            end&lt;br /&gt;          end&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To use it let's show a quick script/console session&lt;br /&gt;&lt;pre&gt;  &gt;&gt; ActiveRecord::Base.connection.enable_dbms_output&lt;br /&gt;  =&gt; []&lt;br /&gt;  &gt;&gt; ActiveRecord::Base.connection.select_all("select MORE_THAN_FIVE_CHARACTERS_LONG('a long string') from dual")&lt;br /&gt;  =&gt; [{"more_than_five_characters_long('alongstring')"=&gt;1}]&lt;br /&gt;&lt;/pre&gt;And what's in log/development.log&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    SQL (27.0ms)   BEGIN dbms_output.enable(10000); END;&lt;br /&gt;    SQL (25.9ms)   select MORE_THAN_FIVE_CHARACTERS_LONG('a long string') from dual&lt;br /&gt;  DBMS_OUTPUT: before the if -a long string-&lt;br /&gt;  DBMS_OUTPUT: it is longer than 5&lt;br /&gt;  DBMS_OUTPUT: about to return: 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is admittedly a very simple example but I have used this in a real application where I am updating several related ActiveRecord objects and seeing the DBMS_OUTPUT inline with the various SQL update statements has been extremely useful in tracking down a real bug in the PL/SQL procedure that has been in our system for over a year!&lt;br /&gt;&lt;br /&gt;I've &lt;a href="http://groups.google.com/group/oracle-enhanced/browse_thread/thread/91574e45b1e2cf09"&gt;submitted&lt;/a&gt; this as a &lt;a href="http://github.com/alexrothenberg/oracle-enhanced/commit/00084e4e97d94cdab04ca1eb16275e0ed88c9490"&gt;patch&lt;/a&gt; to the Oracle Enhanced Adapter so perhaps it will be included at some point so you wont have to do the monkey patching yourself.  I was somewhat surprised not to find anything similar out there so if you know of something please leave a comment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-7197348622671778074?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/U0xDj52VxPo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/7197348622671778074/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=7197348622671778074" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7197348622671778074?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7197348622671778074?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/U0xDj52VxPo/how-to-capture-oracles-dbmsoutput-in.html" title="How to capture Oracle's dbms_output in your Rails log file" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/08/how-to-capture-oracles-dbmsoutput-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcGSH0-cCp7ImA9WxJbEkU.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-672276106815230669</id><published>2009-07-22T10:26:00.001-04:00</published><updated>2009-07-22T13:00:29.358-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-22T13:00:29.358-04:00</app:edited><title>One year of blogging</title><content type="html">I just noticed that its been a year since my first post and thought I'd take a moment to reflect on the experience....&lt;br /&gt;&lt;br /&gt;I set out with a goal of writing 2-3 posts a month and have managed to write 28 articles over 12 months so I feel pretty good about that.  Most of the time I've gotten excited about something I accomplished or learned during the week. The process of writing it up turned out to be as much of a learning experience for myself as the original discovery.  As they say "Teaching is the best way to learn"!&lt;br /&gt;&lt;br /&gt;I've been most surprised how easy it is to get to the top of a Google Search results list (for very specific searches of course).  With my Google Analytics tracking I can see more than 1/2 of my hits coming from search engines and not all of them are me searching for my own name :)   Its been great when I've gotten comments from people I don't know (especially when the comments are positive).&lt;br /&gt;&lt;br /&gt;Writing this blog has also helped turn my focus outward so I'm not only working on internal projects at my company but feel (in a small way) a part of the broader community.  &lt;br /&gt;&lt;br /&gt;I hope to continue in the coming year as I'm having a lot of fun so far ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-672276106815230669?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/l15ZKojNsnQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/672276106815230669/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=672276106815230669" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/672276106815230669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/672276106815230669?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/l15ZKojNsnQ/one-year-of-blogging.html" title="One year of blogging" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/07/one-year-of-blogging.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIEQH4-fCp7ImA9WxJVE08.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-1251412314005594539</id><published>2009-06-25T09:54:00.003-04:00</published><updated>2009-06-29T20:58:21.054-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-29T20:58:21.054-04:00</app:edited><title>Using Github Through Draconian Proxies (Windows And Unix)</title><content type="html">I came across this great set of instructions on how to tunnel through a proxy to use github&lt;span style="text-decoration: underline;"&gt; - &lt;/span&gt;&lt;a href="http://returnbooleantrue.blogspot.com/2009/06/using-github-through-draconian-proxies.html"&gt;http://returnbooleantrue.blogspot.com/2009/06/using-github-through-draconian-proxies.html&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;The proxy where I work is not &lt;span style="font-style: italic;"&gt;quite&lt;/span&gt; so draconian that I need to follow these steps but its nice to have these instructions just in case :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-1251412314005594539?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/UXXgtImqgi0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/1251412314005594539/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=1251412314005594539" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/1251412314005594539?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/1251412314005594539?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/UXXgtImqgi0/using-github-through-draconian-proxies.html" title="Using Github Through Draconian Proxies (Windows And Unix)" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/06/using-github-through-draconian-proxies.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4BR3k6fCp7ImA9WxJWGEo.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-2778046913982289013</id><published>2009-06-24T16:12:00.003-04:00</published><updated>2009-06-24T17:12:36.714-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-24T17:12:36.714-04:00</app:edited><title>Metric_fu 1.1.0 released with a patch from me</title><content type="html">&lt;a href="http://metric-fu.rubyforge.org/"&gt;Metric_fu&lt;/a&gt; just released version 1.1.0 of their gem which I'm pleased to say includes a &lt;a href="http://github.com/jscruggs/metric_fu/commit/573afaf19e7a578aa0b161569290169d9298cf96"&gt;patch submitted by me&lt;/a&gt;.  This is exciting as its the first time I've had my code included by someone I don't know in one of the open source projects I admire.&lt;br /&gt;&lt;br /&gt;I was not originally going to make the patch as I thought I'd just hack around to fix the problem locally just enough to get it working but my friend &lt;a href="http://www.theagiledeveloper.com/"&gt;Matt&lt;/a&gt; encouraged me to fix the root cause which turned out to be not too hard and got me into the metric_fu source.  I always learn something when I read source from others I admire.&lt;br /&gt;&lt;br /&gt;The process of submitting the patch was pretty easy and I plan to make it a habit when I run into issues with other gems.  I,&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Experienced a problem and decided not to live with it&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Created a &lt;a href="http://github.com/alexrothenberg/metric_fu"&gt;fork of metric_fu&lt;/a&gt; on github&lt;/li&gt;&lt;li&gt;Cloned locally and iteratively made my changes until I had my fix complete&lt;/li&gt;&lt;li&gt;Rebased my changes into a single commit (I wrote &lt;a href="http://www.alexrothenberg.com/2009/06/changing-history-with-git-rebase-how-to.html"&gt;an article&lt;/a&gt; on this a few weeks ago)&lt;/li&gt;&lt;li&gt;Pushed my patch back to github on &lt;a href="http://github.com/alexrothenberg/metric_fu/tree/runtime_dependencies"&gt;a branch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Submitted my patch back to Jake's repository as &lt;a href="http://github.com/jscruggs/metric_fu/issues#issue/1"&gt;an issue&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Solving the problem of gem dependencies with github names&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I am using &lt;a href="http://github.com/relevance/rcov"&gt;relevance-rcov&lt;/a&gt; instead of the rubyforge version of rcov as it has been maintained more recently and specifically fixed a segmentation fault problem I previously faced.  The problem is that I couldn't install metric_fu as it put a gem dependency on 'rcov' in its gemspec.  What seems broken is that there's no way to put a dependency on 'rcov' but have rubygems realize that 'relevance-rcov' is a fork of rcov so should satisfy the dependency.&lt;br /&gt;&lt;br /&gt;I've seen a lot of talk recently about rubygems (including &lt;a href="http://www.rubyinside.com/why-using-require-rubygems-is-wrong-1478.html"&gt;Why Using require ‘rubygems’ Is Wrong&lt;/a&gt;, &lt;a href="http://yehudakatz.com/2009/06/15/rubygems-problems-and-proposed-solutions"&gt;RubyGems: Problems and (proposed) Solutions&lt;/a&gt; and &lt;a href="http://www.rubyinside.com/rip-ruby-packaging-system-1837.html"&gt;Rip: A Next Generation Ruby Packaging System - Watch Out RubyGems!&lt;/a&gt;) but so far I haven't found a solution to the forking and naming problem emerge.&lt;br /&gt;&lt;br /&gt;For Metric_fu I decided to change the install-time dependency to a runtime one.  This solves my basic problem of not finding relevance-rcov but also allows for more flexibile use of metric_fu.  If someone does not want to generate reek or rcov metrics why should they be forced to install those gems in order to use metric_fu?  By defering the dependency until runtime we will never hit the dependency for those metrics we are not using.&lt;br /&gt;&lt;br /&gt;You can look through the &lt;a href="http://github.com/jscruggs/metric_fu/commit/573afaf19e7a578aa0b161569290169d9298cf96"&gt;commit&lt;/a&gt; to see exactly how I did this.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Deleted the install time dependencies from metric_fu.gemspec&lt;/li&gt;&lt;li&gt;Added the runtime dependencies when a specific type of metric is instantiated by&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Inserting a verify_dependencies! strategy step to the initialization in lib/base/generator.rb&lt;/li&gt;&lt;li&gt;Implementing verify_dependencies! in each of the subclasses for different metrics in lib/generators/*&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Did it with TDD writing tests around everything before implementing my changes&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Should gems use install time dependencies? Should a gem author decide which fork of a gem is required? What do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-2778046913982289013?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/HVk8IzYw0Ys" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/2778046913982289013/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=2778046913982289013" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2778046913982289013?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2778046913982289013?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/HVk8IzYw0Ys/metricfu-110-released-with-patch-from.html" title="Metric_fu 1.1.0 released with a patch from me" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/06/metricfu-110-released-with-patch-from.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8EQ3c_eyp7ImA9WxJXFk8.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-6346906639645335189</id><published>2009-06-10T04:53:00.005-04:00</published><updated>2009-06-10T05:40:02.943-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-10T05:40:02.943-04:00</app:edited><title>Changing history with git rebase: How to combine several commits into one</title><content type="html">When I look at commits on github I'm always impressed at how concise they are.  When I read a commit I can understand the intent of the change without getting distracted by the author's journey to get there.  In contrast when I look at my commits they tend to be smaller and more incremental and meandering as I work my way down some false starts until I get to the solution I want.  I'm guessing that I'm not alone in the way I work and recently discovered &lt;a href="http://www.kernel.org/pub/software/scm/git-core/docs/git-rebase.html"&gt;git rebase&lt;/a&gt; and an &lt;a href="http://blog.madism.org/index.php/2007/09/09/138-git-awsome-ness-git-rebase-interactive"&gt;helpful tutorial&lt;/a&gt; showing how I can continue to work in my meandering style but package my changes to hide the journey before publishing to the world on github.&lt;br /&gt;&lt;br /&gt;Let me show you what I mean with some changes I recently made to &lt;a href="http://github.com/jscruggs/metric_fu/tree/master"&gt;metric_fu&lt;/a&gt;.  Over the course of a few days I made 6 small commits as you can see below.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; git log&lt;br /&gt;&lt;br /&gt;commit d4b18b16e982ac57741f7a0a12cb085bb9b0e840&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Mon Jun 1 09:55:37 2009 -0400&lt;br /&gt;&lt;br /&gt;    reverted rakefile&lt;br /&gt;&lt;br /&gt;commit ebda1cb67a2f0f0a85e51469d70919fa7c27d318&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Mon Jun 1 09:49:05 2009 -0400&lt;br /&gt;&lt;br /&gt;    refactoring of verify_dependencies&lt;br /&gt;&lt;br /&gt;commit 3d45903b64a772fd09ec07bf69880bdb29ae4944&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Fri May 29 20:40:49 2009 -0400&lt;br /&gt;&lt;br /&gt;    runtime dependency check for all gems&lt;br /&gt;&lt;br /&gt;commit 36e269bf8f0edccfb39cc767182406cdaa16a559&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Fri May 29 20:24:16 2009 -0400&lt;br /&gt;&lt;br /&gt;    logic for checking dependencies in generator base&lt;br /&gt;&lt;br /&gt;commit bb2ee9a983c6adf54a8c95ee5851c6f8d3bffaba&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Fri May 29 19:38:21 2009 -0400&lt;br /&gt;&lt;br /&gt;    made rcov dependency figure itself out when generating rcov metrics&lt;br /&gt;&lt;br /&gt;commit 750b000e5563e917f21eb1b7837e8001fb53f688&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Fri May 29 16:42:12 2009 -0400&lt;br /&gt;&lt;br /&gt;    removed gem dependency on rcov - to allow use of relevance-rcov or other github forks&lt;br /&gt;&lt;br /&gt;commit d6af5089adce9eeed4916a155c3bdaeb4be6771a&lt;br /&gt;Author: Randy Souza &lt;randy@farnsworth.(none)&gt;&lt;br /&gt;Date:   Sat May 16 08:47:37 2009 +0800&lt;br /&gt;&lt;br /&gt;    Added a simple fix for cases where Saikuro results with nested information&lt;br /&gt;    cause metrics:all to crash&lt;br /&gt;    &lt;br /&gt;    Signed-off-by: Jake Scruggs &lt;jake.scruggs@gmail.com&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I am going to combine all of these into a single commit using the incredible power of git rebase.  I find the last commit I do not want to change (the one made by Randy Souza on May 16th) and issue a git rebase command with that id.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; git rebase --interactive d6af5089adce9eeed4916a155c3bdaeb4be6771a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now my editor comes up showing the 6 changes since then giving me options of what to do.  Its very powerful, I can reorder commits, combine commits, eliminate commits.  I'm going back in time to change the past!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pick 750b000 removed gem dependency on rcov - to allow use of relevance-rcov or other github forks&lt;br /&gt;pick bb2ee9a made rcov dependency figure itself out when generating rcov metrics&lt;br /&gt;pick 36e269b logic for checking dependencies in generator base&lt;br /&gt;pick 3d45903 runtime dependency check for all gems&lt;br /&gt;pick ebda1cb refactoring of verify_dependencies&lt;br /&gt;pick d4b18b1 reverted rakefile&lt;br /&gt;&lt;br /&gt;# Rebase d6af508..d4b18b1 onto d6af508&lt;br /&gt;#&lt;br /&gt;# Commands:&lt;br /&gt;#  p, pick = use commit&lt;br /&gt;#  e, edit = use commit, but stop for amending&lt;br /&gt;#  s, squash = use commit, but meld into previous commit&lt;br /&gt;#&lt;br /&gt;# If you remove a line here THAT COMMIT WILL BE LOST.&lt;br /&gt;# However, if you remove everything, the rebase will be aborted.&lt;br /&gt;#&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In my case I want to combine these into a single commit so I change all but the first pick command to a squash and save.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pick 750b000 removed gem dependency on rcov - to allow use of relevance-rcov or other github forks&lt;br /&gt;squash bb2ee9a made rcov dependency figure itself out when generating rcov metrics&lt;br /&gt;squash 36e269b logic for checking dependencies in generator base&lt;br /&gt;squash 3d45903 runtime dependency check for all gems&lt;br /&gt;squash ebda1cb refactoring of verify_dependencies&lt;br /&gt;squash d4b18b1 reverted rakefile&lt;br /&gt;&lt;br /&gt;# Rebase d6af508..d4b18b1 onto d6af508&lt;br /&gt;#&lt;br /&gt;# Commands:&lt;br /&gt;#  p, pick = use commit&lt;br /&gt;#  e, edit = use commit, but stop for amending&lt;br /&gt;#  s, squash = use commit, but meld into previous commit&lt;br /&gt;#&lt;br /&gt;# If you remove a line here THAT COMMIT WILL BE LOST.&lt;br /&gt;# However, if you remove everything, the rebase will be aborted.&lt;br /&gt;#&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It now gives me a chance to edit the new commit message which it defaults to the original messages concatenated together.  This really is a single commit so if one of the original commits changed a file and a subsequent one undid the change that file will no longer appear in the list of modified files.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;# This is a combination of 6 commits.&lt;br /&gt;# The first commit's message is:&lt;br /&gt;removed gem dependency on rcov - to allow use of relevance-rcov or other github forks&lt;br /&gt;&lt;br /&gt;# This is the 2nd commit message:&lt;br /&gt;&lt;br /&gt;made rcov dependency figure itself out when generating rcov metrics&lt;br /&gt;&lt;br /&gt;# This is the 3rd commit message:&lt;br /&gt;&lt;br /&gt;logic for checking dependencies in generator base&lt;br /&gt;&lt;br /&gt;# This is the 4th commit message:&lt;br /&gt;&lt;br /&gt;runtime dependency check for all gems&lt;br /&gt;&lt;br /&gt;# This is the 5th commit message:&lt;br /&gt;&lt;br /&gt;refactoring of verify_dependencies&lt;br /&gt;&lt;br /&gt;# This is the 6th commit message:&lt;br /&gt;&lt;br /&gt;reverted rakefile&lt;br /&gt;&lt;br /&gt;# Please enter the commit message for your changes. Lines starting&lt;br /&gt;# with '#' will be ignored, and an empty message aborts the commit.&lt;br /&gt;# Explicit paths specified without -i nor -o; assuming --only paths...&lt;br /&gt;# Not currently on any branch.&lt;br /&gt;# Changes to be committed:&lt;br /&gt;#   (use "git reset HEAD &lt;file&gt;..." to unstage)&lt;br /&gt;#&lt;br /&gt;#       modified:   Rakefile&lt;br /&gt;#       modified:   lib/base/generator.rb&lt;br /&gt;#       modified:   lib/generators/flay.rb&lt;br /&gt;#       modified:   lib/generators/flog.rb&lt;br /&gt;#       modified:   lib/generators/rcov.rb&lt;br /&gt;#       modified:   lib/generators/reek.rb&lt;br /&gt;#       modified:   lib/generators/roodi.rb&lt;br /&gt;#       modified:   lib/generators/saikuro.rb&lt;br /&gt;#       modified:   metric_fu.gemspec&lt;br /&gt;#       modified:   spec/base/generator_spec.rb&lt;br /&gt;#       modified:   spec/generators/flay_spec.rb&lt;br /&gt;#       modified:   spec/generators/flog_spec.rb&lt;br /&gt;#       modified:   spec/generators/reek_spec.rb&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's edit this commit message to read something like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Changed gem dependencies from install-time in gemspec to runtime when each&lt;br /&gt;of the generators is loaded.  This allows use of github gems (i.e.&lt;br /&gt;relevance-rcov instead of rcov) and also allows you to install only the&lt;br /&gt;gems for the metrics you plan on using.&lt;br /&gt;&lt;br /&gt;                                     &lt;br /&gt;# Please enter the commit message for your changes. Lines starting&lt;br /&gt;# with '#' will be ignored, and an empty message aborts the commit.&lt;br /&gt;# Explicit paths specified without -i nor -o; assuming --only paths...&lt;br /&gt;# Not currently on any branch.&lt;br /&gt;# Changes to be committed:&lt;br /&gt;#   (use "git reset HEAD &lt;file&gt;..." to unstage)&lt;br /&gt;#&lt;br /&gt;#       modified:   Rakefile&lt;br /&gt;#       modified:   lib/base/generator.rb&lt;br /&gt;#       modified:   lib/generators/flay.rb&lt;br /&gt;#       modified:   lib/generators/flog.rb&lt;br /&gt;#       modified:   lib/generators/rcov.rb&lt;br /&gt;#       modified:   lib/generators/reek.rb&lt;br /&gt;#       modified:   lib/generators/roodi.rb&lt;br /&gt;#       modified:   lib/generators/saikuro.rb&lt;br /&gt;#       modified:   metric_fu.gemspec&lt;br /&gt;#       modified:   spec/base/generator_spec.rb&lt;br /&gt;#       modified:   spec/generators/flay_spec.rb&lt;br /&gt;#       modified:   spec/generators/flog_spec.rb&lt;br /&gt;#       modified:   spec/generators/reek_spec.rb&lt;br /&gt;#&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's it!  Now when we check our history we see.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; git log&lt;br /&gt;&lt;br /&gt;commit 6f389364eb972871867d3a71677b8eb7046541a2&lt;br /&gt;Author: Alex Rothenberg &lt;alex@alexrothenberg.com&gt;&lt;br /&gt;Date:   Fri May 29 16:42:12 2009 -0400&lt;br /&gt;&lt;br /&gt;    Changed gem dependencies from install-time in gemspec to runtime when each&lt;br /&gt;    of the generators is loaded.  This allows use of github gems (i.e.&lt;br /&gt;    relevance-rcov instead of rcov) and also allows you to install only the&lt;br /&gt;    gems for the metrics you plan on using.&lt;br /&gt;&lt;br /&gt;commit d6af5089adce9eeed4916a155c3bdaeb4be6771a&lt;br /&gt;Author: Randy Souza &lt;randy@farnsworth.(none)&gt;&lt;br /&gt;Date:   Sat May 16 08:47:37 2009 +0800&lt;br /&gt;&lt;br /&gt;    Added a simple fix for cases where Saikuro results with nested information&lt;br /&gt;    cause metrics:all to crash&lt;br /&gt;    &lt;br /&gt;    Signed-off-by: Jake Scruggs &lt;jake.scruggs@gmail.com&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And &lt;a href="http://github.com/alexrothenberg/metric_fu/commit/573afaf19e7a578aa0b161569290169d9298cf96"&gt;this commit&lt;/a&gt; is how I pushed this patch to github for all the world to see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-6346906639645335189?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/Zgi5MBd7CcI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/6346906639645335189/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=6346906639645335189" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/6346906639645335189?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/6346906639645335189?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/Zgi5MBd7CcI/changing-history-with-git-rebase-how-to.html" title="Changing history with git rebase: How to combine several commits into one" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/06/changing-history-with-git-rebase-how-to.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEFR3s_eCp7ImA9WxJQFE4.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-8072366036639778399</id><published>2009-05-21T16:55:00.018-04:00</published><updated>2009-05-27T11:20:16.540-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-27T11:20:16.540-04:00</app:edited><title>How to use dates in Rails when your database stores a string</title><content type="html">When working with Rails there's a lot of magic that happens behind the scenes to make it easy to do complex things.  Most of the time you don't need to know how that "magic" works but there are times when things don't work as expected and its helpful to dig in and understand what Rails is doing under the covers so you can change how it works.  Did I just say "change how Rails works"?!?  I did!  Rails is opinionated software that seeks to lead you down the golden path but there are legitimate times when you have to veer off that path and Rails lets you do so.  I find this most often happens to me when I'm dealing with an existing legacy database which is not setup as Rails would like.
&lt;br /&gt;
&lt;br /&gt;Today I'm going to go through an example that happened to me recently when I had an existing database that stored some dates in a text column but I needed to treat them as dates in my UI.  I couldn't change the type of that column as there was another legacy application that expected it to be text.
&lt;br /&gt;
&lt;br /&gt;&lt;span style="font-size:130%;font-weight: bold;"&gt;Using Dates the Rails Way&lt;/span&gt;
&lt;br /&gt;
&lt;br /&gt;First let's look at how easy it is to work with dates when you can follow the Rails Way.  Let's create a new project and add a scaffolded Person object with a date attribute called birthday.
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;rails date_select_example
&lt;br /&gt;cd date_select_example
&lt;br /&gt;script/generate scaffold person name:string birthday:date
&lt;br /&gt;rake db:migrate
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;Now if we hit the site and try to create a new person we see a screen like this
&lt;br /&gt;
&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_36rptmL_RFc/ShbLyDIggQI/AAAAAAAADRI/M5mxXuL8rfw/s1600-h/new_person.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 241px; height: 223px;" src="http://1.bp.blogspot.com/_36rptmL_RFc/ShbLyDIggQI/AAAAAAAADRI/M5mxXuL8rfw/s320/new_person.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5338678468989190402" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;And when you click "Create"
&lt;br /&gt;
&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_36rptmL_RFc/ShbeAUi2a1I/AAAAAAAADRk/GYT6EpIyQts/s1600-h/person_created_successfully.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 234px; height: 139px;" src="http://4.bp.blogspot.com/_36rptmL_RFc/ShbeAUi2a1I/AAAAAAAADRk/GYT6EpIyQts/s320/person_created_successfully.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5338698505390549842" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;Exactly what you want with almost no code in the view and none in the model.  I did make a minor edit to the view so we would get 1960 in the year select by adding &lt;span style="font-family: courier new;"&gt;:start_year=&gt;1900&lt;/span&gt; (a full documentation of &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#M001666"&gt;date_select options&lt;/a&gt; are available here)
&lt;br /&gt;
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;#app/views/people/new.html.erb
&lt;br /&gt;&amp;lt;h1&gt;New person&lt;/h1&gt;
&lt;br /&gt;&amp;lt;% form_for(@person) do |f| %&gt;
&lt;br /&gt;  &amp;lt;%= f.error_messages %&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.label :name %&gt;&amp;lt;br /&gt;
&lt;br /&gt;    &amp;lt;%= f.text_field :name %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.label :birthday %&gt;&amp;lt;br /&gt;
&lt;br /&gt;    &amp;lt;%= f.date_select :birthday, :start_year=&gt;1900 %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.submit 'Create' %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;&amp;lt;% end %&gt;
&lt;br /&gt;
&lt;br /&gt;&amp;lt;%= link_to 'Back', people_path %&gt;
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;and 
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;#app/models/person.rb
&lt;br /&gt;class Person &lt; ActiveRecord::Base
&lt;br /&gt;end
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;span style="font-size:130%;font-weight: bold;"&gt;Errors when storing as text in the database&lt;/span&gt;
&lt;br /&gt;
&lt;br /&gt;Now what happens when you run into a case where the date is stored as a string in the database.  Let's say we have an "anniversary" attribute stored as a string that we want to treat the same way as we did birthday.  We create the migration.
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;script/generate migration AddAnniversaryToPerson anniversary:string
&lt;br /&gt;rake db:migrate
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;Then add the anniversary to our view.
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;#app/views/people/edit.html.erb
&lt;br /&gt;&amp;lt;h1&gt;Editing person&lt;/h1&gt;
&lt;br /&gt;&amp;lt;% form_for(@person) do |f| %&gt;
&lt;br /&gt;  &amp;lt;%= f.error_messages %&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.label :name %&gt;&amp;lt;br /&gt;
&lt;br /&gt;    &amp;lt;%= f.text_field :name %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.label :birthday %&gt;&amp;lt;br /&gt;
&lt;br /&gt;    &amp;lt;%= f.date_select :birthday, :start_year=&gt;1900  %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.label :anniversary %&gt;&amp;lt;br /&gt;
&lt;br /&gt;    &amp;lt;%= f.date_select :anniversary, :start_year=&gt;1900  %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;  &amp;lt;p&gt;
&lt;br /&gt;    &amp;lt;%= f.submit 'Update' %&gt;
&lt;br /&gt;  &amp;lt;/p&gt;
&lt;br /&gt;&amp;lt;% end %&gt;
&lt;br /&gt;
&lt;br /&gt;&amp;lt;%= link_to 'Show', @person %&gt; |
&lt;br /&gt;&amp;lt;%= link_to 'Back', people_path %&gt;
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_36rptmL_RFc/Shbhd6Rdh6I/AAAAAAAADRs/KU4XDoM_ICY/s1600-h/edit_person.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 236px; height: 277px;" src="http://1.bp.blogspot.com/_36rptmL_RFc/Shbhd6Rdh6I/AAAAAAAADRs/KU4XDoM_ICY/s320/edit_person.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5338702312269252514" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;It looks like we're done so we click "Update" and .. Oops.  It doesn't work!  We get the error &lt;span style="font-family: courier new;"&gt;1 error(s) on assignment of multiparameter attributes&lt;/span&gt;.  Now we need to figure out what multiparameter attributes are and whey they're not working for us.  
&lt;br /&gt;
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;ActiveRecord::MultiparameterAssignmentErrors in PeopleController#update
&lt;br /&gt;
&lt;br /&gt;1 error(s) on assignment of multiparameter attributes
&lt;br /&gt;RAILS_ROOT: /Users/alexrothenberg/date_select_example
&lt;br /&gt;
&lt;br /&gt;Application Trace | Framework Trace | Full Trace
&lt;br /&gt;/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/&lt;a href="http://github.com/rails/rails/blob/dc88847e5ce392eed210b97525c14fca55852867/activerecord/lib/active_record/base.rb#L3034-3063"&gt;active_record/base.rb:3061&lt;/a&gt;:in `execute_callstack_for_multiparameter_attributes'
&lt;br /&gt;/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/&lt;a href="http://github.com/rails/rails/blob/dc88847e5ce392eed210b97525c14fca55852867/activerecord/lib/active_record/base.rb#L3014-3024"&gt;active_record/base.rb:3022&lt;/a&gt;:in `assign_multiparameter_attributes'
&lt;br /&gt;/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/&lt;a href="http://github.com/rails/rails/blob/dc88847e5ce392eed210b97525c14fca55852867/activerecord/lib/active_record/base.rb#L2733-2750"&gt;active_record/base.rb:2749&lt;/a&gt;:in `attributes='
&lt;br /&gt;/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/&lt;a href="http://github.com/rails/rails/blob/dc88847e5ce392eed210b97525c14fca55852867/activerecord/lib/active_record/base.rb#L2626-2629"&gt;active_record/base.rb&lt;/a&gt;:2627:in `update_attributes'
&lt;br /&gt;/Users/alexrothenberg/date_select_example/app/controllers/&lt;a href="http://github.com/alexrothenberg/date_select_example/blob/master/app/controllers/people_controller.rb#L59-72"&gt;people_controller.rb:63&lt;/a&gt;:in `update'
&lt;br /&gt;...more stack trace...
&lt;br /&gt;
&lt;br /&gt;Request
&lt;br /&gt;
&lt;br /&gt;Parameters:
&lt;br /&gt;
&lt;br /&gt;{"commit"=&gt;"Update",
&lt;br /&gt; "_method"=&gt;"put",
&lt;br /&gt; "authenticity_token"=&gt;"qezkVq+MNzFuXxFBJ/GaSoh2BNdxM6oF3H7JP5beFFE=",
&lt;br /&gt; "id"=&gt;"1",
&lt;br /&gt; "person"=&gt;{"name"=&gt;"Barack Obama",
&lt;br /&gt; "birthday(2i)"=&gt;"8",
&lt;br /&gt; "birthday(3i)"=&gt;"4",
&lt;br /&gt; "anniversary(1i)"=&gt;"2009",
&lt;br /&gt; "anniversary(2i)"=&gt;"5",
&lt;br /&gt; "anniversary(3i)"=&gt;"22",
&lt;br /&gt; "birthday(1i)"=&gt;"1960"}}
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;What happened?  There are two keys to figuring out what's going on
&lt;br /&gt;&lt;ol&gt;
&lt;br /&gt;&lt;li&gt;The date_select helper actually sends 3 http parameters to our application &lt;span style="font-family: courier new;"&gt;anniversary(1i)&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;anniversary(2i)&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;anniversary(3i)&lt;/span&gt;.  ActiveRecord must combine those into a single Date before updating the row in the database.  
&lt;br /&gt;&lt;li&gt;Looking at assign_multiparameter_attributes in active_record/base.rb we see this this comment that talks about combining 3 http parameters into a date type &lt;em&gt;by calling new on the column type&lt;/em&gt;
&lt;br /&gt;  &lt;blockquote&gt;Instantiates objects for all attribute classes that &lt;b&gt;needs more than one constructor parameter&lt;/b&gt;. This is done by calling new on the column type or aggregation type (through composed_of) object with these parameters.
&lt;br /&gt;So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.&lt;/blockquote&gt;
&lt;br /&gt;&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;Now we know we're close to the right place and can check the column's return type using script/console
&lt;br /&gt;
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;$ script/console
&lt;br /&gt;Loading development environment (Rails 2.3.2)
&lt;br /&gt;&gt;&gt; Person.columns_hash['birthday']
&lt;br /&gt;=&gt; #&lt;ActiveRecord::ConnectionAdapters::SQLiteColumn:0x24c6bdc @default=nil, @type=:date, @null=true, @name="birthday", @scale=nil, @sql_type="date", @precision=nil, @primary=false, @limit=nil&gt;
&lt;br /&gt;&gt;&gt; Person.columns_hash['birthday'].klass&lt;b&gt;
&lt;br /&gt;=&gt; Date&lt;/b&gt;
&lt;br /&gt;&gt;&gt; Person.columns_hash['anniversary']
&lt;br /&gt;=&gt; #&lt;ActiveRecord::ConnectionAdapters::SQLiteColumn:0x24c6984 @default=nil, @type=:string, @null=true, @name="anniversary", @scale=nil, @sql_type="varchar(255)", @precision=nil, @primary=false, @limit=255&gt;
&lt;br /&gt;&gt;&gt; Person.columns_hash['anniversary'].klass&lt;b&gt;
&lt;br /&gt;=&gt; String&lt;/b&gt;
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;This makes sense.  ActiveRecord is treating the anniversary column as a string because that's what it is in the database so is not combining the 3 multiparameter attributes into a Date.  What we need to do is override the default ActiveRecord logic and tell it to treat this column as a date.  We can write a failing spec
&lt;br /&gt;
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;require File.dirname(__FILE__) + '/../spec_helper'
&lt;br /&gt;
&lt;br /&gt;describe Person do
&lt;br /&gt;  it "should treat anniversary as a Date column" do
&lt;br /&gt;    Person.columns_hash['anniversary'].klass.should == Date
&lt;br /&gt;  end
&lt;br /&gt;end
&lt;br /&gt;
&lt;br /&gt;# 'Person should treat anniversary as a Date column' FAILED
&lt;br /&gt;# expected: Date,
&lt;br /&gt;#      got: String (using ==)
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;Then take advantage of a trick of Ruby that allows you to extend an object without affecting other instances of its class (see &lt;a href="http://whytheluckystiff.net/ruby/pickaxe/html/classes.html#UB"&gt;PickAxe book's explanation&lt;/a&gt; of this technique).  If we lookup the &lt;a href="http://github.com/rails/rails/blob/18eb80ccc7e932f9a6c00462ceaeea648631b120/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L51-64"&gt;definition of klass for a column&lt;/a&gt; It is actually very simple to implement our fix with the 5 lines below.
&lt;br /&gt;
&lt;br /&gt;&lt;pre&gt;
&lt;br /&gt;class Person &lt; ActiveRecord::Base
&lt;br /&gt;
&lt;br /&gt;  class &lt;&lt; columns_hash['anniversary']
&lt;br /&gt;    def type
&lt;br /&gt;      :date
&lt;br /&gt;    end
&lt;br /&gt;  end
&lt;br /&gt;end
&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;Now our tests pass and when we go back to our site and click the Update button and it works.
&lt;br /&gt;
&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_36rptmL_RFc/ShcBLPag4KI/AAAAAAAADSE/neXvP4YCZJE/s1600-h/person_saved.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 231px; height: 166px;" src="http://2.bp.blogspot.com/_36rptmL_RFc/ShcBLPag4KI/AAAAAAAADSE/neXvP4YCZJE/s320/person_saved.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5338737175898939554" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-8072366036639778399?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/8HonW0Sb9S8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/8072366036639778399/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=8072366036639778399" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8072366036639778399?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8072366036639778399?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/8HonW0Sb9S8/how-to-use-dates-in-rails-when-your.html" title="How to use dates in Rails when your database stores a string" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_36rptmL_RFc/ShbLyDIggQI/AAAAAAAADRI/M5mxXuL8rfw/s72-c/new_person.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/05/how-to-use-dates-in-rails-when-your.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUEFQXoyfSp7ImA9WxJSF0g.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-2959343452329459972</id><published>2009-05-06T13:43:00.012-04:00</published><updated>2009-05-07T23:33:30.495-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-07T23:33:30.495-04:00</app:edited><title>Testing AJAX without a browser with Cucumber and Webrat</title><content type="html">I have lately fallen in love with using Cucumber and Webrat for my integration/acceptance testing.  &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; because it allows non-technical people to write or at least read the test scenarios and &lt;a href=http://github.com/brynary/webrat/tree/master&gt;Webrat&lt;/a&gt; because it matches content and encourages you to write integration tests without relying on xpath to find html elements.  The way I like to use these tools is to run Rails integration tests which means its fast since I don’t need to start a mongrel or fire up a browser and can use Rails’ transactional fixtures to rollback all my database changes at the end of each test scenario.  The only downside is that you can’t test javascript.&lt;br /&gt;&lt;br /&gt;Today I am going to talk about how to get around this and test a form with an ajax autocomplete field.  I've built a sample application with all the code examples here and you can download it from &lt;a href="http://github.com/alexrothenberg/testing-ajax-example"&gt;http://github.com/alexrothenberg/testing-ajax-example&lt;/a&gt; if you like.  The application I'm building is just some simple app created with scaffolding that just has a User resource with a name and address.  I modified the /users page to not display all users but include the auto_complete typeahead to let you pick a user (imagining there may be a lot) so the page looks something like this.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_36rptmL_RFc/SgG3eqE-W3I/AAAAAAAADOw/7p-_x0l6qLo/s1600-h/search_for_a_user_screen.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 157px;" src="http://4.bp.blogspot.com/_36rptmL_RFc/SgG3eqE-W3I/AAAAAAAADOw/7p-_x0l6qLo/s320/search_for_a_user_screen.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5332745171102489458" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;My first test scenario will ignore the ajax and just test the form which is super easy and can be done by writing a single feature file.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#features/find_a_user.feature&lt;br /&gt;Feature: Allow anyone to find a user and see their details&lt;br /&gt;  In order to handle a large set of users&lt;br /&gt;  I want search with autocomplete&lt;br /&gt; &lt;br /&gt;  Scenario: View a candidate detail page without testing ajax&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"&lt;br /&gt;    When I am on the homepage&lt;br /&gt;      And I fill in "Which user" with "Mickey Mouse"&lt;br /&gt;      And I press "Find"&lt;br /&gt;    Then I should see "Mickey Mouse"&lt;br /&gt;      And I should see "123 Main Street"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I run it it all passes and I get&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ rake features&lt;br /&gt;(in /Users/alexrothenberg/ruby/testing-ajax-example)&lt;br /&gt;/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I "/Library/Ruby/Gems/1.8/gems/cucumber-0.3.2/lib:lib" "/Library/Ruby/Gems/1.8/gems/cucumber-0.3.2/bin/cucumber" --format pretty --require features/step_definitions/user_steps.rb --require features/step_definitions/webrat_steps.rb --require features/support/env.rb --require features/support/paths.rb features/find_a_user.feature&lt;br /&gt;Feature: Allow anyone to find a user and see their details&lt;br /&gt;  In order to handle a large set of users &lt;br /&gt;  I want search with autocomplete&lt;br /&gt;&lt;br /&gt;  Scenario: View a candidate detail page without testing ajax     # features/find_a_user.feature:5&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"    # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                     # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I fill in "Which user" with "Mickey Mouse"                # features/step_definitions/webrat_steps.rb:22&lt;br /&gt;    And I press "Find"                                            # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                              # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                            # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;1 scenario (1 passed)&lt;br /&gt;6 steps (6 passed)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I makes use of the default webrat steps that cucumber gives you for free in features/steps/webrat_steps.rb so I don’t even have to write any code to get it to pass but I still haven’t tested any of my code that responds to the autocomplete request.  I’d like my test to verify that my routes, controller and model will all work together. So, I write another scenario and run it and this time it fails because I haven't defined the typeahead steps for the typeahead lines.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;first scenario omitted&amp;gt;&lt;br /&gt;&lt;br /&gt;Scenario: View a candidate detail page testing (most of) the ajax # features/find_a_user.feature:13&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"      # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                       # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I typeahead in "Which user" with "Mickey Mouse"             # features/find_a_user.feature:16&lt;br /&gt;    And I fill in "Which candidate" with the first typeahead result # features/find_a_user.feature:17&lt;br /&gt;    And I press "Find"                                              # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                                # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                              # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;2 scenarios (1 undefined, 1 passed)&lt;br /&gt;13 steps (3 skipped, 2 undefined, 8 passed)&lt;br /&gt;&lt;br /&gt;You can implement step definitions for undefined steps with these snippets:&lt;br /&gt;&lt;br /&gt;When /^I typeahead in "([^\"]*)" with "([^\"]*)"$/ do |arg1, arg2|&lt;br /&gt;  pending&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;When /^I fill in "([^\"]*)" with the first typeahead result$/ do |arg1|&lt;br /&gt;  pending&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I take the hints cucumber has given me and write my autocomplete steps.  The interesting thing here is that I need to leave the response object unchanged so I can fill in the form field after running the typeahead step so I can't use the existing webrat steps as they work on a single pair of request and response objects.  So I knew I'd be creating a new class with its own request and response that could be used without affecting the one used by my other cucumber steps.  Using good outside-in development practices I deferred thinking about how to do that and first wrote my steps file to look something like this.  One interesting thing to notice here is that you can invoke a step from inside another step just by omitting the block as I do in the second step.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;When /^I typeahead in "(.*)" with "(.*)"$/ do |field, value|&lt;br /&gt;  field = field_labeled field&lt;br /&gt;  @typeahead = AutoCompleteStepHelper.new(request)&lt;br /&gt;  @typeahead.type(field, value)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;When /^I fill in "(.*)" with the first typeahead result$/ do |field|&lt;br /&gt;  When %Q[I fill in "#{field}" with "#{@typeahead.items.first}"]&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now when run the feature again it fails telling me I haven’t yet built the AutoCompleteStepHelper.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;first scenario omitted&amp;gt;&lt;br /&gt;&lt;br /&gt;Scenario: View a candidate detail page testing (most of) the ajax # features/find_a_user.feature:13&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"      # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                       # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I typeahead in "Which user" with "Mickey Mouse"             # features/step_definitions/autocomplete_steps.rb:1&lt;br /&gt;      uninitialized constant AutoCompleteStepHelper (NameError)&lt;br /&gt;      ./features/step_definitions/autocomplete_steps.rb:3:in `/^I typeahead in "(.*)" with "(.*)"$/'&lt;br /&gt;      features/find_a_user.feature:16:in `And I typeahead in "Which user" with "Mickey Mouse"'&lt;br /&gt;    And I fill in "Which candidate" with the first typeahead result # features/step_definitions/autocomplete_steps.rb:7&lt;br /&gt;    And I press "Find"                                              # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                                # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                              # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;2 scenarios (1 failed, 1 passed)&lt;br /&gt;13 steps (1 failed, 4 skipped, 8 passed)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the next step is to write the AutoCompleteStepHelper class.  This class’ job is to have its own request and response that will not affect the ones used by cucumber for the main page requests.  It turns out that I can do this by having my class extend ActionController::IntegrationTest and I can even use webrat methods in it because webrat &lt;a href="http://github.com/brynary/webrat/blob/481bfe03c0829a7d858443ceb6a51e1587a0d931/lib/webrat/rails.rb#L100-105"&gt;adds its methods to IntegrationTest&lt;/a&gt;.  In this example I'm calling &lt;span style="font-family: courier new;"&gt;visit&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;current_dom&lt;/span&gt; and using &lt;a href="http://nokogiri.rubyforge.org/nokogiri/"&gt;nokogiri&lt;/a&gt; to parse the dom.  It is a little weird that I'm subclassing IntegrationTest but this class is not a TestUnit class itself but I decided that was okay.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class AutoCompleteStepsHelper &lt; ActionController::IntegrationTest&lt;br /&gt;  def initialize(existing_request)&lt;br /&gt;    @controller_name = existing_request.parameters[:controller]&lt;br /&gt;    @controller_class = "#{@controller_name.to_s.camelize}Controller".constantize&lt;br /&gt;    raise "Can't determine controller class for #{@controller_class_name}" if @controller_class.nil?&lt;br /&gt;&lt;br /&gt;    @controller = @controller_class.new&lt;br /&gt;    @request = ActionController::TestRequest.new&lt;br /&gt;    @response = ActionController::TestResponse.new&lt;br /&gt;    @response.session = @request.session&lt;br /&gt;  end&lt;br /&gt; &lt;br /&gt;  def type(field, value)&lt;br /&gt;    visit url_for(:controller=&gt;@controller_name, :action=&gt;"auto_complete_for_#{field.id}", field.send(:name)=&gt;value)&lt;br /&gt;  end&lt;br /&gt; &lt;br /&gt;  def items&lt;br /&gt;    current_dom.search('//ul/li').map(&amp;:inner_html)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now when I run the feature it all passes.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;first scenario omitted&amp;gt;&lt;br /&gt;&lt;br /&gt;  Scenario: View a candidate detail page testing (most of) the ajax # features/find_a_user.feature:13&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"      # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                       # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I typeahead in "Which user" with "Mickey Mouse"             # features/step_definitions/autocomplete_steps.rb:1&lt;br /&gt;    And I fill in "Which user" with the first typeahead result      # features/step_definitions/autocomplete_steps.rb:7&lt;br /&gt;    And I press "Find"                                              # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                                # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                              # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;2 scenarios (2 passed)&lt;br /&gt;13 steps (13 passed)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last step I took was to test that the list returned in the typeahead was correct.  I created another scenario in my feature.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  Scenario: Typeahead should return 2 users that match but not a third&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"&lt;br /&gt;      And "Donald Duck" is a user living at "123 Pond Lane"&lt;br /&gt;      And "Minnie Mouse" is a user living at "123 Disney Avenue"&lt;br /&gt;    When I am on the homepage&lt;br /&gt;      And I typeahead in "Which user" with "Mi"&lt;br /&gt;    Then I should see in my typeahead "Mickey Mouse"&lt;br /&gt;      And I should see in my typeahead "Minnie Mouse"&lt;br /&gt;      And I should not see in my typeahead "Donald Duck"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and I added two new steps&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Then /^I should see in my typeahead "(.*)"$/ do |text|&lt;br /&gt;  @typeahead.response_body.should =~ /#{text}/m&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Then /^I should not see in my typeahead "(.*)"$/ do |text|&lt;br /&gt;  @typeahead.response_body.should_not =~ /#{text}/m&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now when I run my features all 3 scenarios are passing&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ rake features&lt;br /&gt;(in /Users/alexrothenberg/ruby/testing-ajax-example)&lt;br /&gt;/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I "/Library/Ruby/Gems/1.8/gems/cucumber-0.3.2/lib:lib" "/Library/Ruby/Gems/1.8/gems/cucumber-0.3.2/bin/cucumber" --format pretty --require features/step_definitions/autocomplete_steps.rb --require features/step_definitions/user_steps.rb --require features/step_definitions/webrat_steps.rb --require features/support/autocomplete_steps_helper.rb --require features/support/env.rb --require features/support/paths.rb features/find_a_user.feature&lt;br /&gt;Feature: Allow anyone to find a user and see their details&lt;br /&gt;  In order to handle a large set of users &lt;br /&gt;  I want search with autocomplete&lt;br /&gt;&lt;br /&gt;  Scenario: View a candidate detail page without testing ajax  # features/find_a_user.feature:5&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street" # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                  # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I fill in "Which user" with "Mickey Mouse"             # features/step_definitions/webrat_steps.rb:22&lt;br /&gt;    And I press "Find"                                         # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                           # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                         # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;  Scenario: View a candidate detail page testing (most of) the ajax # features/find_a_user.feature:13&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"      # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                       # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I typeahead in "Which user" with "Mickey Mouse"             # features/step_definitions/autocomplete_steps.rb:1&lt;br /&gt;    And I fill in "Which user" with the first typeahead result      # features/step_definitions/autocomplete_steps.rb:7&lt;br /&gt;    And I press "Find"                                              # features/step_definitions/webrat_steps.rb:14&lt;br /&gt;    Then I should see "Mickey Mouse"                                # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;    And I should see "123 Main Street"                              # features/step_definitions/webrat_steps.rb:93&lt;br /&gt;&lt;br /&gt;  Scenario: Typeahead should return 2 users that match but not a third # features/find_a_user.feature:22&lt;br /&gt;    Given "Mickey Mouse" is a user living at "123 Main Street"         # features/step_definitions/user_steps.rb:1&lt;br /&gt;    And "Donald Duck" is a user living at "123 Pond Lane"              # features/step_definitions/user_steps.rb:1&lt;br /&gt;    And "Minnie Mouse" is a user living at "123 Disney Avenue"         # features/step_definitions/user_steps.rb:1&lt;br /&gt;    When I am on the homepage                                          # features/step_definitions/webrat_steps.rb:6&lt;br /&gt;    And I typeahead in "Which user" with "Mi"                          # features/step_definitions/autocomplete_steps.rb:1&lt;br /&gt;    Then I should see in my typeahead "Mickey Mouse"                   # features/step_definitions/autocomplete_steps.rb:16&lt;br /&gt;    And I should see in my typeahead "Minnie Mouse"                    # features/step_definitions/autocomplete_steps.rb:16&lt;br /&gt;    And I should not see in my typeahead "Donald Duck"                 # features/step_definitions/autocomplete_steps.rb:20&lt;br /&gt;&lt;br /&gt;3 scenarios (3 passed)&lt;br /&gt;21 steps (21 passed)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What I've done is not full javascript testing (for that I'm planning to look into &lt;a href="http://blog.thinkrelevance.com/2009/4/30/javascript-testing-at-railsconf"&gt;Blue-Ridge&lt;/a&gt; from Relevance).  This technique does allow you to test ajax (skipping the "J") without a browser.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-2959343452329459972?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/ldTYKZEHxBE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/2959343452329459972/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=2959343452329459972" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2959343452329459972?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2959343452329459972?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/ldTYKZEHxBE/testing-ajax-without-browser-with.html" title="Testing AJAX without a browser with Cucumber and Webrat" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_36rptmL_RFc/SgG3eqE-W3I/AAAAAAAADOw/7p-_x0l6qLo/s72-c/search_for_a_user_screen.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/05/testing-ajax-without-browser-with.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYMSXw6eip7ImA9WxVbFE4.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5524234857417099518</id><published>2009-03-30T11:02:00.004-04:00</published><updated>2009-03-30T14:19:48.212-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-30T14:19:48.212-04:00</app:edited><title>Managing the draw of a single-elimination tournament</title><content type="html">My company has been running a &lt;a href="http://en.wikipedia.org/wiki/Go_%28board_game%29"&gt;GO&lt;/a&gt; tournament and since its around NCAA March Madness time I thought it'd be easy to find a site to manage the draw.  I looked but couldn't find anything that did what I wanted so I wrote my own.  Caveat: I just spent a few days on this and it could stand to be enhanced with more features and a rewrite of the ui implementation but I'm open to suggestions if anyone finds this useful.&lt;br /&gt;&lt;br /&gt;To use it &lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;Edit the file lib/names.txt with a the players in your tournament&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Run 'rake create_tournament&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Now when you view the site you'll see a draw that looks something like this.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_36rptmL_RFc/SdDfviojZMI/AAAAAAAADNw/U1bcraUPbAU/s1600-h/tournament-draw.png"&gt;&lt;img style="cursor: pointer; width: 232px; height: 320px;" src="http://4.bp.blogspot.com/_36rptmL_RFc/SdDfviojZMI/AAAAAAAADNw/U1bcraUPbAU/s320/tournament-draw.png" alt="" id="BLOGGER_PHOTO_ID_5318997167768298690" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;For games with 2 players - all first round games and games where the previous round winners have been determined - you'll be able to click the link and set which player won.&lt;br /&gt;&lt;br /&gt;This was all very quickly thrown together but if you're interested in hosting any sort of single-elimination tournament check it out on github &lt;a href="http://github.com/alexrothenberg/tournament-draw/"&gt;tournament-draw&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;Let me know if you use it and how you'd like to enhance it from the quick application it is today.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5524234857417099518?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/rkDGmJsnoSc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5524234857417099518/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5524234857417099518" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5524234857417099518?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5524234857417099518?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/rkDGmJsnoSc/managing-draw-of-single-elimination.html" title="Managing the draw of a single-elimination tournament" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_36rptmL_RFc/SdDfviojZMI/AAAAAAAADNw/U1bcraUPbAU/s72-c/tournament-draw.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/03/managing-draw-of-single-elimination.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUNRnoycSp7ImA9WxVUF0o.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5843513361467282898</id><published>2009-03-22T21:47:00.002-04:00</published><updated>2009-03-22T21:54:57.499-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-22T21:54:57.499-04:00</app:edited><title>Link: Search Engine in 200 lines of Ruby Code</title><content type="html">I read a very interesting article by someone who works for Yahoo about how he wrote a basic search engine in Ruby in just a few files.  I wouldn't use this for a production system for as an example of how search engines work its fascinating and I plan to keep following his site to see how he enhances it&lt;br /&gt;&lt;br /&gt;Description: &lt;a href="http://blog.saush.com/2009/03/write-an-internet-search-engine-with-200-lines-of-ruby-code/"&gt;http://blog.saush.com/2009/03/write-an-internet-search-engine-with-200-lines-of-ruby-code/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Source: &lt;a href="http://github.com/sausheong/saushengine"&gt;http://github.com/sausheong/saushengine&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;... and he's got a really cool css/javascript formatter for the source in his blog.  I've gotta learn more about &lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter"&gt;SyntaxHighlighter&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5843513361467282898?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/KDhzhojnpxE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5843513361467282898/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5843513361467282898" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5843513361467282898?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5843513361467282898?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/KDhzhojnpxE/link-search-engine-in-200-lines-of-ruby.html" title="Link: Search Engine in 200 lines of Ruby Code" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/03/link-search-engine-in-200-lines-of-ruby.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYEQX87eCp7ImA9WxVUFEQ.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-4348459139798160542</id><published>2009-03-18T15:13:00.004-04:00</published><updated>2009-03-19T17:11:40.100-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-19T17:11:40.100-04:00</app:edited><title>Java as a Legacy Language</title><content type="html">I came across this article titled &lt;a href="http://asserttrue.blogspot.com/2009/03/java-as-legacy-language.html"&gt;Java as Legacy Language&lt;/a&gt; today.  As an ex-Java guy who is now committed to Ruby I was amused by the title but also think he makes a good point.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;One thing is for sure: If you're in the software development business, don't cling to old ways of doing development. And also, don't get too carried away thinking that something like Scrum is going to be the Bandaid that fixes your agility problems, because it may turn out that your main problem is Java itself. Keep an open mind. Try new things. Be ready when the next disruption arrives, or you may find yourself without a chair when the music stops. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I don't think Java is "bad" but I do think it encourages you to write big, complicated applications to solve big, complicated problems.  In contrast I find Rails encourages you to realize most of the apps we write are in fact fairly simple and do the same CRUD steps over and over.  Having so much support for that built into the framework helps you think about what's "interesting" about your appplication which may be unique but is probably not big.&lt;br /&gt;&lt;br /&gt;Add to this how easy it is to test in Rails - tools like RSpec &amp; Shoulda, mocking tools, integration testing and more recently cucumber + webrat all make it easier to practice TDD than not to.  While there are tools in Java, the fact that they're harder to use, I believe, means fewer developers will use them and these practices will remain less ingrained in that community.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-4348459139798160542?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/P_fdM4nffec" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/4348459139798160542/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=4348459139798160542" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/4348459139798160542?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/4348459139798160542?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/P_fdM4nffec/java-as-legacy-language.html" title="Java as a Legacy Language" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/03/java-as-legacy-language.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUER3wzeSp7ImA9WxVUFE0.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-2229205486979826702</id><published>2009-03-18T09:05:00.004-04:00</published><updated>2009-03-18T15:06:46.281-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-18T15:06:46.281-04:00</app:edited><title>Using scopes in auto_complete plugin</title><content type="html">My colleague &lt;a href="http://patshaughnessy.net"&gt;Pat Shaughnessy&lt;/a&gt; has spent a lot of time recently enhancing the auto_complete plugin.  I suggest you read his blog posts and check out his &lt;a href="http://github.com/patshaughnessy/auto_complete"&gt;fork of auto_complete on github&lt;/a&gt; to see the details.  &lt;br /&gt;&lt;br /&gt;I was reading his latest change to allow &lt;a href="http://patshaughnessy.net/2009/3/14/filtering-auto_complete-pick-lists"&gt;filtering of auto complete picklists&lt;/a&gt; and really like what he did but thought there was one thing that didn't quite feel right - the fact that you have to mix the application logic to filter the list with the plugin logic to find the list in the block in your controller.  &lt;br /&gt;&lt;br /&gt;Here's the code Pat wrote in his controller and what I'd like to avoid is having to re-implement the "LOWER(tasks.name) LIKE ?" portion that's already implemented in the filtered_auto_complete_for method of autocomplete.rb in the plugin.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# For task name auto complete, only display tasks&lt;br /&gt;# that belong to the given project: &lt;br /&gt;filtered_auto_complete_for :task, :name do | find_options, params|&lt;br /&gt;  find_options.merge!(&lt;br /&gt;    {&lt;br /&gt;      :include =&gt; :project,&lt;br /&gt;      :conditions =&gt; [ "LOWER(tasks.name) LIKE ? AND projects.name = ?",&lt;br /&gt;                       '%' + params['task']['name'].downcase + '%',&lt;br /&gt;                       params['project'] ],&lt;br /&gt;      :order =&gt; "tasks.name ASC"&lt;br /&gt;    }&lt;br /&gt;  )&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I'd like to propose a slight modification so the block you write as part of your application can focus just on the filtering and leave the responsibility for the search with plugin.  I'm also proposing we use scopes (which I don't think were around when the original auto_complete plugin was written) to filter and sort the list the plugin.  Here is the code I'd like to write in my application.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ProjectController &lt; ApplicationController&lt;br /&gt;  # For task name auto complete, only display tasks&lt;br /&gt;  # that belong to the given project: &lt;br /&gt;  filtered_auto_complete_for :task, :name do | list, params |&lt;br /&gt;    list.by_project(params['project']['id'])&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Project &lt; ActiveRecord::Base&lt;br /&gt;  named_scope :by_project, &lt;br /&gt;              lambda {|project_id| {:conditions =&gt; {:project_id =&gt; project_id} } }&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can do this with a simple modification of the plugin implementation of filtered_auto_complete_for&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def filtered_auto_complete_for(object, method)&lt;br /&gt;  define_method("auto_complete_for_#{object}_#{method}") do&lt;br /&gt;    find_options = { &lt;br /&gt;      :conditions =&gt; [ "LOWER(#{method}) LIKE ?", '%' +&lt;br /&gt;        params[object][method].downcase + '%' ], &lt;br /&gt;      :order =&gt; "#{method} ASC",&lt;br /&gt;      :limit =&gt; 10 }&lt;br /&gt;    @items = object.to_s.camelize.constantize.scoped(find_options)&lt;br /&gt;    @items = yield(@items, params) if block_given?&lt;br /&gt;&lt;br /&gt;    render :inline =&gt; "&lt;%= auto_complete_result @items, '#{method}' %&gt;"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Originally, it used passed the find_options hash to the block and then executed the search in one step as "@items = object.to_s.camelize.constantize.find(:all, find_options)".  My change is to rely on ActiveRecord proxy objects to chain criteria together leaving the block independent of the criteria here in the plugin.  The best part is you don't have to worry about performance as ActiveRecord will intelligently combine the criteria into a single SQL request.  For example searching for tasks that start with 'tas' within project #7&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; User Load (1.2ms)   SELECT * FROM `tasks` WHERE ((`tasks`.`project_id` = '7') AND (LOWER(name) LIKE '%tas%'))  ORDER BY name ASC LIMIT 10&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-2229205486979826702?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/H242dyoX1SE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/2229205486979826702/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=2229205486979826702" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2229205486979826702?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2229205486979826702?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/H242dyoX1SE/using-scopes-in-autocomplete-plugin.html" title="Using scopes in auto_complete plugin" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/03/using-scopes-in-autocomplete-plugin.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMFR3w9eCp7ImA9WxVVEko.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5516259370645936062</id><published>2009-03-04T10:00:00.005-05:00</published><updated>2009-03-05T12:00:16.260-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-05T12:00:16.260-05:00</app:edited><title>Microsoft Office links causing InvalidAuthenticityToken in Rails</title><content type="html">I started receiving a lot of error notifications recently from my ExceptionNotfier plugin for an error with ActionController::InvalidAuthenticityToken.  It turned out the error was occurring because one of my users was pasting a link to my app in an MS Office document and when Office sees the link it makes a request that Rails could not handle.  Here I'll show you a simple fix you can use to avoid these errors with much credit going to an article at &lt;a href="http://rails.learnhub.com/lesson/2318-dealing-with-microsoft-office-protocol-discovery-in-rails"&gt;&lt;br /&gt;Dealing with Microsoft Office Protocol Discovery in Rails&lt;br /&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My execptions looked something like this (lots of boring details omitted)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;A ActionController::InvalidAuthenticityToken occurred in events#1164:&lt;br /&gt;&lt;br /&gt;  ActionController::InvalidAuthenticityToken&lt;br /&gt;  [RAILS_ROOT]/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'&lt;br /&gt;&lt;br /&gt;-------------------------------&lt;br /&gt;Environment:&lt;br /&gt;-------------------------------&lt;br /&gt;  * HTTP_USER_AGENT        : Microsoft Data Access Internet Publishing Provider Protocol Discovery&lt;br /&gt;  * REQUEST_METHOD         : OPTIONS&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The problem is that Rails doesn't understand the method 'OPTIONS' (see &lt;a href="http://github.com/rails/rails/blob/ce56c5daa81d61a745b88220014a846a0eea46a4/actionpack/lib/action_controller/routing.rb"&gt;rails/actionpack/lib/action_controller/routing.rb&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#around line 270 of routing.rb&lt;br /&gt;module ActionController&lt;br /&gt;  module Routing&lt;br /&gt;    HTTP_METHODS = [:get, :head, :post, :put, :delete]&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Fixing the problem is fairly simple.  You insert a before_filter into your application controller to intercept and handle requests with the option method before the rails code realizes it can't handle the request.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ApplicationController &lt; ActionController::Base&lt;br /&gt;  before_filter :options_for_microsoft_office_protocol_discovery&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  def options_for_microsoft_office_protocol_discovery&lt;br /&gt;    render :nothing =&gt; true, :status =&gt; 200 if request.method == :options&lt;br /&gt;  end&lt;br /&gt;end  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Its also easy to write a simple spec in rspec to verify the behavior.  There is one trick which is that rails/actionpack/lib/action_controller/test_process.rb defines helper methods for get, post, put, delete &amp;amp; head that we can't use so we need to call the underlying method directly but the signature for that underlying method changed with Rails 2.3 (&lt;a href="http://github.com/rails/rails/commit/6e2a771661a47fb682108648244837f8616e350d"&gt;commit&lt;/a&gt;) so depending what version you're using you'll need one of 2 flavors.  &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  # Rails 2.3 and above &lt;br /&gt;  it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do&lt;br /&gt;    process '/any/goofy/path', nil, nil, nil, 'OPTIONS'&lt;br /&gt;    response.should be_success&lt;br /&gt;  end &lt;br /&gt;&lt;br /&gt;  # Rails &lt; 2.3 version&lt;br /&gt;  it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do&lt;br /&gt;    @request.env['REQUEST_METHOD'] = 'OPTIONS'&lt;br /&gt;    process '/any/goofy/path'&lt;br /&gt;    response.should be_success&lt;br /&gt;  end &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also test from the command line using curl&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;curl -X OPTIONS http://localhost:3000/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update:&lt;/span&gt; It turns out that Rails stores the acceptable methods in 2 different places &lt;a href="http://github.com/rails/rails/blob/b1c989f28dd1d619f0e3e3ca1b894b686e517f2f/actionpack/lib/action_controller/request.rb"&gt;actionpack/lib/action_controller/request.rb&lt;/a&gt; which does include all of get head put post delete options and also (see &lt;a href="http://github.com/rails/rails/blob/ce56c5daa81d61a745b88220014a846a0eea46a4/actionpack/lib/action_controller/routing.rb"&gt;rails/actionpack/lib/action_controller/routing.rb&lt;/a&gt; which only includes :get, :head, :post, :put, :delete (options is missing).  This means this fix will only work for OPTIONS requests and not any other type as ActionController::Request request_method will throw an exception before getting to the filter code above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5516259370645936062?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/FWQ6-WIaDmU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5516259370645936062/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5516259370645936062" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5516259370645936062?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5516259370645936062?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/FWQ6-WIaDmU/microsoft-office-links-causing.html" title="Microsoft Office links causing InvalidAuthenticityToken in Rails" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/03/microsoft-office-links-causing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYAQ3c7cCp7ImA9WxVXEE4.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-8845862891270009488</id><published>2009-02-07T11:39:00.007-05:00</published><updated>2009-02-07T13:09:02.908-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-07T13:09:02.908-05:00</app:edited><title>Investigating how Symbol to_proc works</title><content type="html">One of the things I love about Ruby is how expressive it is and how with open classes it can be optimized to become even more expressive.  Since I started using Ruby I don't think I've written a single for or while loop - something I couldn't have imagined saying with any other language!  Of course I do this by using iterators and writing code like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;user_names = User.all.collect {|user| user.name}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I recently started discovered I could write the same thing even more concisely (as long as I'm using Rails or Ruby 1.9)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;user_names = User.all.collect(&amp;:name)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I decided to investigate how this works.&lt;br /&gt;&lt;br /&gt;First I found some good posts by &lt;a href="http://pragdave.pragprog.com/pragdave/2005/11/symbolto_proc.html"&gt;Prag Dave&lt;/a&gt; and &lt;a href="http://railscasts.com/episodes/6"&gt;Ryan Bates&lt;/a&gt; and at &lt;a href="http://www.infoq.com/news/2008/02/to_proc-currying-ruby19"&gt;InfoQ&lt;/a&gt;.  This helped but I still didn't understand it all so decided to dig further.&lt;br /&gt;&lt;br /&gt;First I took a look at how Rails extends &lt;a href="http://github.com/rails/rails/blob/24ac1d6bdc860d234e70dd4cd4713bd13ac9d40d/activesupport/lib/active_support/core_ext/symbol.rb"&gt;Symbol&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;unless :to_proc.respond_to?(:to_proc)&lt;br /&gt;  class Symbol&lt;br /&gt;    # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:&lt;br /&gt;    #&lt;br /&gt;    # # The same as people.collect { |p| p.name }&lt;br /&gt;    # people.collect(&amp;:name)&lt;br /&gt;    #&lt;br /&gt;    # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }&lt;br /&gt;    # people.select(&amp;:manager?).collect(&amp;:salary)&lt;br /&gt;    def to_proc&lt;br /&gt;      Proc.new { |*args| args.shift.__send__(self, *args) }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So they defined the &lt;span style="font-family: courier new;"&gt;to_proc&lt;/span&gt; method on symbol and that means the new code will be called when we write &lt;span style="font-family: courier new;"&gt;&amp;:name&lt;/span&gt; because it magically gets transformed into &lt;span style="font-family: courier new;"&gt;:name.to_proc&lt;/span&gt;.  I learned something but still needed to learn more to understand how it all works.&lt;br /&gt;&lt;br /&gt;Why does the &lt;span style="font-family: courier new;"&gt;&amp;&lt;/span&gt; cause Ruby to call &lt;span style="font-family: courier new;"&gt;to_proc&lt;/span&gt;?  I knew that &lt;span style="font-family: courier new;"&gt;&amp;&lt;/span&gt; in the last parameter declaration will pass a provided block as a parameter but this seems to be doing the reverse.  Calling a method as an argument but having it interpreted as a block.  I tried a couple of experiments in irb&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def was_block_given?&lt;br /&gt;  block_given?&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# As expected&lt;br /&gt;was_block_given? {}&lt;br /&gt;=&gt; true &lt;br /&gt;&lt;br /&gt;# Passing a proc is not the same as having a block&lt;br /&gt;was_block_given? Proc.new{}&lt;br /&gt;ArgumentError: wrong number of arguments (1 for 0)&lt;br /&gt; from (irb):195:in `was_block_given?'&lt;br /&gt; from (irb):195&lt;br /&gt;&lt;br /&gt;# Prefixing the proc with an &amp; makes it like a block&lt;br /&gt;was_block_given? &amp;Proc.new{}&lt;br /&gt;=&gt; true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It was not all as I expected but some reading through the &lt;a href="http://www.pragprog.com/titles/ruby/programming-ruby"&gt;PickAxe book&lt;/a&gt; led me to a better understanding.  I found this paragraph in the Calling A Method section (page 115 in my copy)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If the last argument to a method is preceded by an ampersand, Ruby assumes that it is a Proc object. &lt;br /&gt;It removes it from the parameter list, converts the Proc object into a block, and associates it with the method. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Ok so now I know why when Ruby sees &lt;span style="font-family: courier new;"&gt;User.all.collect(&amp;:name)&lt;/span&gt; it invokes the &lt;span style="font-family: courier new;"&gt;collect&lt;/span&gt; method with &lt;span style="font-family: courier new;"&gt;name.to_proc&lt;/span&gt; as a block.  Next, it was time to figure out why the code Rails put in the to_proc method worked.  I took a look at the &lt;a href="http://github.com/evanphx/rubinius/blob/83ab3fd8b6645ac7389e97b5a1969e5eb3b2b038/kernel/common/enumerable.rb"&gt;Rubinius implementation Enumerable&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  def collect&lt;br /&gt;    ary = []&lt;br /&gt;    if block_given?&lt;br /&gt;      each { |o| ary &lt;&lt; yield(o) }&lt;br /&gt;    else&lt;br /&gt;      each { |o| ary &lt;&lt; o }&lt;br /&gt;    end&lt;br /&gt;    ary&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Again I decided to experiment with irb to see what each part of the to_proc implementation was doing.  First I redefined the Symbol to_proc again with a puts so I could confirm what was going on.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Symbol&lt;br /&gt;  # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:&lt;br /&gt;  #&lt;br /&gt;  #   # The same as people.collect { |p| p.name }&lt;br /&gt;  #   people.collect(&amp;:name)&lt;br /&gt;  #&lt;br /&gt;  #   # The same as people.select { |p| p.manager? }.collect { |p| p.salary }&lt;br /&gt;  #   people.select(&amp;:manager?).collect(&amp;:salary)&lt;br /&gt;  def to_proc&lt;br /&gt;    Proc.new do |*args| &lt;br /&gt;      puts "to_proc args: #{args.inspect}"&lt;br /&gt;      args_shift = args.shift&lt;br /&gt;      puts "to_proc: #{args_shift.inspect}.__send__(#{self.inspect}, *#{args.inspect})"&lt;br /&gt;      result = args_shift.__send__(self, *args) &lt;br /&gt;      puts "to_proc result: #{result.inspect}"&lt;br /&gt;      result&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# Make the call and see what happens&lt;br /&gt;[1].collect( &amp;:to_s)&lt;br /&gt;# to_proc args: [1]&lt;br /&gt;# to_proc: 1.__send__(:to_s, *[])&lt;br /&gt;# to_proc result: "1"&lt;br /&gt;# =&gt; ["1"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Its brute force but tells us everything we need to know.  As expected &lt;span style="font-family: courier new;"&gt;collect&lt;/span&gt; yields to our proc/block with the element in a variable length argument &lt;span style="font-family: courier new;"&gt;[1]&lt;/span&gt;, it extracts the &lt;span style="font-family: courier new;"&gt;1&lt;/span&gt; and sends it the &lt;span style="font-family: courier new;"&gt;to_s&lt;/span&gt; method with no arguments returning the string &lt;span style="font-family: courier new;"&gt;"1"&lt;/span&gt;.  At this point I think I  understand how it all works and decide to confirm by running a few more (more complicated) tests in irb&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[1, 'hi', :b].collect( &amp;:to_s)&lt;br /&gt;# to_proc args: [1]&lt;br /&gt;# to_proc: 1.__send__(:to_s, *[])&lt;br /&gt;# to_proc result: "1"&lt;br /&gt;# to_proc args: ["hi"]&lt;br /&gt;# to_proc: "hi".__send__(:to_s, *[])&lt;br /&gt;# to_proc result: "hi"&lt;br /&gt;# to_proc args: [:b]&lt;br /&gt;# to_proc: :b.__send__(:to_s, *[])&lt;br /&gt;# to_proc result: "b"&lt;br /&gt;# =&gt; ["1", "hi", "b"]&lt;br /&gt;&lt;br /&gt;[1,2,3].inject(&amp;:+)&lt;br /&gt;# to_proc args: [1, 2]&lt;br /&gt;# to_proc: 1.__send__(:+, *[2])&lt;br /&gt;# to_proc result: 3&lt;br /&gt;# to_proc args: [3, 3]&lt;br /&gt;# to_proc: 3.__send__(:+, *[3])&lt;br /&gt;# to_proc result: 6&lt;br /&gt;# =&gt; 6&lt;br /&gt;&lt;br /&gt;1.__send__(:+, *[2])&lt;br /&gt;# =&gt; 3&lt;br /&gt;&lt;br /&gt;:b.__send__(:to_s, *[])&lt;br /&gt;# =&gt; "b"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It all works as expected and I decide I know as much as I need to about this and call it a day.&lt;br /&gt;&lt;br /&gt;So why did I bother figuring all this out and then writing it up?  Mostly because I didn't know how it worked and thought there was some 'magic' going on.  I could have continued using this feature without understanding how it worked but now that I understand how it works if some need ever arises for me to do some similar magic I know how to go about it.  As for writing it up I hope someone else may read this and find it useful but by I increased my own understanding through the act of writing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-8845862891270009488?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/Uqb5BGj0BP0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/8845862891270009488/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=8845862891270009488" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8845862891270009488?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8845862891270009488?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/Uqb5BGj0BP0/investigating-how-symbol-toproc-works.html" title="Investigating how Symbol to_proc works" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/02/investigating-how-symbol-toproc-works.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0AGQnozeyp7ImA9WxVQGEg.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-2419345969142927990</id><published>2009-02-05T13:41:00.002-05:00</published><updated>2009-02-05T13:48:43.483-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-05T13:48:43.483-05:00</app:edited><title>Maintaining your technical chops is a full time job</title><content type="html">Great quote from &lt;a href="http://blog.objectmentor.com/articles/2009/01/31/quality-doesnt-matter-that-much-jeff-and-joel"&gt;Uncle Bob&lt;/a&gt; (near the bottom of the post)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I think that maintaining your technical chops is a full time job. For that reason I have avoided becoming a business wonk. I hire people to do that for me so I can keep my technical skills as sharp as possible and remain relevant to my profession. I don’t believe I can offer technical advice unless I am living that technical advice.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-2419345969142927990?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/OXazLc5wAsM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/2419345969142927990/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=2419345969142927990" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2419345969142927990?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/2419345969142927990?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/OXazLc5wAsM/maintaining-your-technical-chops-is.html" title="Maintaining your technical chops is a full time job" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/02/maintaining-your-technical-chops-is.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEDQX48cSp7ImA9WxVQF0U.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5062424033561747392</id><published>2009-02-04T15:42:00.002-05:00</published><updated>2009-02-04T16:24:30.079-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-04T16:24:30.079-05:00</app:edited><title>Never sacrifice quality for speed!</title><content type="html">I just read two really good articles by &lt;a href="http://xprogramming.com/blog/2009/02/01/quality-speed-tradeoff-youre-kidding-yourself/"&gt;Ron Jeffries&lt;/a&gt; and &lt;a href="http://blog.objectmentor.com/articles/2009/02/03/speed-kills"&gt;Uncle Bob&lt;/a&gt; about why sacrificing quality to go faster is always a bad idea.  &lt;br /&gt;&lt;br /&gt;This is very relevant to me now as I'm working with a sponsor now who thinks that by pushing harder and 'doing it in parallel' he can get everything he wants by the date he wants avoiding any hard decisions involving tradeoffs between scope and date.  I have worked on many teams over the years that could speed up without sacrificing quality by focusing on the right things basically by emphasizing working software over high ceremony (pretty much straight from the &lt;a href="http://agilemanifesto.org/"&gt;Agile Manifesto&lt;/a&gt;).  However the idea that you can get something out the door quickly without "wasting your time on quality" to me means that the person you're talking to doesn't understand quality.  They think quality means &lt;span style="font-style:italic;"&gt;those silly engineering things developers spend their time on&lt;/span&gt; rather than &lt;span style="font-style:italic;"&gt;software that works the way you want and can be reliably enhanced later&lt;/span&gt;.  &lt;br /&gt;&lt;br /&gt;Somehow we as technologists need to do a better job of explaining to our business sponsors that "quality" is not a technical term!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5062424033561747392?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/curw9O8uQRc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5062424033561747392/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5062424033561747392" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5062424033561747392?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5062424033561747392?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/curw9O8uQRc/never-sacrifice-quality-for-speed.html" title="Never sacrifice quality for speed!" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/02/never-sacrifice-quality-for-speed.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEGQH4yfyp7ImA9WxVSFUg.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-7785237345109392683</id><published>2009-01-09T15:10:00.002-05:00</published><updated>2009-01-09T20:57:01.097-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-09T20:57:01.097-05:00</app:edited><title>How I refine a test spec while writing getting it to green</title><content type="html">In most of the examples I've read on TDD they show the Red-Green-Refactor cycle as &lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;Write a failing test&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Write just enough code to make the test pass&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Refactor&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Repeat&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;I absolutely do believe in this cycle and live it every day however I think there's a small detail that differs from what I do.  When I move from step 1 to step 2, I keep my test window open and will switch back and forth between the test and code windows and refining the test while writing the code that makes the test pass.  Its only as I write the code that I realize what calls I need to mock out in the test which also adds additional expectations to my test.  I'm sure this is how most people work but I've never seen it written up so I'm going to try going through an example here. &lt;br /&gt;&lt;br /&gt;Let's say I have a social networking application with a page showing friend requests where you can accept or reject each request.  Something like this&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color:#F5F6E7"&gt;&lt;br /&gt;  Friend Requests:&lt;br /&gt;  &lt;form onclick='return false'&gt;&lt;br /&gt;    &lt;input name="approved_requests[]" type="checkbox" value="123" /&gt; Accept request from Pat? &lt;br /&gt;    &lt;input name="approved_requests[]" type="checkbox" value="124" /&gt; Accept request from Gourav? &lt;br /&gt;  &lt;/form&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;  &lt;br /&gt;Now I'm ready to implement an action in the Friend Controller that receives a posted form with a list of friend requests to accept and reject.  I might first go into my &lt;span style="font-family: courier new;"&gt;friends_controller_spec.rb&lt;/span&gt; and write something like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;describe FriendController do&lt;br /&gt;  it 'should approve one friend requests' do&lt;br /&gt;    post :update_requests, :approved_requests =&gt; ['123']&lt;br /&gt;    response.should be_redirect&lt;br /&gt;    response.should redirect_to(:back)&lt;br /&gt;    flash[:notice].should  == "Friend requests approved."&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I have a failing test so I start implementing the code&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class FriendController &lt; ApplicationController&lt;br /&gt;  def update_requests&lt;br /&gt;    FriendRequest.approve(params[:approve_requests])&lt;br /&gt;    flash[:notice] = "User access changes saved."&lt;br /&gt;    redirect_to :back&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;  &lt;br /&gt;I'm done but my test is still failing...why?  I haven't added the &lt;span style="font-family: courier new;"&gt;approve&lt;/span&gt; method to my &lt;span style="font-family: courier new;"&gt;FriendRequest&lt;/span&gt; model yet.  I could go ahead and implement that method to get this test to pass but I don't want to do that because then this test would be testing more than just one unit.  This is a unit test for the update_requests method of my FriendController and should be isolated from bugs in the rest of my application.  Time to go back to the spec and add a mock to isolate this spec from the model and impose another expectation on my spec (that the method be called with the correct argument).  &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;describe FriendController do&lt;br /&gt;  it 'should approve one friend requests' do&lt;br /&gt;    FriendRequest.expects(:approve).with(['123'])&lt;br /&gt;    request.env['HTTP_REFERER'] = "http://some.site.com"&lt;br /&gt;  &lt;br /&gt;    post :update_requests, :approved_requests =&gt; ['123']&lt;br /&gt;    response.should be_redirect&lt;br /&gt;    response.should redirect_to(:back)&lt;br /&gt;    flash[:notice].should  == "Friend requests approved."&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now my spec passes and I can do my refactoring then go back to the beginning and write my next expectation.  &lt;br /&gt;&lt;br /&gt;The point of this example is that when I first wrote the spec I hadn't yet thought about how I would implement update_requests so did not yet know that I would need to mock FriendRequest.approve.  I do this all the time and typically find myself go back and forth between the code and spec many times with the spec telling me what code I need to write and the code telling me how to refine the spec.  &lt;br /&gt;&lt;br /&gt;One last point (and a teaser for a future post).  You have probably noticed that although my spec passes if I try to run the application it will not work because I still haven't written the FriendRequest.approve method.  In a future post I hope to discuss how to interleave integration testing into this process to ensure that the classes we're writing in isolation also integrate so that the application satisfies its business function.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-7785237345109392683?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/-bB32mpLNjk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/7785237345109392683/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=7785237345109392683" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7785237345109392683?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7785237345109392683?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/-bB32mpLNjk/how-i-refine-test-spec-while-writing.html" title="How I refine a test spec while writing getting it to green" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2009/01/how-i-refine-test-spec-while-writing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUAHQ348eCp7ImA9WxRaEE8.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-5259128325601708836</id><published>2008-12-11T13:56:00.002-05:00</published><updated>2008-12-11T14:28:52.070-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-11T14:28:52.070-05:00</app:edited><title /><content type="html">I read an interesting post today by David Chelimsky who wrote RSpec &lt;a href="http://blog.davidchelimsky.net/2008/12/11/a-case-against-a-case-against-mocking-and-stubbing"&gt;A case against a case against mocking and stubbing&lt;/a&gt;.  Its about mocking in testing and isolated vs integrated tests.  I liked it all but what I particularly liked is how it describes the process of &lt;a href="http://kinderman.net/articles/2007/11/18/testing-on-high-bottom-up-versus-top-down-test-driven-development"&gt;outside-in development&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To quote:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;   Write scenarios in plain text with cucumber (driven by user stories, organized in features).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;   Write the code for a step (or part of a step), run the feature, and observe the failure.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;   Optionally (yes, it depends – and why is the topic for another blog) drive out a view with a view spec. When I say “drive out,” I mean a very granular Red/Green/Refactor cycle that only involves this view, and only enough of this view to support satisfaction of the failing step in the cucumber feature.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;   Drive out a controller action using the same, granular Red/Green/Refactor cycle. And it may not be the entire controller action I think I want, covering all the cases I think I want. Just enough to support satisfaction of the failing step.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;   Drive out the parts of the model that I need to satisfy the failing step, using the same granular R/G/R process.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;   Run the cucumber feature and assess where I am.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;In my projects I think I'm pretty good at following 3,4 &amp;amp;5 but I've struggled with steps 1 &amp;amp; 6.  With the advent of &lt;a href="http://github.com/aslakhellesoy/cucumber/wikis"&gt;cucumber&lt;/a&gt; I think now is the time to work on those other steps.  I'll let you know how it goes in the New Year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-5259128325601708836?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/m0hTFlY8Ryg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/5259128325601708836/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=5259128325601708836" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5259128325601708836?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/5259128325601708836?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/m0hTFlY8Ryg/i-read-interesting-post-today-by-david.html" title="" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2008/12/i-read-interesting-post-today-by-david.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0INRHg4eSp7ImA9WxRbFkw.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-1812571187797516405</id><published>2008-12-06T19:39:00.002-05:00</published><updated>2008-12-06T19:59:55.631-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-06T19:59:55.631-05:00</app:edited><title>Bug/patch with rspec-rails and helper instance variables</title><content type="html">I finally got around to upgrading my version of rspec-rails from one that's almost a year old and came across an issue with the way &lt;a href="http://blog.davidchelimsky.net/2008/5/29/rspec-waving-bye-bye-to-implicit-module-inclusion"&gt;implicit module inclusion&lt;/a&gt; is handled.&lt;br /&gt;&lt;br /&gt;If you have a handler that uses memoization to cache some information in instance variables such as this (I'm not sure if this is a smell but my project has some examples of it)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;module UsersHelper&lt;br /&gt; def all_users&lt;br /&gt;   @users ||= User.find(:all)&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You would expect this spec to work&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;describe UsersHelper do&lt;br /&gt; it "should find all users" do&lt;br /&gt;   User.expects(:find).with(:all).returns(result=mock)&lt;br /&gt;   helper.all_users.should == result&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sometimes it does but if any other spec has called &lt;span style="font-family: courier new;"&gt;helper.all_users&lt;/span&gt; previously it will fail as the memoized &lt;span style="font-family: courier new;"&gt;@users&lt;/span&gt; variable is not nil so the collection is reused.&lt;br /&gt;&lt;br /&gt;I have submitted a &lt;a href="http://rspec.lighthouseapp.com/projects/5645-rspec/tickets/627-rspec-rails-specs-resusing-helper-so-instance-variables-are-side-effects"&gt;lighthouse patch to rspec-rails&lt;/a&gt; that fixes the problem so hopefully they will agree to fix it soon.  Until then patch is available on github in &lt;a href="http://github.com/alexrothenberg/rspec-rails/commit/2d38cb9e2a8e2eb407e07e5494382ac2f263aef4"&gt;my fork or rspec-rails&lt;/a&gt; if you want to use it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-1812571187797516405?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/oyv-SPvDei4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/1812571187797516405/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=1812571187797516405" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/1812571187797516405?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/1812571187797516405?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/oyv-SPvDei4/bugpatch-with-rspec-rails-and-helper.html" title="Bug/patch with rspec-rails and helper instance variables" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2008/12/bugpatch-with-rspec-rails-and-helper.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYBQXw7eyp7ImA9WxRbFk0.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-7655933115768146258</id><published>2008-12-06T18:41:00.004-05:00</published><updated>2008-12-06T19:02:30.203-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-06T19:02:30.203-05:00</app:edited><title>Knows which specs are slowing down my spec suite</title><content type="html">I often watch my specs run with the ........'s marching across my terminal and sometimes it seems to pause as it hits a particularly slow one.  I figure there's some poorly written spec that's slowing my entire suite down and I &lt;span style="font-style:italic;"&gt;should&lt;/span&gt; figure out what it is and fix it.  But I rarely (if ever) take the time to track down the offender so never fix it.  I just discovered that the clever folks writing rspec have taken away my excuse for laziness.  I can tell it to report on the slowest specs by putting a line in my spec.opts file telling it to use the ProfileFormatter (see the first line below)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;--format profile&lt;br /&gt;--colour&lt;br /&gt;--loadby mtime&lt;br /&gt;--reverse&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For example running the specs on my current project I get this output.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Profiling enabled.&lt;br /&gt;........(a bunch more dots)&lt;br /&gt;&lt;br /&gt;Top 10 slowest examples:&lt;br /&gt;2.4908950 WikiContent should find all activity for a user&lt;br /&gt;0.8061750 WikiContent should find recent activity for a user in last 15 days&lt;br /&gt;0.2438460 WikiContent::Version should allow limit on results from finding recent activity&lt;br /&gt;0.1821710 User should have a list of owned content&lt;br /&gt;0.1700670 WikiContent should find results if access type is not same as role of the user but role of user is 'Leader'&lt;br /&gt;0.1593900 ContentsController allows PUT request to 'update' action for 'member' role&lt;br /&gt;0.1514150 Search::SearchResponse gets the wiki content id of an asset from the xml response&lt;br /&gt;0.1470310 Content should always allow leaders to see all pages&lt;br /&gt;0.1394920 Page should delete all comments when destroyed&lt;br /&gt;0.1370330 ContentsController should save content and create the uploaded asset on successful POST to the 'create' action&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Over the next few days I will find time to take a look at 'WikiContent find all activity' and 'WikiContent find recent activity' specs as they are the two clear outliers.&lt;br /&gt;&lt;br /&gt;I now have no excuse to not optimize my slowest specs because I didn't know which they were!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-7655933115768146258?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/lHrpg_ldVpc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/7655933115768146258/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=7655933115768146258" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7655933115768146258?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/7655933115768146258?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/lHrpg_ldVpc/knows-which-specs-are-slowing-down-my.html" title="Knows which specs are slowing down my spec suite" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2008/12/knows-which-specs-are-slowing-down-my.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEMR345fSp7ImA9WxRUFk8.&quot;"><id>tag:blogger.com,1999:blog-86313661235162475.post-8343656599792343859</id><published>2008-11-25T09:31:00.003-05:00</published><updated>2008-11-25T09:51:26.025-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-25T09:51:26.025-05:00</app:edited><title>Applications usually use their own data</title><content type="html">I just read a very interesting post by Martin Fowler called &lt;a href="http://martinfowler.com/bliki/DatabaseThaw.html"&gt;Database Thaw&lt;/a&gt;.  He talks about various database technologies and what the future might hold for object or relational databases but what caught my interest was his discussion of application and database integration patterns.  &lt;br /&gt;&lt;br /&gt;He says "For many organizations today, the primary pattern for integration is &lt;a href="http://www.eaipatterns.com/SharedDataBaseIntegration.html"&gt;Shared Database Integration&lt;/a&gt; - where multiple applications are integrated by all using a common database." This is certainly true where I work.  I've even heard it said that we have one gigantic application since all (or most) share the same underlying database so cannot be updated independently.&lt;br /&gt;&lt;br /&gt;He also talks about &lt;a href="http://martinfowler.com/bliki/IntegrationDatabase.html"&gt;Integration Databases&lt;/a&gt; which store data for multiple applications and &lt;a href="http://martinfowler.com/bliki/ApplicationDatabase.html"&gt;Application Databases&lt;/a&gt; which are controlled and accessed by a single application.&lt;br /&gt;&lt;br /&gt;Historically my company has moved toward the integration database pattern often building complex service layers to enable shared access to the data on the assumption that if you build it they will come the data would be reused if it was easy enough.  However I believe that its only a very small subset of our data that is truly common and many applications want to use.  For that small subset a shared database with a service layer is probably a good design.  The vast majority is private to an application and by designing it into a separate application database can evolve to best meet the needs of the application without concern for a more general service that in our case is more hindrance than benefit.&lt;br /&gt;&lt;br /&gt;One important thing to remember about an application database is that the data exists to meet the needs of the application so a design that allows the database to best meet the application's needs will be to everyone's benefit.  Finally database technologies are very complex and do a lot so it is probably wise to have a good database developer on your team to make sure you're using the database appropriately but they should be a part of your team not on a separate database team as I've seen in the past - particularly on Java/Oracle projects I've been a part of.  &lt;br /&gt;&lt;br /&gt;Thoughts?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/86313661235162475-8343656599792343859?l=www.alexrothenberg.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CommonSenseSoftware/~4/8lpMJ4LW71Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.alexrothenberg.com/feeds/8343656599792343859/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=86313661235162475&amp;postID=8343656599792343859" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8343656599792343859?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/86313661235162475/posts/default/8343656599792343859?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CommonSenseSoftware/~3/8lpMJ4LW71Q/applications-usually-use-their-own-data.html" title="Applications usually use their own data" /><author><name>Alex Rothenberg</name><uri>http://www.blogger.com/profile/18273757675203348828</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00768929870793010489" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.alexrothenberg.com/2008/11/applications-usually-use-their-own-data.html</feedburner:origLink></entry></feed>
