{"id":344,"date":"2009-11-16T20:44:51","date_gmt":"2009-11-16T19:44:51","guid":{"rendered":"http:\/\/www.dtsato.com\/blog\/?p=344"},"modified":"2009-11-16T20:44:51","modified_gmt":"2009-11-16T19:44:51","slug":"merging_named_scopes_without_performing_a_query","status":"publish","type":"post","link":"https:\/\/www.dtsato.com\/blog\/2009\/11\/16\/merging_named_scopes_without_performing_a_query\/","title":{"rendered":"Merging named scopes without performing a query"},"content":{"rendered":"<p>It all started when I <a href=\"http:\/\/twitter.com\/dtsato\/status\/5770647837\">tweeted<\/a>:<\/p>\n<blockquote><p>wish I could get hold of a chain of named_scopes without performing any query (which means it can&#8217;t be dynamically composed) <a title=\"#activerecord\" href=\"http:\/\/twitter.com\/search?q=%23activerecord\">#activerecord<\/a><\/p><\/blockquote>\n<p>My team has currently been performing some refactorings on our Rails app and one of our goals is to move complex find queries into more composable\/meaningful\/maintainable named scopes. Contrary to my expectations, when you call a named scope, Active Record performs a <code>find(:all)<\/code> query implicitly as you finish your statement:<\/p>\n<pre lang=\"ruby\">\r\nclass Shirt < ActiveRecord::Base\r\n  named_scope :red, :conditions => {:color => 'red'}\r\n  named_scope :clean, :conditions => {:clean => true}\r\nend\r\n\r\n>> red_proxy = Shirt.red\r\n  Shirt Load (0.3ms)   SELECT * FROM \"shirts\" WHERE (\"shirts\".\"color\" = 'red')\r\n+----+-------+-------------------------+-------------------------+-------+\r\n| id | color | created_at              | updated_at              | clean |\r\n+----+-------+-------------------------+-------------------------+-------+\r\n| 5  | red   | 2009-11-16 18:36:37 UTC | 2009-11-16 18:36:37 UTC | false |\r\n| 6  | red   | 2009-11-16 18:36:37 UTC | 2009-11-16 19:15:18 UTC | true  |\r\n+----+-------+-------------------------+-------------------------+-------+\r\n2 rows in set\r\n>> red_proxy.clean\r\n  Shirt Load (0.4ms)   SELECT * FROM \"shirts\" WHERE ((\"shirts\".\"clean\" = 't' AND \"shirts\".\"color\" = 'red'))\r\n+----+-------+-------------------------+-------------------------+-------+\r\n| id | color | created_at              | updated_at              | clean |\r\n+----+-------+-------------------------+-------------------------+-------+\r\n| 6  | red   | 2009-11-16 18:36:37 UTC | 2009-11-16 19:15:18 UTC | true  |\r\n+----+-------+-------------------------+-------------------------+-------+\r\n1 row in set\r\n>>\r\n<\/pre>\n<p>So, even though I can compose named scopes, I can&#8217;t defer the query to the point I define which operation to execute. I&#8217;m obviously not the first one who had this problem, but it seems like this <a href=\"https:\/\/rails.lighthouseapp.com\/projects\/8994\/tickets\/1331-allow-arbitrary-named_scope-instances-to-be-merged\">won&#8217;t be fixed until a new solution is proposed in Rails 3<\/a>.<\/p>\n<p>In our particular case, we didn&#8217;t even need the proxy object, but just a hash representation of the composed query parameters. We&#8217;re using ActiveRecord::Extensions (ar-extensions) to perform some UNION queries to aggregate results, and it expects a list of hashes that represent each query. It would be nice if I could grab the hash from my named scopes without performing the query&#8230;<\/p>\n<p>The documentation points to a <code>proxy_options<\/code> method that you can use to test your named scope, but it didn&#8217;t solve my problem, since it only returns the hash for the last scope in your chain:<\/p>\n<pre lang=\"ruby\">\r\n>> Shirt.red.proxy_options\r\n=> {:conditions=>{:color=>\"red\"}}\r\n>> Shirt.red.clean.proxy_options\r\n=> {:conditions=>{:clean=>true}}\r\n<\/pre>\n<p>The solution, after digging a while through the source code, was to use an internal method that&#8217;s used during the merge algorithm, called <code>current_scoped_methods<\/code>:<\/p>\n<pre lang=\"ruby\">\r\n>> Shirt.red.clean.current_scoped_methods[:find]\r\n=> {:conditions=>\"(\"shirts\".\"clean\" = 't' AND \"shirts\".\"color\" = 'red')\"}\r\n<\/pre>\n<p>This is not a perfect solution, but saved me the trouble of performing the merge myself. I hope the Rails 3 solution addresses these issues and that this post saves me some time digging through ActiveRecord source code if I need this before then :)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It all started when I tweeted: wish I could get hold of a chain of named_scopes without performing any query (which means it can&#8217;t be dynamically composed) #activerecord My team has currently been performing some refactorings on our Rails app and one of our goals is to move complex find queries into more composable\/meaningful\/maintainable named [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[143,144,61,67],"class_list":["post-344","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-activerecord","tag-named_scope","tag-rails","tag-ruby"],"_links":{"self":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts\/344","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/comments?post=344"}],"version-history":[{"count":6,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts\/344\/revisions"}],"predecessor-version":[{"id":351,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts\/344\/revisions\/351"}],"wp:attachment":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/media?parent=344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/categories?post=344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/tags?post=344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}