<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: enhance code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Fri, 25 Jul 2008 02:26:01 GMT</pubDate>
    <description>DZone Snippets: enhance code</description>
    <item>
      <title>Rails: Inner joins by association names</title>
      <link>http://snippets.dzone.com/posts/show/2119</link>
      <description>From http://habtm.com/articles/2006/05/10/inner-joins-by-association-names:&lt;br /&gt;&lt;br /&gt;We can enjoy eager loading with association names.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Member.find(:all, :include=&gt;[:group, :profile])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;But :joins option forces us to write raw SQL like this.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Member.count(:joins=&gt;"INNER JOIN groups ON groups.id = members.group_id ...")&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Why should we do it by hand although those AR classes know their reflections? It&#8217;s too boring and painful, and far from DRY.&lt;br /&gt;expand join query&lt;br /&gt;&lt;br /&gt;So, why don&#8217;t you put this code to &#8216;vendor/plugins/expandjoinquery/init.rb&#8217;?&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class ActiveRecord::Base&lt;br /&gt;  class &lt;&lt; self&lt;br /&gt;    private&lt;br /&gt;    def add_joins!(sql, options, scope = :auto)&lt;br /&gt;      scope = scope(:find) if :auto == scope&lt;br /&gt;      join = (scope &amp;&amp; scope[:joins]) || options[:joins]&lt;br /&gt;      sql &lt;&lt; " #{expand_join_query(join)} " if join&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def expand_join_query(*joins)&lt;br /&gt;      joins.flatten.map{|join|&lt;br /&gt;        case join&lt;br /&gt;        when Symbol&lt;br /&gt;          ref = reflections[join] or&lt;br /&gt;            raise ActiveRecord::ActiveRecordError, "Could not find the source association :#{join} in model #{self}"&lt;br /&gt;          case ref.macro&lt;br /&gt;          when :belongs_to&lt;br /&gt;            "INNER JOIN %s ON %s.%s = %s.%s" % [ref.table_name, ref.table_name, primary_key, table_name, ref.primary_key_name]&lt;br /&gt;          else&lt;br /&gt;            "INNER JOIN %s ON %s.%s = %s.%s" % [ref.table_name, ref.table_name, ref.primary_key_name, table_name, primary_key]&lt;br /&gt;          end&lt;br /&gt;        else&lt;br /&gt;          join.to_s&lt;br /&gt;        end&lt;br /&gt;      }.join(" ")&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Now, we can enjoy :joins option as same as :include one!&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# simply inner join&lt;br /&gt;Member.find(:first, :joins=&gt;:group)&lt;br /&gt;=&gt; SELECT * FROM members INNER JOIN groups ON groups.id = members.group_id LIMIT 1&lt;br /&gt;&lt;br /&gt;# two inner joins&lt;br /&gt;Member.count(:joins=&gt;[:group, :profile])&lt;br /&gt;=&gt; SELECT count(*) AS count_all FROM members&lt;br /&gt;   INNER JOIN groups   ON groups.id = members.group_id&lt;br /&gt;   INNER JOIN profiles ON profiles.member_id = members.id &lt;br /&gt;&lt;br /&gt;# symbol and raw SQL&lt;br /&gt;Member.find(:all, :joins=&gt;[:group, "INNER JOIN addresses USING(address_id)"])&lt;br /&gt;=&gt; SELECT * FROM members&lt;br /&gt;   INNER JOIN groups ON groups.id = members.group_id &lt;br /&gt;   INNER JOIN addresses USING(address_id) &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;restrictions&lt;br /&gt;&lt;br /&gt;This is just an idea and not deeply considered yet. So, there are following restrictions.&lt;br /&gt;&lt;br /&gt;    * cascading is not supported&lt;br /&gt;    * not work with :include option (i think)&lt;br /&gt;&lt;br /&gt;I hope someone will polish this up :)&lt;br /&gt;</description>
      <pubDate>Tue, 30 May 2006 08:58:02 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2119</guid>
      <author>MattScilipoti (Matt Scilipoti)</author>
    </item>
  </channel>
</rss>
