<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Hobo Blog &#187; Ruby Skills</title>
	<atom:link href="http://hobocentral.net/blog/category/ruby-skills/feed/" rel="self" type="application/rss+xml" />
	<link>http://hobocentral.net/blog</link>
	<description>Hobo - the web app builder for Rails</description>
	<lastBuildDate>Tue, 24 Apr 2012 15:48:31 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>ActiveRecord behaviour with associations</title>
		<link>http://hobocentral.net/blog/2008/04/29/activerecord-behaviour-with-associations/</link>
		<comments>http://hobocentral.net/blog/2008/04/29/activerecord-behaviour-with-associations/#comments</comments>
		<pubDate>Tue, 29 Apr 2008 13:36:24 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Ruby Skills]]></category>

		<guid isPermaLink="false">http://hobocentral.net/blog/2008/04/29/activerecord-behaviour-with-associations/</guid>
		<description><![CDATA[The interaction between ActiveRecord and the database is very simple when working with a single record &#8211; it&#8217;s always pretty clear when the database is going to be changed. What about when you&#8217;re working with multiple records and associations? I did some experiments way back at the start of the Hobo project, but recently I [...]]]></description>
			<content:encoded><![CDATA[<p>The interaction between ActiveRecord and the database is very simple when working with a single record &#8211; it&#8217;s always pretty clear when the database is going to be changed. What about when you&#8217;re working with multiple records and associations? I did some experiments way back at the start of the Hobo project, but recently I wanted to check if anything had changed.</p>

<p>So I threw together some simple experiments, and turned on logging in the console. It&#8217;s a bit rough and certainly not exhaustive, but I formatted it in markdown out of habit and then though hey, I should post this, so here it is.</p>

<p>Is this stuff documented somewhere? I never found it if it is. I wonder if most Rails devs know about all this already.</p>

<p>This is all in Rail 2.0.2 BTW.</p>

<p><span id="more-201"></span></p>

<h2>Some simple models</h2>

<pre><code>class Post &lt; ActiveRecord::Base
  has_many :comments
  has_many :categorisations
  has_many :categories, :through =&gt; :categorisations
end

class Comment &lt; ActiveRecord::Base
  belongs_to :post
end

class Category &lt; ActiveRecord::Base
  has_many :categorisations
end

class Categorisation &lt; ActiveRecord::Base
  belongs_to :post
  belongs_to :category
end
</code></pre>

<h2><code>has_many</code> (not through)</h2>

<h3>Assigning to the array on a new record</h3>

<p>New comments are created along with a new post:</p>

<pre><code>&gt;&gt; p = Post.new
=&gt; #&lt;Post id: nil&gt;
&gt;&gt; p.comments = [Comment.new]
=&gt; [#&lt;Comment id: nil, post_id: nil&gt;]
&gt;&gt; p.save
  Post Create (0.000601)   INSERT INTO posts VALUES(NULL)
  Comment Create (0.000195)   INSERT INTO comments ("post_id") VALUES(1)
=&gt; true
</code></pre>

<h3>Appending to the array</h3>

<p>For a post that exists, the appended comments are created immediately:</p>

<pre><code>&gt;&gt; p
=&gt; #&lt;Post id: 1&gt;
&gt;&gt; p.comments &lt;&lt; Comment.new
  Comment Create (0.000481)   INSERT INTO comments ("post_id") VALUES(1)
=&gt; [#&lt;Comment id: 1, post_id: 1&gt;, #&lt;Comment id: 2, post_id: 1&gt;]
</code></pre>

<h3>Assigning to the array on an existing record</h3>

<p>Comments no longer in the array have their foreign_key set to NULL. (I&#8217;d guess this changes if you declare <code>:dependent =&gt; :destroy</code>, but I didn&#8217;t try it)</p>

<pre><code>&gt;&gt; p.comments
=&gt; [#&lt;Comment id: 1, post_id: 1&gt;, #&lt;Comment id: 2, post_id: 1&gt;]
&gt;&gt; p.comments = []
  Comment Update (0.001335)   UPDATE comments SET post_id = NULL WHERE (post_id = 1 AND id IN (1,2))
=&gt; []
</code></pre>

<p>New comments in the array are created immediately:</p>

<pre><code>&gt;&gt; p.comments = [Comment.new]
  Comment Create (0.000504)   INSERT INTO comments ("post_id") VALUES(1)
=&gt; [#&lt;Comment id: 3, post_id: 1&gt;]
</code></pre>

<p>Existing comments have their foreign key set</p>

<pre><code>&gt;&gt; p2 = Post.create
  Post Create (0.000820)   INSERT INTO posts VALUES(NULL)
=&gt; #&lt;Post id: 2&gt;
&gt;&gt; c = p.comments.first
=&gt; #&lt;Comment id: 3, post_id: 1&gt;
&gt;&gt; p2.comments = [c]
  Comment Load (0.000292)   SELECT * FROM comments WHERE (comments.post_id = 2) 
  Comment Update (0.000684)   UPDATE comments SET "post_id" = 2 WHERE "id" = 3
=&gt; [#&lt;Comment id: 3, post_id: 2&gt;]
</code></pre>

<h2><code>belongs_to</code></h2>

<p>When assigning <code>c.post</code> on an existing comment, the change is saved when the comment is saved:</p>

<pre><code>&gt;&gt; c.post == p2
=&gt; true
&gt;&gt; c.post = p
=&gt; #&lt;Post id: 1&gt;
&gt;&gt; c.save
  Comment Update (0.000778)   UPDATE comments SET "post_id" = 1 WHERE "id" = 3
=&gt; true
</code></pre>

<p>When assigning a <code>c.post</code> to a new post, the post is created when the comment is saved:</p>

<pre><code>&gt;&gt; c
=&gt; #&lt;Comment id: 3, post_id: 1&gt;
&gt;&gt; c.post = Post.new
=&gt; #&lt;Post id: nil&gt;
&gt;&gt; c.save
  Post Create (0.000464)   INSERT INTO posts VALUES(NULL)
  Comment Update (0.000148)   UPDATE comments SET "post_id" = 3 WHERE "id" = 3
=&gt; true
</code></pre>

<p>This happens the same way when the comment is new &#8212; both are created together:</p>

<pre><code>&gt;&gt; c = Comment.new
=&gt; #&lt;Comment id: nil, post_id: nil&gt;
&gt;&gt; c.post = Post.new
=&gt; #&lt;Post id: nil&gt;
&gt;&gt; c.save
  Post Create (0.000499)   INSERT INTO posts VALUES(NULL)
  Comment Create (0.000161)   INSERT INTO comments ("post_id") VALUES(4)
=&gt; true
</code></pre>

<h2><code>has_many :through</code></h2>

<h3>Assigning to the array has no effect:</h3>

<p>Assignment to <code>p.categories</code> where <code>p</code> is an existing post:</p>

<pre><code>&gt;&gt; p
=&gt; #&lt;Post id: 1&gt;
&gt;&gt; cat = Category.create
  Category Create (0.000427)   INSERT INTO categories VALUES(NULL)
=&gt; #&lt;Category id: 1&gt;
&gt;&gt; p.categories = [cat]
  Category Load (0.000289)   SELECT categories.* FROM categories INNER JOIN categorisations ON categories.id = categorisations.category_id WHERE ((categorisations.post_id = 1)) 
=&gt; [#&lt;Category id: 1&gt;]
&gt;&gt; p.save
=&gt; true
</code></pre>

<p>Note there were no changes to the categories table.</p>

<p>Assignment to <code>p.categories</code> where <code>p</code> is a new post:</p>

<pre><code>&gt;&gt; p = Post.new
=&gt; #&lt;Post id: nil&gt;
&gt;&gt; p.categories = [cat]
=&gt; [#&lt;Category id: 1&gt;]
&gt;&gt; p.save
  Post Create (0.000513)   INSERT INTO posts VALUES(NULL)
=&gt; true
</code></pre>

<p>Again, nothing happens to the categories table</p>

<h3>Appending to the array does have an effect</h3>

<p>Can&#8217;t append to a has-many-through on a new record:</p>

<pre><code>&gt;&gt; p = Post.new
=&gt; #&lt;Post id: nil&gt;
&gt;&gt; p.categories &lt;&lt; cat
ActiveRecord::HasManyThroughCantAssociateNewRecords: Cannot associate new records through 'Post#categorisations' on '#'. Both records must have an id in order to create the has_many :through record associating them.
</code></pre>

<p>Can append to a has-many-through on an existing record. The joining record is created immediately:</p>

<pre><code>&gt;&gt; p = Post.find(:first)
  Post Load (0.000365)   SELECT * FROM posts LIMIT 1
=&gt; #&lt;Post id: 1&gt;
&gt;&gt; p.categories
  Category Load (0.000294)   SELECT categories.* FROM categories INNER JOIN categorisations ON categories.id = categorisations.category_id WHERE ((categorisations.post_id = 1)) 
=&gt; []
&gt;&gt; p.categories &lt;&lt; cat
  Categorisation Create (0.000479)   INSERT INTO categorisations ("post_id", "category_id") VALUES(1, 1)
=&gt; [#&lt;Category id: 1&gt;]
</code></pre>

<p>But this is not allowed if the category is new:</p>

<pre><code>&gt;&gt; p.categories &lt;&lt; Category.new
ActiveRecord::HasManyThroughCantAssociateNewRecords: Cannot associate new records through 'Post#categorisations' on '#'. Both records must have an id in order to create the has_many :through record associating them.
</code></pre>

<p>Did you learn something?</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2008/04/29/activerecord-behaviour-with-associations/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>[Ruby Skills] * and where</title>
		<link>http://hobocentral.net/blog/2008/04/09/ruby-skills-and-where/</link>
		<comments>http://hobocentral.net/blog/2008/04/09/ruby-skills-and-where/#comments</comments>
		<pubDate>Wed, 09 Apr 2008 14:00:16 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Ruby Skills]]></category>

		<guid isPermaLink="false">http://hobocentral.net/blog/2008/04/09/ruby-skills-and-where/</guid>
		<description><![CDATA[This is the first post in a new category I&#8217;ve added to the blog: &#8220;Ruby Skills&#8221;. It&#8217;s a place for me to share Ruby tricks and tips I&#8217;ve picked up along the way. Sometimes, as with this post, I&#8217;ll post about the Ruby extensions in HoboSupport. Now that HoboSupport is available as a gem, you [...]]]></description>
			<content:encoded><![CDATA[<p>This is the first post in a new category I&#8217;ve added to the blog: &#8220;Ruby Skills&#8221;. It&#8217;s a place for me to share Ruby tricks and tips I&#8217;ve picked up along the way. Sometimes, as with this post, I&#8217;ll post about the Ruby extensions in HoboSupport. Now that HoboSupport is available as a gem, you can easily use these tricks in any Ruby project.</p>

<p><span id="more-198"></span></p>

<p>First up, two new Enumerable methods that HoboSupport adds: <code>*</code> and <code>where</code>. Attentive readers might be thinking &#8212; hang on, Array already defined <code>*</code>. Don&#8217;t worry, it still works.</p>

<p><code>*</code> is some syntactic sugar for <code>map</code>. The idea is that we use &#8216;dot&#8217; to call a method on <em>one</em> object, and we use &#8216;dot star&#8217; to call a method on a <em>whole collection</em> of objects, returning all the results in a new array.</p>

<p>Say <code>users</code> is an array of user objects, and we want all the names:</p>

<pre><code>users.*.name
</code></pre>

<p>Nice eh? You can pass arguments too:</p>

<pre><code>users.*.to_json(:only =&gt; [:first_name, :surname])
</code></pre>

<p>Note that you can&#8217;t do</p>

<pre><code>users.*.name.upcase
</code></pre>

<p>That would try to upcase the array. You&#8217;d have to do:</p>

<pre><code>users.*.name.*.upcase # Not very efficient though
</code></pre>

<p>Of course, as a good functional programmer, I wouldn&#8217;t dream of giving map some love while neglecting filter (better known in Ruby-land as <code>find_all</code> or <code>select</code>). So you can also do:</p>

<pre><code>users.where.active? # same as users.find_all {|u| u.active? }
</code></pre>

<p>There&#8217;s also <code>where_not</code></p>

<p>Given that the result is just an array, we can chain them. Want the names of all the inactive users?</p>

<pre><code>users.where_not.active?.*.name
</code></pre>

<p>Very handy in the console.</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2008/04/09/ruby-skills-and-where/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

