<?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/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss"><id>tag:blogger.com,1999:blog-1845039873784052989</id><updated>2009-11-05T14:38:45.587-08:00</updated><title type="text">Strictly Untyped</title><subtitle type="html">Pragmatic Rails in an Idiomatic World</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/StrictlyUntyped" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-5225287423149945949</id><published>2009-09-02T22:46:00.000-07:00</published><updated>2009-09-03T00:04:55.645-07:00</updated><title type="text">Foreigner comes full circle</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;After actively developing multiple Rails projects over the last three years, I've learned when to not fight the framework, and I've also learned when it's appropriate to roll up your sleeves and write some Ruby code. I've stopped using &lt;a href="http://github.com/drnic/composite_primary_keys/tree/master"&gt;composite primary keys&lt;/a&gt;, I've stopped attempting to &lt;a href="http://www.strictlyuntyped.com/2008/08/activerecord-can-haz-namespaces.html"&gt;namespace models&lt;/a&gt;, I always use the default "id" for a primary key, the list goes on.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;One thing my mind has not changed on is using foreign keys. I use them, and I've finally completed &lt;a style="font-weight: bold" href="http://github.com/matthuhiggins/foreigner/tree/master"&gt;Foreigner&lt;/a&gt;, a foreign key migration library that meets my initial requirements:&lt;ul&gt;&lt;li&gt;Do not change existing behavior of the application&lt;/li&gt;&lt;li&gt;Perform a no-op when the adapter does not support foreign keys&lt;/li&gt;&lt;li&gt;Follow similar API as 'add_index'&lt;/li&gt;&lt;li&gt;Dump foreign keys into schema.rb&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Do not change existing behavior of the application&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I found numerous existing foreign key plugins that attempted to &lt;span style="font-style: italic"&gt;automagically&lt;/span&gt; add foreign keys. For example, &lt;a href="http://github.com/harukizaemon/foreign_key_migrations/tree/master"&gt;automatically adding foreign keys based on column names&lt;/a&gt;, or &lt;a href="http://agilewebdevelopment.com/plugins/foreign_key_associations"&gt;automatically adding associations to active record models base on foreign keys in the database&lt;/a&gt;. I believe both of these are mistakes. Automatically adding foreign keys changes existing migrations, and always seems to break things. Automatically creating active record associations puts too much guesswork into play. There's a difference between using foreign keys to enforce integrity (good), and using foreign keys to change the behavior of the application (bad).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Lastly, I thought long and hard about whether calls to &lt;span style="font-weight: bold"&gt;t.references&lt;/span&gt; should automatically add a foreign key. I finally resolved on not doing this, and compromised by adding a new &lt;span style="font-style: italic"&gt;:foreign_key&lt;/span&gt; option. For example:&lt;/span&gt;&lt;pre&gt;change_table :comments do |t|&lt;br /&gt;  t.references :author, :foreign_key =&gt; true&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Perform a no-op when the adapter does not support foreign keys&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;While this does not help me, it is required to support adapters such as sqlite3. The &lt;a href="http://github.com/matthuhiggins/foreigner/blob/904986112df534c4ae01e1da51e12876df60741b/lib/foreigner/connection_adapters/abstract/schema_statements.rb"&gt;AbstractAdapter&lt;/a&gt; implements all necessary methods with a no-op. With Foreigner installed, you can develop with sqlite3 and run production with MySql.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Follow similar API as 'add_index'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Adding foreign keys isn't much different than adding indexes, and everyone is familiar with using &lt;span style="font-weight: bold"&gt;add_index&lt;/span&gt;. You &lt;span style="font-weight: bold"&gt;add_foreign_key&lt;/span&gt;, &lt;span style="font-weight: bold"&gt;remove_foreign_key&lt;/span&gt; and can even use &lt;span style="font-weight: bold"&gt;change_table&lt;/span&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Dump foreign keys into schema.rb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The final feature I added to Foreigner is dumping foreign keys to schema.rb, so that you can avoid using the SQL structure. Similar to how indexes are added to schema.rb, Foreigner inspects the database and determines the foreign keys.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 130%"&gt;In conclusion...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There's been a lot of discussion in the past about whether or not you should be using foreign keys. This shows to me is that there is a large group of people who probably want this available out of the box with Rails. The library was designed to not affect those who don't want them; the primary purpose in doing so was to show that everyone can have it their own way. My next step is turning this into a gem.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Get the source: &lt;a href="http://github.com/matthuhiggins/foreigner/tree/master"&gt;http://github.com/matthuhiggins/foreigner/tree/master&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-5225287423149945949?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/5225287423149945949/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=5225287423149945949" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5225287423149945949" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5225287423149945949" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/09/foreigner-comes-full-circle.html" title="Foreigner comes full circle" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4316296047410965370</id><published>2009-05-14T10:31:00.001-07:00</published><updated>2009-05-14T10:35:46.132-07:00</updated><title type="text">Scopes for conditions</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It is common for your named and anonymous scopes to only specify the :conditions option. In &lt;a href="http://railscasts.com/episodes/112-anonymous-scopes"&gt;RailsCast #112&lt;/a&gt;, Ryan Bates suggests adding a &lt;span style="font-style: italic"&gt;conditions&lt;/span&gt; named_scope:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/111778.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This allows you to replace:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;active_people = People.scoped(:conditions =&gt; {:active =&gt; true})&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;with:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;active_people = People.conditions(:active =&gt; true)&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;A step further&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This can be taken a step further by adding a class method, &lt;span style="font-style: italic"&gt;named_conditions&lt;/span&gt;, to ActiveRecord::Base:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/111780.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Now you can replace:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;class Post&lt;br /&gt;  named_scope :recent, lambda { {:conditions =&gt; ['created_at &gt; ?',  1.day.ago] } }&lt;br /&gt;end&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;with:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;class Post&lt;br /&gt;  named_conditions :recent, lambda { ['created_at &gt; ?',  1.day.ago] }&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Not very exciting, but it's the little things that count. You can download the module at &lt;a href="http://gist.github.com/111783"&gt;http://gist.github.com/111783&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4316296047410965370?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4316296047410965370/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4316296047410965370" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4316296047410965370" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4316296047410965370" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/05/scopes-for-conditions.html" title="Scopes for conditions" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-7978518195073526509</id><published>2009-04-16T14:26:00.000-07:00</published><updated>2009-04-16T14:49:43.242-07:00</updated><title type="text">A Foreign Key migration plugin for Rails</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I use foreign keys in my Rails projects, and they work great. There was a point in time where Rails tests did not work well when using foreign keys, but those days are long gone. I've been hand rolling foreign key migrations for quite some time, and decided to write a plugin that covers most of my needs. This resulted in &lt;span style="font-weight: bold"&gt;Foreigner&lt;/span&gt;, which is now available at &lt;a href="http://github.com/matthuhiggins/foreigner/tree/master"&gt;http://github.com/matthuhiggins/foreigner/tree/master&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;One of my goals was to follow the API for &lt;span style="font-style: italic"&gt;add_index&lt;/span&gt; and &lt;span style="font-style: italic"&gt;remove_index&lt;/span&gt;. Another goal was to make sure migrations did not break when run against adapters that do not support foreign keys. The API simply no-ops by default, and only supported adapters perform the operation. (MySql is currently the only implemented adapter.)&lt;br /&gt;&lt;br /&gt;You should be able to add most of your foreign keys with following (See the &lt;a href="http://github.com/matthuhiggins/foreigner/tree/master#readme"&gt;Readme&lt;/a&gt; for more details.)&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;add_foreign_key(:comments, :posts)&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;If you drank the Rails lemonade and don't think foreign keys are necessary, you're correct. They're not necessary, they are a nice to have.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-7978518195073526509?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/7978518195073526509/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=7978518195073526509" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7978518195073526509" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7978518195073526509" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/04/foreign-key-migration-plugin-for-rails.html" title="A Foreign Key migration plugin for Rails" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-5885467596397755341</id><published>2009-03-21T15:42:00.000-07:00</published><updated>2009-03-21T16:38:50.141-07:00</updated><title type="text">A Survey of URL Sharing Debris</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;As a loyal follower of &lt;a href="http://www.edwardtufte.com/tufte/"&gt;Tufte&lt;/a&gt;, I believe in removing all unnecessary junk and debris from the visual design of a website. (The layout for this blog is a preselected template from Blogger; don't hold it against me). I'm going to point out a trend I'm noticing everywhere, and you will either start being irritated by it, or disagree with me. I call it &lt;span style="font-weight: bold"&gt;URL Sharing Debris&lt;/span&gt;, and it is best described with examples. I surfed to random websites to collect a few samples.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;huffingtonpost.com&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://4.bp.blogspot.com/_6FnnKREfVl8/ScVwfvKmAeI/AAAAAAAAABg/BORE0-V19oc/s400/huffingtonpost.com.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Here's your classic example. Since it's too hard to copy and paste a URL, the user is provided with approximately 8-10 icons to share the URL.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;indecisionforever.com&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_6FnnKREfVl8/ScVwz4U4biI/AAAAAAAAABo/CqWWJpJwvZM/s400/indecisionforever.com.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt; At least they realize the icons detract from the content and gray them out.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;www.dailymail.co.uk&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://4.bp.blogspot.com/_6FnnKREfVl8/ScVw0NlU-XI/AAAAAAAAAB4/YquUz_BAaFc/s400/www.dailymail.co.uk.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Since most users won't know what the icons mean, this example adds labels.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;youtube.com&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://2.bp.blogspot.com/_6FnnKREfVl8/ScVw0WXNPBI/AAAAAAAAACA/axFioWK55Mk/s400/youtube.com.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;More space is wasted on the sharing section than on the video control. Am I the only one who knows how to copy and paste the URL displayed in my browser?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;failblog.org&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_6FnnKREfVl8/ScVtmjaJ_4I/AAAAAAAAABY/v2R57wh6f30/s400/failblog.org.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Finally, a site that makes fun of the sharing links.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;scm.jadeferret.com&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_6FnnKREfVl8/ScVw0KFLCBI/AAAAAAAAABw/Ywf-MztI0SE/s400/scm.jadeferret.com.png" /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;In my quest to find the worst offender, I came across the above winner.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Final words&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I think a lot of site developers responsible for this are simply copying popular sites who started this trend. Putting a Digg button on your site does not make it popular. It needs to be popular in the first place.&lt;br /&gt;&lt;br /&gt;It is difficult to fit content on a page. It is difficult to design clean aesthetics. I believe this fad will eventually die, and join the graveyard with &lt;a href="http://en.wikipedia.org/wiki/Webring"&gt;webrings&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-5885467596397755341?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/5885467596397755341/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=5885467596397755341" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5885467596397755341" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5885467596397755341" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/03/survey-of-url-sharing-debris.html" title="A Survey of URL Sharing Debris" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_6FnnKREfVl8/ScVwfvKmAeI/AAAAAAAAABg/BORE0-V19oc/s72-c/huffingtonpost.com.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-3497929914284402963</id><published>2009-03-18T19:54:00.001-07:00</published><updated>2009-04-16T15:23:23.554-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="css" /><title type="text">5 new CSS features you can use if you drop IE6</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It is time for most sites to drop IE6, especially for projects that are just getting started. If you do make this choice, it is important to realize that there are some new CSS features that you can begin using, and some old habits worth dropping. While there are many sites that describe CSS bugs in IE6, I found no websites that describe working examples to improve your CSS and HTML if you simply drop support for it.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Here are 5 examples from my own project, &lt;a href="http://www.haikuvillage.com"&gt;Haiku Village&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;1. Attribute Selectors&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;You can use attribute selectors for any arbitrary attribute, but I have used it exclusively for form elements.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style&amp;gt;&lt;br /&gt;input[type="submit"] {&lt;br /&gt;  margin-top: 5px;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;input type="submit" value="Save" /&amp;gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;This will help you delete those arbitrary class names you made up to select certain types of inputs.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;2. Multiple Classes on a Single Element&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This is best described with an example:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style&amp;gt;&lt;br /&gt;.icon {&lt;br /&gt;  background: transparent url(/images/icons.png) no-repeat scroll 0 0;&lt;br /&gt;  padding: 2px 0 0 20px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.icon.trash {&lt;br /&gt;  background-position: 0 -80px;    &lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;span class="icon trash"&amp;gt;Delete&amp;lt;/span&amp;gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;With IE6, you either needed to make a class name of "icon-delete", or nest one element inside of the other.&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;3. Sibling Selectors&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There are many ways to use sibling selectors. My most recent use of them is to add borders between the list items in a sub menu:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style&amp;gt;&lt;br /&gt;ul.menu li + li {&lt;br /&gt;  border-left: 1px dotted #DADADA;&lt;br /&gt;  padding-left: 8px;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;Normally, you would need to introduce a "first" or "last" class name to make the borders only appear between items, and not on the outside.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;3. Child Selectors&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;If you know that a certain style should &lt;span style="font-weight: bold;"&gt;only&lt;/span&gt; apply immediately within an element, then simply insert a "&gt;" into your stylesheets:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style&amp;gt;&lt;br /&gt;ul.menu &amp;gt; li {&lt;br /&gt;    float: left;&lt;br /&gt;    margin-right: 7px;&lt;br /&gt;    padding: 3px 0 1px;&lt;br /&gt;  }&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;This will ensure that other list items within this list item do not receive the style. It also prevents the necessity to define a new class name, such as "menu-item"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;4. Use :hover on any element&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I started web development in 1995, and until recently, I never considered using the :hover meta selector for anything besides the "a" tag. Remove this from your mind, because it is no longer the case. I use :hover to expose some icons when you mouse over a haiku:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style&amp;gt;&lt;br /&gt;.haiku .actions {&lt;br /&gt;  visibility: hidden;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.haiku:hover .actions&lt;br /&gt;  :visibility visible&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;Most of us have probably worked around this by either introducing an arbitrary "a" element with an "href='#'", or by adding a class name during a 'mouseover' event.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;5. New properties available&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There are plenty of new properties that you can start using. The most significant are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic"&gt;min-width/min-height&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic"&gt;max-width/max-height&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic"&gt;background-attachment&lt;/span&gt; - You can use a value of 'fixed'&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;There are hacks to make all of these work in IE6, but that's the point: You should stop the hacking. Also, did I mention transparent PNGs?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-3497929914284402963?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/3497929914284402963/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=3497929914284402963" title="13 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3497929914284402963" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3497929914284402963" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/03/5-new-css-features-you-can-use-if-you_18.html" title="5 new CSS features you can use if you drop IE6" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-7058858962358531913</id><published>2009-01-18T22:40:00.000-08:00</published><updated>2009-01-18T22:57:45.271-08:00</updated><title type="text">A matter of style...</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I've seen plenty of code examples that show how to set the current user or subdomain to a variable. For example:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;8&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfileController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  before_filter &lt;span class="sy"&gt;:set_current_user&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  private&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;set_current_user&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      &lt;span class="iv"&gt;@current_user&lt;/span&gt; = &lt;span class="co"&gt;User&lt;/span&gt;.find_by_username(session[&lt;span class="sy"&gt;:username&lt;/span&gt;]) &lt;span class="r"&gt;if&lt;/span&gt; session[&lt;span class="sy"&gt;:username&lt;/span&gt;]&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;However, I prefer this way:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfileController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  private&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;current_user&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      &lt;span class="iv"&gt;@current_user&lt;/span&gt; ||= &lt;span class="co"&gt;User&lt;/span&gt;.find_by_username(session[&lt;span class="sy"&gt;:username&lt;/span&gt;]) &lt;span class="r"&gt;if&lt;/span&gt; session[&lt;span class="sy"&gt;:username&lt;/span&gt;]&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    helper_method &lt;span class="sy"&gt;:current_user&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;With this technique, I always use the &lt;span style="font-weight: bold;"&gt;current_user&lt;/span&gt; method to determine the current user, and never reference the &lt;span style="font-weight: bold;"&gt;@current_user&lt;/span&gt; variable. If an action doesn't need to know the current_user, no time is wasted to set it.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-7058858962358531913?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/7058858962358531913/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=7058858962358531913" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7058858962358531913" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7058858962358531913" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2009/01/matter-of-style.html" title="A matter of style..." /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-5130586814545070201</id><published>2008-11-26T16:16:00.001-08:00</published><updated>2008-11-26T16:27:54.075-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="action_view" /><category scheme="http://www.blogger.com/atom/ns#" term="action_pack" /><title type="text">I killed ApplicationController</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;For the longest time, &lt;a href="http://blog.jayfields.com/2008/01/rails-changing-applicationrb-to.html"&gt;developers have asked&lt;/a&gt; for &lt;span style="font-style: italic"&gt;application.rb&lt;/span&gt; to be renamed to &lt;span style="font-style: italic"&gt;application_controller.rb&lt;/span&gt;. This is finally fixed in &lt;a href="http://github.com/rails/rails/commit/fcce1f17eaf9993b0210fe8e2a8117b61a1f0f69"&gt;RailsEdge&lt;/a&gt;, and there's even a &lt;a href="http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/"&gt;Death of Application.rb&lt;/a&gt; blog posting about it.&lt;br /&gt;&lt;br /&gt;That's great, but none of this affects me. Why? Because ApplicationController always annoyed me, so I killed it.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Life without ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;You might ask, "how do you survive without this loving, parental figure; the &lt;span style="font-style: italic"&gt;ApplicationController&lt;/span&gt;?" Because my parent is &lt;span style="font-style: italic"&gt;ActionController::Base&lt;/span&gt;, in the same way that most of your models inherit from &lt;span style="font-style: italic"&gt;ActiveRecord::Base&lt;/span&gt;. Can you tell me why your models don't inherit from an arbitrary &lt;span style="font-style: italic"&gt;ApplicationRecord&lt;/span&gt; class?&lt;br /&gt;&lt;br /&gt;The primary reason why a developer puts code into &lt;span style="font-style: italic"&gt;ApplicationController&lt;/span&gt; is for &lt;span style="font-weight: bold;"&gt;code reuse&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;changing default behavior&lt;/span&gt;. The ultimate result is that this class becomes a dumping ground for any common methods required across two or more controllers. Ruby has a better pattern for reusing methods and changing behavior across classes: &lt;span style="font-weight: bold;"&gt;Modules&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I create my own controller modules in the &lt;span style="font-style: italic"&gt;lib&lt;/span&gt; folder, and include them into &lt;span style="font-style: italic"&gt;ActionController::Base&lt;/span&gt;. If I want only certain controllers to be affected, the module defines a class level method, similar to the &lt;span style="font-style: italic"&gt;before_filter&lt;/span&gt; and &lt;span style="font-style: italic"&gt;has_many&lt;/span&gt; macros. For example, some controllers require a login. Rather than adding a filter to &lt;span style="font-style: italic"&gt;ApplicationController&lt;/span&gt;, I would make a 'require_login' module. My controllers could optionally use it like this:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PostController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  require_login&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Why do I go through all this trouble? Because it (1) cuts one layer out of the inheritance tree, and (2) forces me to think about packaging functionality into small, testable modules. I encourage you to look at the code in your &lt;span style="font-style: italic"&gt;ApplicationController&lt;/span&gt;, and question whether any or all of your controllers should be inheriting from it.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-5130586814545070201?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/5130586814545070201/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=5130586814545070201" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5130586814545070201" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/5130586814545070201" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/11/i-killed-applicationcontroller.html" title="I killed ApplicationController" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-8729143752565526792</id><published>2008-11-21T21:25:00.000-08:00</published><updated>2008-11-21T21:34:33.852-08:00</updated><title type="text">I am not dead</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;To the dedicated numbers subscribing to my blog: I'd like to notify everyone that I still exist. I just awakened from a temporary, semi-comatose state. Thankfully, I have numerous blog posts queued in my non-volatile persistence mechanism.&lt;br /&gt;&lt;br /&gt;Please stand by for tips in perfectionism with HTTP, SQL and Ruby on Rails.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-8729143752565526792?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/8729143752565526792/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=8729143752565526792" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/8729143752565526792" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/8729143752565526792" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/11/i-am-not-dead.html" title="I am not dead" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4175402619744940285</id><published>2008-09-15T22:38:00.000-07:00</published><updated>2008-09-16T09:53:45.336-07:00</updated><title type="text">Ruby 1.8.7's Enumerator Class</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Early in my Ruby career, I found myself needing access to the &lt;span style="font-style: italic;"&gt;index&lt;/span&gt; while using &lt;span style="font-weight: bold;"&gt;Enumerable&lt;/span&gt; methods such as &lt;span style="font-style: italic;"&gt;map&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;select&lt;/span&gt;. I quickly discovered the obscurely documented &lt;span style="font-style: italic;"&gt;enum_for&lt;/span&gt; method:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="s"&gt;&lt;span class="dl"&gt;%w(&lt;/span&gt;&lt;span class="k"&gt;fun with enumerator&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;.enum_for(&lt;span class="sy"&gt;:each_with_index&lt;/span&gt;).map { |value, i| &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;i&lt;span class="i"&gt;+1&lt;/span&gt;&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;. &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;value&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; }&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Since MacPorts is now on Ruby 1.8.7, I read the &lt;a href="http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7_preview1/NEWS"&gt;1.8.7 summary log&lt;/a&gt; and noticed a bunch of existing methods that now "Return an enumerator if no block is given." I soon realized that the above can now be written as:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="s"&gt;&lt;span class="dl"&gt;%w(&lt;/span&gt;&lt;span class="k"&gt;fun with enumerator&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;.each_with_index.map { |value, i| &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;i&lt;span class="i"&gt;+1&lt;/span&gt;&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;. &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;value&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; }&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It's a seemingly simple and minor improvement, but I think this change will encourage developers to use &lt;span style="font-weight: bold;"&gt;Enumerator&lt;/span&gt; more often with Ruby. For example, the &lt;span style="font-style: italic;"&gt;enum_for&lt;/span&gt; method is never used in Rails 2.1. Contrarily, I bet that the new "Return an enumerator if no block is given" change will be used in Rails (eventually).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Arbitrary Uses&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span style="font-size:85%;"&gt;Since Integer#downto returns an Enumerator, let's write factorial:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;n.downto(&lt;span class="i"&gt;1&lt;/span&gt;).inject(&lt;span class="sy"&gt;:*&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Why not sort the letters in a word? Let's combine &lt;span style="font-style: italic;"&gt;each_char&lt;/span&gt; with &lt;span style="font-style: italic;"&gt;sort:&lt;/span&gt;&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;rails&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;.each_char.sort&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Since &lt;span style="font-weight: bold;"&gt;Enumerator&lt;/span&gt; is just an object, we can pass it around, and customize how other methods behave. For example, here I determine whether the items are processed in ascending or descending order:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;do_stuff&lt;/span&gt;(enumerator)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="c"&gt;# stuff&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;do_stuff([&lt;span class="i"&gt;1&lt;/span&gt;,&lt;span class="i"&gt;3&lt;/span&gt;,&lt;span class="i"&gt;5&lt;/span&gt;].reverse_each)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;do_stuff([&lt;span class="i"&gt;1&lt;/span&gt;,&lt;span class="i"&gt;3&lt;/span&gt;,&lt;span class="i"&gt;5&lt;/span&gt;].each)&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;All of this fun can be added to prior versions of Ruby by opening classes. I think this might work...&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;8&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;9&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;11&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;12&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;13&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;14&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Array&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  [&lt;span class="sy"&gt;:find&lt;/span&gt;, &lt;span class="sy"&gt;:find_all&lt;/span&gt;, &lt;span class="sy"&gt;:partition&lt;/span&gt;, &lt;span class="sy"&gt;:reject&lt;/span&gt;, &lt;span class="sy"&gt;:select&lt;/span&gt;, &lt;span class="sy"&gt;:sort_by&lt;/span&gt;].each &lt;span class="r"&gt;do&lt;/span&gt; |method|&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    class_eval &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;lt;&amp;lt;-EOV&lt;/span&gt;&lt;/span&gt;&lt;span class="s"&gt;&lt;span class="k"&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      def &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;method&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;_with_enumerator(*args)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;        if block_given?&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;          &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;method&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;_without_enumerator(*args)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;        else&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;          Enumerable::Enumerator.new(self, &lt;span class="sy"&gt;:enumerator&lt;/span&gt;, *args)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;        end&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      end&lt;/span&gt;&lt;span class="dl"&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    EOV&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    alias_method_chain method&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Please comment and let me know if there are better uses for this change!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4175402619744940285?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4175402619744940285/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4175402619744940285" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4175402619744940285" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4175402619744940285" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/09/ruby-187s-enumerator-class.html" title="Ruby 1.8.7's Enumerator Class" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-1903235126181481333</id><published>2008-08-29T18:47:00.000-07:00</published><updated>2008-08-29T19:13:37.235-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="action_view" /><title type="text">Obsessing over URLs</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;One of the most important components of a website is URL design. I have obsessed over URLs since the beginning of time, and &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt; caused me to focus on them even more. Here's a classic example of a clean URL:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;www.strictlyuntyped.com/articles/1/comments/1&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;This seems quite ordinary by today's standards. But admit it, a few years ago us web developers were getting really excited about this stuff.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;What was the original problem?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Let's start from the beginning. The 'dirty' version of the above URL:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;www.strictlyuntyped.com/read?article_id=1&amp;comment_id=1&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;One of the most notable problems with this type of URL is that the user sees it. The URL is an important piece of the user experience, since (1) it is displayed at the top of the browser window, (2) users determine where they are on a site by the URL, and (3) they type in and share URLs, so they must be 'readable'. Essentially, no information is provided to the user about the structure and/or hierarchy of your site.&lt;br /&gt;&lt;br /&gt;The second problem with dirty URLs are developer facing. The application code pretty much consists of a function that takes in a hash. A Ruby analogy would be the following method:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;read&lt;/span&gt;(options={})&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The method has zero self-documentation, and a comment would be required to determine what options it expects. Our world here is essentially a one dimensional array of methods that take a hash.&lt;br /&gt;&lt;br /&gt;As a comparison, here is what I believe the Rails analogy of the aforementioned clean URL would be:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="co"&gt;Article&lt;/span&gt;.find(article_id).comments.find(comment_id)&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The idea of introducing structure into a line of characters is a simple, yet profound, concept. It seems to have become a requirement for any 'Web 2.0' site.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Don't go overboard with clean RESTful URLs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Clean URLs and REST works incredibly well for content managing systems such as blogs. Without coincidence, Rails works very well with these types of systems. In other cases, I see myself, and others, forcing a square through a round hole when it comes to URL design. Here are two mistakes that myself, and probably others, have made. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mistake #1: Overcomplicated Hierarchies&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There exists an implicit hierarchy to clean URL design; an article having comments is a good example. On the other hand, there is no reason to create deeply nested hierarchies for the sake of representing the real world. &lt;a href="http://weblog.jamisbuck.org/2007/2/5/nesting-resources"&gt;the { buckblogs :here }&lt;/a&gt; says not to nest resources more than one level deep from a programmatic point of view. I completely agree, and amend his reasons from an information design perspective. If you were to make a website about animals, would the urls represent &lt;a href="http://upload.wikimedia.org/wikipedia/commons/5/5f/Biological_classification_L_Pengo.svg"&gt;biological classifications?&lt;/a&gt; Here is what the URL for a &lt;span style="font-style"&gt;lion&lt;/span&gt; will look like:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;/kingdom/animalia/phylum/chordata/class/mammalia/order/carnivora/family/felidae/genus/panthera/species/lion&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;The point is, flatten your hierarchy and &lt;a href="http://blogs.sun.com/MartinHardee/entry/tufte_story_answerbook"&gt;make Tufte proud!&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mistake #2: Unordered Parameters&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Oftentimes, the input required for a URL does not fit in a hierarchy. A great analogy is a Ruby method. Think of how many methods in Ruby on Rails use the following format:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;some_method&lt;/span&gt;(foo, bar, options = {})&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This method makes the options optional, and they can be passed in without any specific order. Why not leverage these same benefits with a URL? With that in mind, there is nothing wrong with the following URL:&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;www.strictlyuntyped.com/articles/1/comments?order=date&amp;rating=4&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;How else would Google Maps make &lt;a href="http://maps.google.com/maps?f=q&amp;hl=en&amp;geocode=&amp;q=Machu+Picchu,+peru&amp;ie=UTF8&amp;cd=1&amp;ll=-13.162849,-72.515821&amp;spn=5.678343,8.360596&amp;z=7"&gt;locations sharable?&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To wrap up my ramblings, don't get stuck with your project obsessing over how perfect your URLs are. Keep the hierarchy simple, and use a hash of parameters when you need it.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-1903235126181481333?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/1903235126181481333/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=1903235126181481333" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/1903235126181481333" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/1903235126181481333" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/08/obsessing-over-urls.html" title="Obsessing over URLs" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-6586568227825883988</id><published>2008-08-14T09:40:00.001-07:00</published><updated>2008-08-14T11:19:41.623-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="active_record" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><category scheme="http://www.blogger.com/atom/ns#" term="fixtures" /><title type="text">ActiveRecord: Can haz namespaces?</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There are plenty of blog postings from &lt;a href="http://blog.hasmanythrough.com/2008/5/6/a-simple-alternative-to-namespaced-models"&gt;has_many :through&lt;/a&gt;, &lt;a href="http://errtheblog.com/posts/3-organize-your-models"&gt;err the blog&lt;/a&gt; and &lt;a href="http://m.onkey.org/2007/12/9/namespaced-models"&gt;Pratik's blog&lt;/a&gt; describing how to completely avoid namespaced models. I unfortunately discovered these sites only after struggling with namespaces myself.&lt;br /&gt;  &lt;br /&gt;If namespaces were completely unsupported by ActiveRecord, I would leave it at that. Contrarily, namespaces are moderately supported, but lead first timers to the spooky edges of the Rails framework.&lt;br /&gt;  &lt;br /&gt;After giving it some thought, I determined that the best way to describe the state of namespacing in ActiveRecord is to act as a first time novice, pretending to use them for the first time!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The Requirements&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I am an intern working for a company that has built their entire website with Ruby on Rails. My boss wants me to add statistics tracking similar to Google Analytics, and he wants it all to go into the &lt;span style="font-weight: bold;"&gt;Statistics::&lt;/span&gt; namespace. The initial requirements include tracking the milliseconds that each HTTP request took to complete, how long users remained logged into their session, and which session each request belonged to.&lt;br /&gt;&lt;br /&gt;This sounds like a really easy project, and I want to impress my boss by using all the best practices and conventions of Rails.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Table Name Surprise&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I will start with a model that stores the length of each request. It makes sense to use a Rails script to generate &lt;span style="font-weight: bold;"&gt;Statistics::Request&lt;/span&gt;:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;8&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;9&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;ruby script/generate model Statistics::Request milliseconds:integer&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  app/models/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  test/unit/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  test/fixtures/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  app/models/statistics/request.rb&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  test/unit/statistics/request_test.rb&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  test/fixtures/statistics_requests.yml&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  db/migrate&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    create  db/migrate/20080814034925_create_statistics_requests.rb&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The generator conveniently created a statistics folder inside both &lt;span style="font-style: italic;"&gt;app/models&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;test/unit&lt;/span&gt;. I am a little bit confused as to why the folder &lt;span style="font-style: italic;"&gt;test/fixtures/statistics&lt;/span&gt; was created, when the actual fixture was placed in &lt;span style="font-style: italic;"&gt;test/fixtures/statistics_requests.yml&lt;/span&gt;, but I will figure that out later.&lt;br /&gt;  &lt;br /&gt;The table name in the migration is &lt;span style="font-style: italic;"&gt;statistics_requests&lt;/span&gt;, so I immediately assume that table names include the namespace. After migrating the database, I fire up the console to try out the model:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; Statistics::Request.create(:milliseconds =&amp;gt; 10)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;ActiveRecord::StatementInvalid: Could not find table 'requests'&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;How strange, I thought the table's name is &lt;span&gt;statistics_requests&lt;/span&gt;. Just to make sure:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; Statistics::Request.table_name&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;=&amp;gt; &amp;quot;requests&amp;quot;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Is this a bug in Rails? Apparently not. I guess it's time to do some dirty monkey patching:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.class_eval &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.table_name&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    name.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;::&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).map { |package| package.underscore.pluralize }.join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Good, now the model can find its table. Time to write some fixtures and tests.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Tests are looking good&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Feeling confident after getting my namespaced model to work, it would be nice to make sure the the new test runs:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; rake test:units&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;... &amp;quot;test/unit/statistics/request_test.rb&amp;quot;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;1 tests, 1 assertions, 0 failures, 0 errors&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Sweet - It looks like tests are discovered recursively in the test/unit folder.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Associations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I need to add the &lt;span style="font-weight: bold;"&gt;Statistics::Session&lt;/span&gt; model, and also update &lt;span style="font-weight: bold;"&gt;Statistics::Request&lt;/span&gt; to belong to a session.&lt;br /&gt;&lt;br /&gt;The migration:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;create_table &lt;span class="sy"&gt;:statistics_sessions&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  t.datetime &lt;span class="sy"&gt;:start&lt;/span&gt;, &lt;span class="sy"&gt;:end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;change_table &lt;span class="sy"&gt;:statistics_requests&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  t.integer &lt;span class="sy"&gt;:session_id&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The model:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Statistics::Request&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  belongs_to &lt;span class="sy"&gt;:session&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I use the console to test out the association:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; session = Statistics::Session.create(:start =&amp;gt; 4.hours.ago, :end =&amp;gt; Time.now)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;=&amp;gt; #&amp;lt;Statistics::Session id: 1, start: &amp;quot;2008-08-14 00:42:00&amp;quot;, end: &amp;quot;2008-08-14 04:42:00&amp;quot;&amp;gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&amp;gt;&amp;gt; request = Statistics::Request.create(:milliseconds =&amp;gt; 10, :session =&amp;gt; session)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;ArgumentError: Statistics is not missing constant Session!&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This is a bizarre error. It turns out that Rails can't find &lt;span style="font-weight: bold;"&gt;Statistics::Request&lt;/span&gt; from &lt;span style="font-weight: bold;"&gt;Statistics::Session&lt;/span&gt;. I finally figured out that I need to do this:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;belongs_to &lt;span class="sy"&gt;:session&lt;/span&gt;, &lt;span class="sy"&gt;:class_name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Statistics::Session&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It would make more sense if I only had to specify :class_name if it existed in a different namespace.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Namespaced Fixtures = WTF?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Development and testing will be easier if I generate some fixture data. Remember when the model generator script created an unused folder?&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;create  test/fixtures/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;...&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;create  test/fixtures/statistics_requests.yml&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Based on the other conventions, it seems like this file belongs in test/fixtures/statistics/requests.yml. I'll move it there, and load up some fake data:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;rake db:fixtures:load&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It will be fun to play around with all that fixture data:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; Statistics::Request.count&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;=&amp;gt; 0&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;How bizarre, the data did not even get loaded. I soon find out that unlike unit tests, fixture files are not recursively loaded. The file can't be moved after all.&lt;br /&gt;&lt;br /&gt;I need to get the request -&gt; session association working in the fixtures too. Let me edit &lt;span style="font-style: italic"&gt;statistics_sessions.yml&lt;/span&gt; and &lt;span style="font-style: italic"&gt;statistics_requests.yml&lt;/span&gt;&lt;/span&gt;:&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;long_session:&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  start: 2008-08-13 21:32:32&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  end: 2008-08-13 21:32:32&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;short_request:&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  milliseconds: 1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  session: long_session&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Unfortunately, this does not work:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;rake db:fixtures:load&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;rake aborted!&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;SQLite3::SQLException: table statistics_requests has no column named session...&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;For some reason, the label referencing is not working with my namespaced models. I guess I need to hand hold Rails on this one:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;short_request:&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  milliseconds: &lt;span class="i"&gt;1&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  session: &amp;lt;&lt;span class="s"&gt;&lt;span class="dl"&gt;%=&lt;/span&gt;&lt;span class="k"&gt; Fixtures.identify(:long_session) %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Observers - OMG they work!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Feeling a little down after my battle with fixtures, I decide to make a request observer that logs whenever we get a request:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;ruby script/generate observer Statistics::Request&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  exists  app/models/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  exists  test/unit/statistics&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  create  app/models/statistics/request_observer.rb&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  create  test/unit/statistics/request_observer_test.rb&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Looking good so far. Let me implement the class:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Statistics::RequestObserver&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Observer&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;after_create&lt;/span&gt;(request)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    puts &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;We got a hit!!!&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I also won't forget to add the following line to &lt;span style="font-style: italic;"&gt;environment.rb&lt;/span&gt;:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;config.active_record.observers = &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;statistics/request_observer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;And viola, it all works:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&amp;gt;&amp;gt; Statistics::Request.create(:milliseconds =&amp;gt; 10)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;We got a hit!!!&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;In Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Ok, I'm done playing stupid. My final word is that namespaced models can be made to work, but there are some rough edges. Some final tips for you:&lt;ul&gt;&lt;li&gt;Don't have conflicting namespaces and class names. For example, I should not introduce a class named &lt;span style="font-weight: bold;"&gt;Statistics&lt;/span&gt; in the above example. Otherwise, I will end up seeing the following message: "warning: toplevel constant Statistics referenced by Statistics::Statistics".&lt;/li&gt;&lt;li&gt;Even when referencing other classes in the same namespace, it is best to always include the namespace prefix. Otherwise, you will run into issues with conflicting class names across different namespaces and Rails' automagical class loading.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Some things that Rails should do:&lt;ul&gt;&lt;li&gt;All APIs in Rails that support defining classes by Symbols should also support Strings. I was fortunate that I could pass in 'statistics/request_observer' to active_record.observers. However, other methods such as &lt;span style="font-weight: bold;"&gt;ActiveRecord::Observer.observe&lt;/span&gt; do not support strings.&lt;/li&gt;&lt;li&gt;I believe that there is enough reason to support prefixing database table names with the namespace. This is as simple as introducing a new option to ActiveRecord called 'include_namespace_in_table_name'.&lt;/li&gt;&lt;li&gt;Fix the fixtures.&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-6586568227825883988?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/6586568227825883988/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=6586568227825883988" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6586568227825883988" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6586568227825883988" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/08/activerecord-can-haz-namespaces.html" title="ActiveRecord: Can haz namespaces?" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-3850579370592097544</id><published>2008-08-09T19:55:00.000-07:00</published><updated>2008-08-09T20:05:22.312-07:00</updated><title type="text">Googtaculous, now with SSL support</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This is just a short note. Google recently &lt;a href="http://googleajaxsearchapi.blogspot.com/2008/07/ajax-libraries-ssl-support-is-live.html"&gt;added SSL support&lt;/a&gt; to their AJAX Libraries API. &lt;a href="http://github.com/matthuhiggins/googtaculous/tree/master"&gt;Googtaculous&lt;/a&gt; will now automatically use &lt;span style="font-style: italic"&gt;https&lt;/span&gt; if the incoming request comes in over SSL. (http://github.com/matthuhiggins/googtaculous/tree/master).&lt;br /&gt;&lt;br /&gt;Browsers show warnings if a website includes both &lt;span style="font-style: italic"&gt;http&lt;/span&gt; and &lt;span style="font-style: italic"&gt;https&lt;/span&gt; requests. A few readers asked for this addition.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-3850579370592097544?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/3850579370592097544/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=3850579370592097544" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3850579370592097544" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3850579370592097544" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/08/googtaculous-now-with-ssl-support.html" title="Googtaculous, now with SSL support" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-6641486040252378282</id><published>2008-08-05T10:04:00.000-07:00</published><updated>2008-08-09T19:41:03.098-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="prototype" /><title type="text">In place Array methods for Prototype</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Unlike Ruby, Prototype does not offer in place iterators for Array methods such as &lt;span style="font-weight: bold;"&gt;compact&lt;/span&gt;, &lt;span style="font-weight: bold;"&gt;map&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;reject&lt;/span&gt;. I started down the road of implementing these methods, and discovered significant improvements with large arrays. Since Javascript does not support the "!" character in function names, I decided to name the in place versions with underscore prefixes.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;map&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-weight: bold;"&gt;_map&lt;/span&gt; is simple to implement. We replace the value in each index with the result of the iterator function:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;Array.prototype._map = &lt;span class="r"&gt;function&lt;/span&gt;(iterator, context) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  iterator = iterator ? iterator.bind(context) : Prototype.K;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;for&lt;/span&gt;(&lt;span class="r"&gt;var&lt;/span&gt; index = &lt;span class="i"&gt;0&lt;/span&gt;, length = &lt;span class="pc"&gt;this&lt;/span&gt;.length; index &amp;lt; length; index++) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="pc"&gt;this&lt;/span&gt;[index] = iterator(&lt;span class="pc"&gt;this&lt;/span&gt;[index], index);&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  }&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="pc"&gt;this&lt;/span&gt;;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;};&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;In this case, and the remaining ones, &lt;span style="font-style: italic;"&gt;this&lt;/span&gt; is returned so that the methods can be chained.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;reject&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;For in place &lt;span style="font-weight: bold;"&gt;_reject&lt;/span&gt;, I scan the array once from left to right. Elements that are not rejected are placed in the array from left to right, and finally the length is truncated. I believe it is important to maintain the original order of the elements:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;8&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;9&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;  &lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;Array.prototype._reject = &lt;span class="r"&gt;function&lt;/span&gt;(iterator, context) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  iterator = iterator ? iterator.bind(context) : Prototype.K;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;var&lt;/span&gt; position = &lt;span class="i"&gt;0&lt;/span&gt;;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;for&lt;/span&gt; (&lt;span class="r"&gt;var&lt;/span&gt; index = &lt;span class="i"&gt;0&lt;/span&gt;, length = &lt;span class="pc"&gt;this&lt;/span&gt;.length; index &amp;lt; length; index++) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;if&lt;/span&gt; (!iterator(&lt;span class="pc"&gt;this&lt;/span&gt;[index], index))&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      &lt;span class="pc"&gt;this&lt;/span&gt;[position++] = &lt;span class="pc"&gt;this&lt;/span&gt;[index];&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  }&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="pc"&gt;this&lt;/span&gt;.length = position;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="pc"&gt;this&lt;/span&gt;;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;};&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;compact&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-weight: bold;"&gt;_compact&lt;/span&gt; is very similar to &lt;span style="font-weight: bold;"&gt;reject&lt;/span&gt;&lt;/span&gt;:&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;1&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;2&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;3&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;4&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;5&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;6&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;7&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;8&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;9&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;Array.prototype._compact = &lt;span class="r"&gt;function&lt;/span&gt;(iterator) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;var&lt;/span&gt; position = &lt;span class="i"&gt;0&lt;/span&gt;;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;for&lt;/span&gt; (&lt;span class="r"&gt;var&lt;/span&gt; index = &lt;span class="i"&gt;0&lt;/span&gt;, length = &lt;span class="pc"&gt;this&lt;/span&gt;.length; index &amp;lt; length; index++) {&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;    &lt;span class="r"&gt;if&lt;/span&gt; (&lt;span class="pc"&gt;this&lt;/span&gt;[index] != &lt;span class="pc"&gt;null&lt;/span&gt;)&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;      &lt;span class="pc"&gt;this&lt;/span&gt;[position++] = &lt;span class="pc"&gt;this&lt;/span&gt;[index];&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  }&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="pc"&gt;this&lt;/span&gt;.length = position;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;  &lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="pc"&gt;this&lt;/span&gt;;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;};&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The above implementation can be shortened by using _reject, but it significantly reduces performance. For completeness, that might be written as:&lt;/span&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;td class="code"&gt;&lt;pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"&gt;&lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="pc"&gt;this&lt;/span&gt;._reject(&lt;span class="r"&gt;function&lt;/span&gt;(value) { &lt;span class="r"&gt;return&lt;/span&gt; value == &lt;span class="pc"&gt;null&lt;/span&gt;; });&lt;/pre&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Results&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;These are results in Firefox 3, using an array of 100,000 numbers and null values. Changing the size did not seem to affect the relative performance.&lt;br /&gt;&lt;/span&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;Method&lt;/th&gt;&lt;th&gt;Prototype Time&lt;/th&gt;&lt;th&gt;In Place Time&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;map&lt;/td&gt;&lt;td&gt;159ms&lt;/td&gt;&lt;td&gt;42ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;reject&lt;/td&gt;&lt;td&gt;158ms&lt;/td&gt;&lt;td&gt;45ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;compact&lt;/td&gt;&lt;td&gt;161ms&lt;/td&gt;&lt;td&gt;13ms&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;One should probably do a more comprehensive test, but these results are enough to show that performance gains can be made with my faster versions. In addition, less memory is being used.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-6641486040252378282?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/6641486040252378282/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=6641486040252378282" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6641486040252378282" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6641486040252378282" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/08/in-place-array-methods-for-prototype.html" title="In place Array methods for Prototype" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-6215141409302110061</id><published>2008-07-29T01:51:00.000-07:00</published><updated>2008-07-29T01:56:59.423-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><category scheme="http://www.blogger.com/atom/ns#" term="prototype" /><title type="text">Time for protodate</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I decided to stay up late and implement &lt;a href="http://www.ruby-doc.org/core/classes/Time.html"&gt;Ruby's Time.strftime&lt;/a&gt; in Javascript. Prototype's &lt;a href="http://www.prototypejs.org/api/template"&gt;Template class&lt;/a&gt; ended up making this easy to implement. To clarify, here is how you might use this function.&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; new Date().format(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Printed on %m/%d/%Y at %I:%M%p&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt; =&amp;gt; &amp;quot;Printed on 07/29/2008 at 12:31AM&amp;quot;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I checked my code into github, so take at your own peril. (&lt;a href="http://github.com/matthuhiggins/protodate/tree/master"&gt;http://github.com/matthuhiggins/protodate/tree/master&lt;/a&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span style="font-size:85%;"&gt;And yes, I know that this has &lt;a href="http://alternateidea.com/blog/articles/2008/2/8/a-strftime-for-prototype"&gt;already been blogged about at alternateidea&lt;/a&gt;. However, that particular solution converts all values to UTC, whereas I almost always want to use the browser's local time zone when prettying up dates.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-6215141409302110061?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/6215141409302110061/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=6215141409302110061" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6215141409302110061" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6215141409302110061" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/07/my-time-with-protodate.html" title="Time for protodate" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-2759932306689635505</id><published>2008-07-22T09:35:00.000-07:00</published><updated>2008-07-27T22:30:18.697-07:00</updated><title type="text">MySql Lovin' Part 3: Summing up with group_concat</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Ruby on Rails provides some handy methods for using SQL aggregate functions:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;count&lt;/li&gt;&lt;li&gt;average&lt;/li&gt;&lt;li&gt;minimum&lt;/li&gt;&lt;li&gt;maximum&lt;/li&gt;&lt;li&gt;sum&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;While this is a good start, MySql has &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html"&gt;additional functions&lt;/a&gt; that are not included in this list. I will show an example using &lt;span style="font-style: italic;"&gt;group_concat&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;group_concat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;If you recall from &lt;a href="http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-1-on-duplicate-key.html"&gt;my post about on duplicate key update&lt;/a&gt;, we had a &lt;span style="font-weight: bold;"&gt;Post&lt;/span&gt; model that included a virtual attribute called &lt;span style="font-style: italic;"&gt;tag_list:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Post&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   ...&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.tags.map(&amp;amp;&lt;span class="sy"&gt;:name&lt;/span&gt;).join(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This code retrieves all tags for the post, creates Tag objects, maps that to an array of strings, and joins them together. While this code looks nifty, it is performing a hefty amount of work for the task.&lt;br /&gt;&lt;br /&gt;Thankfully, MySql has a &lt;span style="font-style: italic;"&gt;group_concat&lt;/span&gt; function, and Rails exposes a &lt;span style="font-style: italic;"&gt;calculate&lt;/span&gt; method to perform arbitrary aggregate functions by name. Without further ado, here is how we can use group_concat:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="pc"&gt;self&lt;/span&gt;.tags.calculate(&lt;span class="sy"&gt;:group_concat&lt;/span&gt;, &lt;span class="sy"&gt;:name&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The separator defaults to ','. If you want to change it, sneak in a separator command:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.tags.calculate(&lt;span class="sy"&gt;:group_concat&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;name separator ' - '&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;group_concat will be significantly faster than the original method.&lt;br /&gt;  &lt;br /&gt;Another group of underused aggregate functions are &lt;span style="font-style: italic;"&gt;bit_and&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;bit_or&lt;/span&gt;, and &lt;span style="font-style: italic;"&gt;bit_xor&lt;/span&gt;. If you have a many-many association between a model and an enumerated set of 64 or less items, this information can be stored in a single BIGINT field on the model.&lt;br /&gt;&lt;br /&gt;This wraps up my unglamorous MySql series. If I saved one clock cycle on one database server, then I claim success.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-2759932306689635505?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/2759932306689635505/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=2759932306689635505" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/2759932306689635505" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/2759932306689635505" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-3-summing-up-with.html" title="MySql Lovin' Part 3: Summing up with group_concat" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4557482446352591842</id><published>2008-07-12T11:17:00.000-07:00</published><updated>2008-07-27T22:31:17.422-07:00</updated><title type="text">MySql Lovin' Part 2: Add New Data Types To Migrations</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This is a follow up to last week's &lt;a href="http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-1-on-duplicate-key.html"&gt;MySql Lovin Part 1&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You will eventually want to use MySql specific data types. While arbitrary SQL statements can be executed in migrations, an alternative is to extend the MySql adapter to support new types of columns.&lt;br /&gt;&lt;br /&gt;Two examples of columns I use are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;BIGINT. Rails 2.1 attempts to use the :limit option to predict you want a BIGINT, but there seems to be confusion in Rails source about what the &lt;span style="font-weight: bold;"&gt;N&lt;/span&gt; means when you write BIGINT(N). Nevertheless, BIGINT always has the same maximum and minimum limit, and if I want to use one, I like to be explicit.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Primary Key without auto increment. Sometimes I have enums defined in Ruby code, and want to explicitly define their value when inserting into the database, rather than having their id value automatically incremented by the database.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;  &lt;br /&gt;Let's add support for these columns, so that they can be used with &lt;span style="font-style: italic"&gt;create_table&lt;/span&gt;. First, have a look at &lt;a href="http://github.com/rails/rails/tree/v2.1.0/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L194"&gt;native_database_types&lt;/a&gt;, defined in &lt;span style="font-weight: bold"&gt;ActiveRecord::ConnectionAdapters::MysqlAdapter.&lt;/span&gt; Rails provides no facility to register more column types with an adapter, but that does not prevent merging them into the column type hash:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ActiveRecord&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ConnectionAdapters&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;MysqlAdapter&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;AbstractAdapter&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="r"&gt;alias&lt;/span&gt; &lt;span class="sy"&gt;:original_native_database_types&lt;/span&gt; &lt;span class="sy"&gt;:native_database_types&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;       &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;native_database_types&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;         original_native_database_types.update(&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;             &lt;span class="sy"&gt;:big_integer&lt;/span&gt;  =&amp;gt; { &lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;bigint&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:limit&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;21&lt;/span&gt; },&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;             &lt;span class="sy"&gt;:manual_pk&lt;/span&gt;    =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;int(11) NOT NULL PRIMARY KEY&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;With the new types added, they can be used in migrations:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; create_table &lt;span class="sy"&gt;:foo&lt;/span&gt;, &lt;span class="sy"&gt;:id&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   t.column &lt;span class="sy"&gt;:id&lt;/span&gt;, &lt;span class="sy"&gt;:manual_pk&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   t.column &lt;span class="sy"&gt;:bytes&lt;/span&gt;, &lt;span class="sy"&gt;:big_integer&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Shorthand Columns&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;To make these migrations a bit more appealing, extend &lt;span style="font-weight: bold"&gt;ActiveRecord::ConnectionAdapters::TableDefinition&lt;/span&gt; to support shorthanded column names:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ActiveRecord&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ConnectionAdapters&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TableDefinition&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="s"&gt;&lt;span class="dl"&gt;%w(&lt;/span&gt;&lt;span class="k"&gt;big_integer manual_pk&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;.each &lt;span class="r"&gt;do&lt;/span&gt; |column_type|&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;         class_eval &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;lt;&amp;lt;-EOV&lt;/span&gt;&lt;/span&gt;&lt;span class="s"&gt;&lt;span class="k"&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;           def &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;column_type&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;(*args)&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;             options = args.extract_options!&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;             column_names = args&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;             column_names.each { |name| column(name, '&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;column_type&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;', options) }&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;           end&lt;/span&gt;&lt;span class="dl"&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;         EOV&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Refactor the migration:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; create_table &lt;span class="sy"&gt;:foo&lt;/span&gt;, &lt;span class="sy"&gt;:id&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   t.manual_pk &lt;span class="sy"&gt;:id&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   t.big_integer &lt;span class="sy"&gt;:bytes&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Adding Native Support to Rails&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;It would be nifty if Rails allowed you to perform the above in an initializer (very similar to how you register mime_types). For example:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;ConnectionAdapters&lt;/span&gt;::&lt;span class="co"&gt;MysqlAdapter&lt;/span&gt;.register &lt;span class="sy"&gt;:big_integer&lt;/span&gt;, { &lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;bigint&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:limit&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;21&lt;/span&gt; }&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;ConnectionAdapters&lt;/span&gt;::&lt;span class="co"&gt;MysqlAdapter&lt;/span&gt;.register &lt;span class="sy"&gt;:manual_pk&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;int(11) NOT NULL PRIMARY KEY&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I might follow this up with a Rails patch, but need to think about it more. Also, the weather is too nice for me to continue blogging.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4557482446352591842?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4557482446352591842/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4557482446352591842" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4557482446352591842" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4557482446352591842" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-2-adding-new-column.html" title="MySql Lovin' Part 2: Add New Data Types To Migrations" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4388166664159702908</id><published>2008-07-04T15:06:00.000-07:00</published><updated>2008-07-07T09:52:07.659-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mysql" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><title type="text">MySql Lovin' Part 1: On Duplicate Key Update</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;By coding through the ActiveRecord abstraction, we lose close ties to SQL. This is a major selling point for Rails, but it should not be why you are sold on Rails. &lt;br /&gt;&lt;br /&gt;Avoiding vendor specific database features is silly, and doing so does not make you a better Rails developer. Database neutrality has never helped me because I have yet to swap database vendors. If I did need to swap, it would be due to a vendor specific feature. ActiveRecord is great for the majority of operations, but you should not live exclusively within its constraints. And so I begin the &lt;span style="font-style: italic; font-weight: bold;"&gt;MySql Lovin'&lt;/span&gt; series...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Perfect Rails Code&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Start with a simple data model for a blog. A post has &lt;span style="font-style: italic;"&gt;text&lt;/span&gt;, and can have zero or more &lt;span style="font-style: italic;"&gt;tags&lt;/span&gt;. Here is what the migration looks like:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; create_table &lt;span class="sy"&gt;:posts&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   t.text &lt;span class="sy"&gt;:text&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   t.timestamps&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;  &lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt; create_table &lt;span class="sy"&gt;:post_tags&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:post_id&lt;/span&gt;, &lt;span class="sy"&gt;:tag_id&lt;/span&gt;, &lt;span class="sy"&gt;:null&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; create_table &lt;span class="sy"&gt;:tags&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;   t.string &lt;span class="sy"&gt;:name&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;   t.string &lt;span class="sy"&gt;:post_tags_count&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;   t.timestamps&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt; add_index &lt;span class="sy"&gt;:tags&lt;/span&gt;, &lt;span class="sy"&gt;:name&lt;/span&gt;, &lt;span class="sy"&gt;:unique&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The &lt;span style="font-weight: bold"&gt;tags&lt;/span&gt; table also keeps track of the total posts per tag.&lt;br /&gt;&lt;br /&gt;The models for the blog might look like this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Post&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   has_many &lt;span class="sy"&gt;:post_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   has_many &lt;span class="sy"&gt;:tags&lt;/span&gt;, &lt;span class="sy"&gt;:through&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:post_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list=&lt;/span&gt;(tag_string)&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.tag = tag_string.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).map { |tag_name| &lt;span class="co"&gt;Tag&lt;/span&gt;.find_or_create_by_name(tag_name.squish!) }&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.tags.map(&amp;amp;&lt;span class="sy"&gt;:name&lt;/span&gt;).join(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PostTag&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;   belongs_to &lt;span class="sy"&gt;:tag&lt;/span&gt;, &lt;span class="sy"&gt;:counter_cache&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;   belongs_to &lt;span class="sy"&gt;:post&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;18&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;19&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Tag&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Notice that the &lt;span style="font-weight: bold"&gt;Post&lt;/span&gt; model has a virtual &lt;span style="font-style: italic;"&gt;tag_list&lt;/span&gt; attribute. This automatically splits a comma delimited list of tags, creates new tags if necessary, and generates the associations between posts and tags.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Too Many Queries&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Fill out the form:&lt;br /&gt;&lt;br /&gt;&lt;img style="margin:0 10px 10px 0" src="http://matthewhiggins.com/blog/post_form.png" border="0" alt="Form" /&gt;&lt;br /&gt;&lt;br /&gt;Now check the query log to see what queries hit the database:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; SELECT * FROM tags WHERE (tags.name = 'latin') LIMIT 1&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt; INSERT INTO tags (name, post_tags_count, created_at, updated_at) VALUES('latin', NULL, '2008-07-04 22:41:28', '2008-07-04 22:41:28')&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt; SELECT * FROM tags WHERE (tags.name = ' illegible') LIMIT 1&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt; INSERT INTO tags (name, post_tags_count, created_at, updated_at) VALUES(' illegible', NULL, '2008-07-04 22:41:28', '2008-07-04 22:41:28')&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt; SELECT * FROM tags WHERE (tags.name = ' rails') LIMIT 1&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt; INSERT INTO tags (name, post_tags_count, created_at, updated_at) VALUES(' rails', NULL, '2008-07-04 22:41:28', '2008-07-04 22:41:28')&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt; INSERT INTO posts (created_at, updated_at, text) VALUES('2008-07-04 22:41:28', '2008-07-04 22:41:28', 'Lorem ipsum dolor sit amet...')&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt; INSERT INTO post_tags (tag_id, post_id) VALUES(1, 1)&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; SELECT * FROM tags WHERE (tags.id = 1) &lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; UPDATE tags SET post_tags_count = post_tags_count + 1 WHERE (id = 1) &lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt; INSERT INTO post_tags (tag_id, post_id) VALUES(2, 1)&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt; SELECT * FROM tags WHERE (tags.id = 2) &lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt; UPDATE tags SET post_tags_count = post_tags_count + 1 WHERE (id = 2) &lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt; INSERT INTO post_tags (tag_id, post_id) VALUES(3, 1)&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt; SELECT * FROM tags WHERE (tags.id = 3) &lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt; UPDATE tags SET post_tags_count = post_tags_count + 1 WHERE (id = 3)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;16 queries. Gross, let's try to do better.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;On Duplicate Key Update&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;MySql's &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html"&gt;INSERT ... ON DUPLICATE KEY UPDATE&lt;/a&gt; is a perfect solution to reduce the number of queries. It can be used to:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Insert non-existing tags&lt;/li&gt;&lt;li&gt;Update the counter cache for posts per tag.&lt;/li&gt;&lt;li&gt;Remove the need to instantiate a &lt;span style="font-weight: bold;"&gt;Tag&lt;/span&gt; object for each tag.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Let's give it a try:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Post&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   has_many &lt;span class="sy"&gt;:post_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   has_many &lt;span class="sy"&gt;:tags&lt;/span&gt;, &lt;span class="sy"&gt;:through&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:post_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list=&lt;/span&gt;(tag_string)&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.tag_ids = tag_string.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).map { |tag_name| create_or_update_tag(tag_name.squish!) }&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;tag_list&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.tags.map(&amp;amp;&lt;span class="sy"&gt;:name&lt;/span&gt;).join(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;   &lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;   private&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;create_or_update_tag&lt;/span&gt;(name)&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;       statement = &lt;span class="s"&gt;&lt;span class="dl"&gt;%{&lt;/span&gt;&lt;span class="k"&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;         INSERT INTO tags(name, post_tags_count, created_at, updated_at)&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt;         VALUES (&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;connection.quote(name)&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;, utc_timestamp, utc_timestamp, 1)&lt;br /&gt;&lt;span class="no"&gt;18&lt;/span&gt;         ON DUPLICATE KEY UPDATE&lt;br /&gt;&lt;span class="no"&gt;19&lt;/span&gt;           updated_at = utc_timestamp,&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;           post_tags_count = post_tags_count + 1,&lt;br /&gt;&lt;span class="no"&gt;21&lt;/span&gt;           id=LAST_INSERT_ID(id)&lt;br /&gt;&lt;span class="no"&gt;22&lt;/span&gt;       &lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;.squish!&lt;br /&gt;&lt;span class="no"&gt;23&lt;/span&gt;       &lt;br /&gt;&lt;span class="no"&gt;24&lt;/span&gt;       connection.insert_sql(statement)&lt;br /&gt;&lt;span class="no"&gt;25&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;26&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;27&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;28&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PostTag&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;29&lt;/span&gt;   belongs_to &lt;span class="sy"&gt;:tag&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt;   belongs_to &lt;span class="sy"&gt;:post&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;31&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;32&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;33&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Tag&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;34&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;That SQL code sure looks ugly. It probably belongs in the &lt;span style="font-weight: bold"&gt;Tag&lt;/span&gt; class, and can be generated automatically from the model's attributes. What if I made a plugin that added &lt;span style="font-style: italic"&gt;create_or_update&lt;/span&gt; to ActiveRecord::Base?&lt;br /&gt;&lt;br /&gt;More importantly, the number of queries have decreased:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; INSERT INTO tags(name, post_tags_count, created_at, updated_at) VALUES ('latin', utc_timestamp, utc_timestamp, 1) ON DUPLICATE KEY UPDATE updated_at = utc_timestamp, post_tags_count = post_tags_count + 1, id=LAST_INSERT_ID(id)&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt; INSERT INTO tags(name, post_tags_count, created_at, updated_at) VALUES ('illegible', utc_timestamp, utc_timestamp, 1) ON DUPLICATE KEY UPDATE updated_at = utc_timestamp, post_tags_count = post_tags_count + 1, id=LAST_INSERT_ID(id)&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt; INSERT INTO tags(name, post_tags_count, created_at, updated_at) VALUES ('rails', utc_timestamp, utc_timestamp, 1) ON DUPLICATE KEY UPDATE updated_at = utc_timestamp, post_tags_count = post_tags_count + 1, id=LAST_INSERT_ID(id)&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt; SELECT * FROM `tags` WHERE (`tags`.`id` IN (1,2,3)&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt; INSERT INTO `posts` (`created_at`, `updated_at`, `text`) VALUES('2008-07-04 23:17:20', '2008-07-04 23:17:20', 'Lorem ipsum dolor sit amet...')&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt; INSERT INTO `post_tags` (`tag_id`, `post_id`) VALUES(1, 1)&lt;br /&gt;&lt;span class="no"&gt;7&lt;/span&gt; INSERT INTO `post_tags` (`tag_id`, `post_id`) VALUES(2, 1)&lt;br /&gt;&lt;span class="no"&gt;8&lt;/span&gt; INSERT INTO `post_tags` (`tag_id`, `post_id`) VALUES(3, 1)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This is a good start. The number of queries has decreased by a 2x factor, and Tag objects are no longer being created.&lt;br /&gt;&lt;br /&gt;By alas, what is that query on &lt;span style="font-style: italic"&gt;line 4&lt;/span&gt; for? It looks like Rails runs this query after assigning to &lt;span style="font-style: italic"&gt;tag_ids&lt;/span&gt;. This can be avoided by directly creating the entries for &lt;span style="font-style: italic"&gt;post_tags&lt;/span&gt;, but I can accept this annoyance for today.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4388166664159702908?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4388166664159702908/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4388166664159702908" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4388166664159702908" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4388166664159702908" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-1-on-duplicate-key.html" title="MySql Lovin' Part 1: On Duplicate Key Update" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-3714169296865695539</id><published>2008-06-27T17:04:00.000-07:00</published><updated>2008-07-06T12:54:57.188-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="blocks" /><category scheme="http://www.blogger.com/atom/ns#" term="action_view" /><category scheme="http://www.blogger.com/atom/ns#" term="refactor" /><title type="text">Refactoring block helpers with more block helpers</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This post shows how to build helpers around &lt;span style="font-style: italic;"&gt;create_table&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;form_for&lt;/span&gt;, which are my two most commonly used block helpers in Rails. This stuff is obvious to a Rubyist, but not everyone is a Rubyist.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;create_table&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;If you reuse the same columns between tables, your migrations can end up containing a lot of duplicate code. For example, let's say you are making a content management system for your office supply company. Since each product is sufficiently different, you want to make a different &lt;span style="font-style: italic;"&gt;ActiveRecord&lt;/span&gt; model for each product.&lt;br /&gt;&lt;br /&gt;As a result, your migration to add products will look like this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; create_table &lt;span class="sy"&gt;:tapes&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   t.string &lt;span class="sy"&gt;:product_name&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:category&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:length&lt;/span&gt;, &lt;span class="sy"&gt;:width&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;   t.float &lt;span class="sy"&gt;:price&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:total_sold&lt;/span&gt;, &lt;span class="sy"&gt;:total_in_stock&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;   t.timestamps&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; create_table &lt;span class="sy"&gt;:staplers&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;   t.string &lt;span class="sy"&gt;:product_name&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:capacity&lt;/span&gt;, &lt;span class="sy"&gt;:type&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;   t.string &lt;span class="sy"&gt;:color&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;   t.float &lt;span class="sy"&gt;:price&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:total_sold&lt;/span&gt;, &lt;span class="sy"&gt;:total_in_stock&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;   t.timestamps&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;A polymorphic model can be used in this scenario. But what if you have 15 very unique products, and each needs their own indexing strategy? Nonetheless, that is not the point. The problem here is that duplicate columns exist between each table, and this can be refactored.&lt;br /&gt;&lt;br /&gt;Simply create a new helper method &lt;span style="font-style: italic;"&gt;create_product_table&lt;/span&gt; that wraps &lt;span style="font-style: italic;"&gt;create_table&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.create_product_table(*args)&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   create_table(*args) &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     t.string &lt;span class="sy"&gt;:product_name&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;     t.float &lt;span class="sy"&gt;:price&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;     t.integer &lt;span class="sy"&gt;:total_sold&lt;/span&gt;, &lt;span class="sy"&gt;:total_in_stock&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;     t.timestamps&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="r"&gt;yield&lt;/span&gt;(t) &lt;span class="r"&gt;if&lt;/span&gt; block_given?&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Then use the helper to refactor your existing migration. It removes the duplication, and makes the code more readable:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; create_product_table &lt;span class="sy"&gt;:tapes&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:category&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:length&lt;/span&gt;, &lt;span class="sy"&gt;:width&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt; &lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt; create_product_table &lt;span class="sy"&gt;:staplers&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;   t.integer &lt;span class="sy"&gt;:capacity&lt;/span&gt;, &lt;span class="sy"&gt;:type&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;   t.string &lt;span class="sy"&gt;:color&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;form_for&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The solution for &lt;span style="font-style: italic;"&gt;form_for&lt;/span&gt; is very similar to &lt;span style="font-style: italic;"&gt;create_table&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;In this arbitrary but typical example, your awesome web 2.0 website supports tagging of just about anything. Comments, Posts, Images, Movies, all taggable. That means all your forms need a "Tags:" input box.&lt;br /&gt;&lt;br /&gt;Rather than duplicating the same code in each form for the tagging input, make a &lt;span style="font-style: italic;"&gt;taggable_form_for&lt;/span&gt; helper:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;taggable_form_for&lt;/span&gt;(*args)&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   form_for(*args) &lt;span class="r"&gt;do&lt;/span&gt; |f|&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;     &lt;span class="r"&gt;yield&lt;/span&gt;(f)&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;     concat(f.label &lt;span class="sy"&gt;:tags&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt;     concat(f.text_field &lt;span class="sy"&gt;:tags&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;7&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;The forms are a bit tricky because you must call &lt;span style="font-style: italic;"&gt;concat&lt;/span&gt; so that the html is emitted in the correct location.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-3714169296865695539?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/3714169296865695539/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=3714169296865695539" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3714169296865695539" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/3714169296865695539" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/refactoring-block-helpers-with-more.html" title="Refactoring block helpers with more block helpers" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4565300314384793705</id><published>2008-06-27T02:17:00.001-07:00</published><updated>2008-06-27T02:31:16.412-07:00</updated><title type="text">Broken 'pre' tags with Blogger</title><content type="html">&lt;span style="font-family: verdana;font-size:85%;" &gt;&lt;br /&gt;Sometime in the last week, a significant change was introduced into Blogger. Previously, any content inside of a &amp;lt;pre&gt; section was left alone. Now, a &amp;lt;br/&amp;gt; tag is insterted for every line break within a &amp;lt;pre&gt; tag, which results in a double line break. As a result, all of my code snippets have an extra line break.&lt;br /&gt;&lt;br /&gt;I will write a more relevant post tomorrow. I just wanted to explain why my existing snippets look wrong.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4565300314384793705?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4565300314384793705/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4565300314384793705" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4565300314384793705" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4565300314384793705" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/broken-pre-tags-with-blogger.html" title="Broken 'pre' tags with Blogger" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-4913035150240439953</id><published>2008-06-24T08:57:00.000-07:00</published><updated>2008-06-26T21:24:18.844-07:00</updated><title type="text">Googtaculous</title><content type="html">&lt;div style="font-family:verdana;"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;To follow up on my &lt;a href="http://www.strictlyuntyped.com/2008/06/using-google-ajax-libraries-api-with.html"&gt;post about Google's Ajax library API&lt;/a&gt;, I made a plugin that you can install to make your life easier.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt; &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; script/plugin install git://github.com/matthuhiggins/googtaculous.git&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;The name is pretty clever, eh?&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-4913035150240439953?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/4913035150240439953/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=4913035150240439953" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4913035150240439953" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/4913035150240439953" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/googtaculous.html" title="Googtaculous" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-6601085231558454132</id><published>2008-06-20T14:16:00.000-07:00</published><updated>2008-06-26T21:25:33.988-07:00</updated><title type="text">Clearing memcache without restart</title><content type="html">&lt;div style="font-family:verdana;"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;Up until now, I always restarted memcache to clear it. However, you can make a rake task to do so:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; namespace &lt;span class="sy"&gt;:cache&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   desc &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Clear memcache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   task &lt;span class="sy"&gt;:clear&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:environment&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;     &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.cache_store.clear&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;Drop this into &lt;span style="font-style: italic;"&gt;/lib/tasks/cache.rake&lt;/span&gt;. Now just run the task:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; rake cache:clear&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;This only works if you have configured &lt;span style="font-style: italic;"&gt;action_controller.cache_store&lt;/span&gt; to be memcache.&lt;br /&gt;&lt;br /&gt;Have a good weekend!&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-6601085231558454132?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/6601085231558454132/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=6601085231558454132" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6601085231558454132" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/6601085231558454132" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/clearing-memcache-without-restart.html" title="Clearing memcache without restart" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-9191672897934632884</id><published>2008-06-16T21:53:00.001-07:00</published><updated>2008-07-06T12:55:46.432-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="plugin" /><category scheme="http://www.blogger.com/atom/ns#" term="action_view" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><title type="text">table_for @appetizers</title><content type="html">&lt;div style="font-family:verdana"&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;My First Plugin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I woke this morning realizing that I suck with git and have never made a public plugin. Finding this unacceptable, I set out today to make those truisms become falsehoods. The result is &lt;a href="http://github.com/matthuhiggins/table_for/tree/master"&gt;&lt;span style="font-style: italic;"&gt;table_for&lt;/span&gt;&lt;/a&gt;, an ActionView plugin hosted on github.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:130%;"&gt;The Distasteful Tables&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;You come up with a brilliant recipe site that focuses on appetizers. The idea seems perfect, but where will you put the appetizers? A table, of course!&lt;br /&gt;&lt;br /&gt;Let's make an AppetizerController to retrieve those tasty morsels:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;AppetizerController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;     &lt;span class="iv"&gt;@appetizers&lt;/span&gt; = &lt;span class="co"&gt;Appetizer&lt;/span&gt;.all&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Then create a table in the view:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="ta"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class="ta"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Price&lt;span class="ta"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Tastiness&lt;span class="ta"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Chef&lt;span class="ta"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Description&lt;span class="ta"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;for&lt;/span&gt; appetizer &lt;span class="r"&gt;in&lt;/span&gt; &lt;span class="iv"&gt;@appetizers&lt;/span&gt; &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt;h appetizer.name &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt;h appetizer.price &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt;h appetizer.tastiness &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt;h appetizer.chef &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;     &lt;span class="ta"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt;h appetizer.description &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;18&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;19&lt;/span&gt; &lt;span class="ta"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Mmm, delicious. But wait, I see some duplication.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Can we get a &lt;span style="font-style: italic;"&gt;table_for&lt;/span&gt; those appetizers?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Let's use table_for to trim down the code. First git it:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; ruby script/plugin install git://github.com/matthuhiggins/table_for.git&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Refactor the table:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; table_for &lt;span class="iv"&gt;@appetizers&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t| &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.name &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.price &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.tastiness &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.chef &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.description &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;7&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;There we go. Much better. A week later, you find that your prices should be labelled with '$', descriptions are way too long, and the tastiness field needs to be right aligned. Not to mention, there are multiple tables on the page requiring different class names. Easy:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; table_for &lt;span class="iv"&gt;@appetizers&lt;/span&gt;, &lt;span class="sy"&gt;:html&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:class&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;appetizers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;} &lt;span class="r"&gt;do&lt;/span&gt; |t| &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.name &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.price, &lt;span class="sy"&gt;:helper_method&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:number_to_currency&lt;/span&gt; &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.tastiness, &lt;span class="sy"&gt;:html&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:class&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;tastiness&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;} &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.chef &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;6&lt;/span&gt;   &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; t.description, &lt;span class="sy"&gt;:helper_method&lt;/span&gt; =&amp;gt; [&lt;span class="sy"&gt;:truncate&lt;/span&gt;, &lt;span class="i"&gt;100&lt;/span&gt;] &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;7&lt;/span&gt; &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Other options exist, which can be found in the RDoc.  I think something similar to &lt;span style="font-style: italic;"&gt;table_for&lt;/span&gt; should go into ActionView, and with a few rounds of feedback, maybe I will suggest a patch.&lt;br /&gt;&lt;br /&gt;Meanwhile, Gregg from &lt;a href="http://railsenvy.com/"&gt;RailsEnvy&lt;/a&gt; asked me to change my &lt;a href="http://www.strictlyuntyped.com/2008/06/using-google-ajax-libraries-api-with.html"&gt;google library post&lt;/a&gt; into a plugin. With my newfound plugin skillz, expect that soon.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-9191672897934632884?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/9191672897934632884/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=9191672897934632884" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/9191672897934632884" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/9191672897934632884" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/tablefor-appetizers.html" title="table_for @appetizers" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-7957512591579180799</id><published>2008-06-14T09:05:00.000-07:00</published><updated>2008-07-06T12:54:04.880-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><title type="text">Rails: Where to put the 'other' files</title><content type="html">&lt;div style="font-family:verdana;"&gt;&lt;br /&gt;  &lt;span style="font-size:130%;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;  &lt;span style="font-size:85%;"&gt;When starting a Rails project, four golden folders are predefined: Models, Views, Controllers, Helpers. Could we possibly need anything more? In my experience, the answer is yes. This leads to the question of, where do these extra files go? Compared to models, views, controllers and helpers, Rails provides little guidance about where to put this other stuff. Fortunately, Rails comes packaged with two folders to put additional Ruby code: &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt; config/initializers&lt;/span&gt;. Each have their unique properties, and they can be used together to neatly package your Ruby code.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The weakling lib folder&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;In &lt;a href="http://pragprog.com/titles/rails3/agile-web-development-with-rails-third-edition"&gt;Agile Web Development on Rails&lt;/a&gt;, the authors describe how to use a naming convention, such that your &lt;span style="font-weight: bold;"&gt;lib &lt;/span&gt;file names match the classes they contain. For example, a class of FooBar must be in the file foo_bar.rb. Classes can even be namespaced. For example, Foo::Bar will be loaded if placed inside of foo/bar.rb.&lt;br /&gt;&lt;br /&gt;Files in &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt; are not loaded when Rails starts. Rails has overridden both Class.const_missing and Module.const_missing to dynamically load  the file based on the class name. In fact, this is exactly how Rails loads your models and controllers.&lt;br /&gt;&lt;br /&gt;While the functionality of the lib folder is nice, it is extremely limited. It prevents writing modules to extend or override the functionality of another class, because they will never get loaded.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The bloated initializers folder&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Rails first loads the framework, then plugins, and finally, it loads the files inside of &lt;span style="font-weight: bold"&gt;config/initializers&lt;/span&gt; (in alphabetic order, no less). Previous to 2.0, naughty developers pasted code at the bottom of environment.rb, and the initializer folder was a welcome convention to help organize this madness.&lt;br /&gt;&lt;br /&gt;I find myself using the initializer folder to configure plugins such as HAML, or loading the action_mailer configuration out of a YML file.&lt;br /&gt;&lt;br /&gt;Unfortunately, I see many code snippets showing how to extend the functionality of Rails simply by creating a new file in initializers, and pasting in the code. In a matter of fact, &lt;a href="http://www.strictlyuntyped.com/2008/06/using-google-ajax-libraries-api-with.html"&gt;my last post&lt;/a&gt; suggested doing this. This is OK in light doses, but after a few months of this practice, you will find yourself with an initializers folder with many files and little structure, not to mention that they get loaded in alphabetic order.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Lib folder, meet initializers&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I think I hurt &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;initializer&lt;/span&gt;'s feelings. I feel bad about this, so maybe it is time to introduce them to each other. A simple example is in order:&lt;br /&gt;&lt;br /&gt;We want to add a new 'user_logger' mixin to ActionController::Base. Any controller can choose to enable 'user_logger', and it will put the current user's name in the log file for each request they make. This won't be the last time we extend ActionController's functionality in this project, so let's do it the Right Way.&lt;br /&gt;&lt;br /&gt;We first write the module. Start by creating a new folder inside of &lt;span style="font-weight: bold;"&gt;/lib&lt;/span&gt; called &lt;span style="font-weight: bold;"&gt;rails_extensions&lt;/span&gt;. Now paste this code inside the file &lt;span style="font-style: italic;"&gt;user_logger.rb:&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ActionControllerExtra&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;UserLogger&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.included(base)&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;       base.extend(&lt;span class="co"&gt;ClassMethods&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ClassMethods&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;       &lt;span class="c"&gt;# Logs that a user requested the current action:&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;       &lt;span class="c"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;       &lt;span class="c"&gt;#   MyController &amp;lt; ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;       &lt;span class="c"&gt;#     user_logger&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;       &lt;span class="c"&gt;#   end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;       &lt;span class="c"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;       &lt;span class="c"&gt;# The same options for ActionController::Filters are available. For example,&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;       &lt;span class="c"&gt;# to only log the user in the create and destroy actions:&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;       &lt;span class="c"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt;       &lt;span class="c"&gt;#   MyController &amp;lt; ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;18&lt;/span&gt;       &lt;span class="c"&gt;#     user_logger :only =&amp;gt; [:create, :destroy]&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;19&lt;/span&gt;       &lt;span class="c"&gt;#   end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;       &lt;span class="c"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;21&lt;/span&gt;       &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;user_logger&lt;/span&gt;(options = {})&lt;br /&gt;&lt;span class="no"&gt;22&lt;/span&gt;         include &lt;span class="co"&gt;InstanceMethods&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;23&lt;/span&gt;         before_filter &lt;span class="sy"&gt;:log_user&lt;/span&gt;, options&lt;br /&gt;&lt;span class="no"&gt;24&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;25&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;26&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;27&lt;/span&gt;     &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;InstanceMethods&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;28&lt;/span&gt;       &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;log_user&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;29&lt;/span&gt;         logger.info(&lt;span class="s"&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;session[&lt;span class="sy"&gt;:username&lt;/span&gt;]&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; requested &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;controller_name&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;/&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;action_name&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;31&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;32&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;33&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;  Next, create the file &lt;span style="font-style: italic;"&gt;rails_extensions.rb&lt;/span&gt; in &lt;span style="font-weight: bold;"&gt;/config/initializers.&lt;/span&gt; Paste this code inside:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;rails_extensions/user_logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;2&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.class_eval &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;4&lt;/span&gt;   include &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;UserLogger&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;5&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;  You may want to create an even deeper directory structure inside of &lt;span style="font-weight: bold;"&gt;rails_extensions&lt;/span&gt;. In my larger projects, I keep a folder for action_controller, active_record, etc. Nonetheless, using &lt;span style="font-weight: bold;"&gt;initializers &lt;/span&gt;and &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt; together can help you avoid a bloated &lt;span style="font-weight: bold;"&gt;initializer&lt;/span&gt; folder and weak set of functionality in the &lt;span style="font-weight: bold;"&gt;lib &lt;/span&gt;folder.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-7957512591579180799?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/7957512591579180799/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=7957512591579180799" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7957512591579180799" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/7957512591579180799" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/rails-where-to-put-other-files.html" title="Rails: Where to put the 'other' files" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1845039873784052989.post-839464279903314302</id><published>2008-06-10T16:33:00.000-07:00</published><updated>2008-06-16T21:41:58.208-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="action_view" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby on rails" /><title type="text">Using Google Ajax Libraries API with Ruby on Rails</title><content type="html">&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;Intro&lt;/span&gt;&lt;br /&gt;The new &lt;a href="http://code.google.com/apis/ajaxlibs/"&gt;Google Ajax Libraries API&lt;/a&gt; can help you offload bandwidth from your site. Their servers are configured with gzip, expires headers and e-tags, which leads to good scores from &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt;. I will show two ways to use Google's new API to offload Ruby on Rails javascript (prototype and scriptaculous). In both solutions, your existing calls to &lt;span style="font-style: italic;"&gt;javascript_include_tag&lt;/span&gt; can be left the same, because all of the magic is handled beneath the covers.&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:130%;"&gt;Implementation #1: Create a module&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This first example is a module that chains the &lt;span style="font-style: italic;"&gt;expand_javascript_sources&lt;/span&gt;&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=";font-family:verdana;font-size:85%;"  &gt; method found in &lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;ActionView::Helpers::AssetTagHelper&lt;/span&gt;. If prototype or a scriptaculous file is loaded via javascript_include_tag, we will replace the normal functionality with a path to the file on Google's servers. Start by creating the file /config/initializers/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;google_asset_tag_helper.rb&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;. Paste in the following:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ActionView&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;Helpers&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;GoogleAssetTagHelper&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="co"&gt;GOOGLE_PATHS&lt;/span&gt; = {&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;         &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;prototype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt;         &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;controls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;  =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/controls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;         &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;dragdrop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;  =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/dragdrop&lt;/span&gt;&lt;span class="dl"&gt;',&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt;         &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;   =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;       }&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt;       &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.included(base)&lt;br /&gt;&lt;span class="no"&gt;12&lt;/span&gt;         base.send &lt;span class="sy"&gt;:alias_method_chain&lt;/span&gt;, &lt;span class="sy"&gt;:expand_javascript_sources&lt;/span&gt;, &lt;span class="sy"&gt;:google&lt;/span&gt; &lt;span class="r"&gt;unless&lt;/span&gt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.consider_all_requests_local&lt;br /&gt;&lt;span class="no"&gt;13&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;14&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;15&lt;/span&gt;       &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;expand_javascript_sources_with_google&lt;/span&gt;(sources)&lt;br /&gt;&lt;span class="no"&gt;16&lt;/span&gt;         google_sources, normal_sources = sources.partition { |source| &lt;span class="co"&gt;GOOGLE_PATHS&lt;/span&gt;.include? source }&lt;br /&gt;&lt;span class="no"&gt;17&lt;/span&gt;         &lt;span class="co"&gt;GOOGLE_PATHS&lt;/span&gt;.values_at(*google_sources) + expand_javascript_sources_without_google(normal_sources)&lt;br /&gt;&lt;span class="no"&gt;18&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;19&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;21&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;22&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;23&lt;/span&gt; &lt;span class="co"&gt;ActionView&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.class_eval &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;24&lt;/span&gt;   include &lt;span class="co"&gt;ActionView&lt;/span&gt;::&lt;span class="co"&gt;Helpers&lt;/span&gt;::&lt;span class="co"&gt;GoogleAssetTagHelper&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;25&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;This defines GoogleAssetTagHelper, which chains &lt;span style="font-style: italic;"&gt;expand_javascript_sources&lt;/span&gt;. The module is included into ActionView::Base. Notice that on line &lt;span style="font-weight: bold;"&gt;12&lt;/span&gt;, the original method is only aliased if &lt;span style="font-style: italic;"&gt;ActionController::Base.consider_all_requests_local&lt;/span&gt; is false. Since this is set to true in development mode, it is a sufficient way to determine whether or not Google AJAX Library should be used. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;If using the remote libraries in development is desirable, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;the condition can be safely removed.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Implementation #2: Assign lambda to ActionController::Base.asset_host&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;An alternative solution uses ActionController::Base.asset_host to determine the root path to the javascript files. In /config/environments/production.rb, you find:&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="c"&gt;# config.action_controller.asset_host = "http://some.asset.host/"&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;As of Rails 2.0.2, asset_host can a string or Proc. Since we want to conditionally change the asset_host based on what file is being include, a Proc is needed:&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; google_paths = {&lt;br /&gt;&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;prototype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="no"&gt; 3&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;controls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;  =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;dragdrop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;  =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="no"&gt; 5&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;   =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 6&lt;/span&gt; }&lt;br /&gt;&lt;span class="no"&gt; 7&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt; 8&lt;/span&gt; config.action_controller.asset_host = &lt;span class="co"&gt;Proc&lt;/span&gt;.new &lt;span class="r"&gt;do&lt;/span&gt; |source, request|&lt;br /&gt;&lt;span class="no"&gt; 9&lt;/span&gt;   google_asset = google_paths.keys.detect { |asset| source.starts_with?(&lt;span class="s"&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="k"&gt;/javascripts/&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;asset&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;/span&gt;) }&lt;br /&gt;&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   google_asset ? google_paths[google_asset] : &lt;span class="s"&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;request.protocol&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;request.host_with_port&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;In this example, the code assumes that you do not already have an asset_host defined. If you do, simply replace the false expression on line &lt;span style="font-weight: bold;"&gt;10&lt;/span&gt; with your original asset_host string.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;(The variable &lt;span style="font-style: italic;"&gt;google_paths &lt;/span&gt;is defined inside of production.rb for brevity. &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;However, I recommend defining it as a constant elsewhere in your own project.&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:130%;"&gt;Using the javascript_include_tag&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;As mentioned in the introduction, your existing &lt;span style="font-style: italic;"&gt;javascript_include_tag&lt;/span&gt; calls remain the same:&lt;br /&gt;&lt;br /&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; javascript_include_tag &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;prototype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;controls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1845039873784052989-839464279903314302?l=www.strictlyuntyped.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.strictlyuntyped.com/feeds/839464279903314302/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1845039873784052989&amp;postID=839464279903314302" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/839464279903314302" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1845039873784052989/posts/default/839464279903314302" /><link rel="alternate" type="text/html" href="http://www.strictlyuntyped.com/2008/06/using-google-ajax-libraries-api-with.html" title="Using Google Ajax Libraries API with Ruby on Rails" /><author><name>Matthew Higgins</name><uri>http://www.blogger.com/profile/11424656227454708097</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="05261720841355274374" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></entry></feed>
