{"id":9829,"date":"2017-03-30T12:25:17","date_gmt":"2017-03-30T12:25:17","guid":{"rendered":"https:\/\/blog.jetbrains.com\/ruby\/?p=7270"},"modified":"2017-03-31T08:24:40","modified_gmt":"2017-03-31T08:24:40","slug":"why-you-should-not-use-a-class-as-a-namespace-in-rails-applications","status":"publish","type":"ruby","link":"https:\/\/blog.jetbrains.com\/ruby\/2017\/03\/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications\/","title":{"rendered":"Why you Should not use a Class as a Namespace in Rails Applications"},"content":{"rendered":"<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignleft\" src=\"https:\/\/pbs.twimg.com\/profile_images\/669251313016770560\/EE3-bufl.jpg\" alt=\"\" width=\"127\" height=\"127\" \/><\/p>\n<p style=\"text-align: left;\"><em>This is a guest post by\u00a0Junichi Ito (<a href=\"https:\/\/twitter.com\/jnchito\" target=\"_blank\" rel=\"noopener\">@jnchito<\/a>).\u00a0<\/em><em>Junichi is a Ruby programmer at <a href=\"https:\/\/www.sonicgarden.jp\/\" target=\"_blank\" rel=\"noopener\">SonicGarden.jp<\/a>, translator of Everyday Rails Testing with RSpec, and\u00a0one of the most popular Ruby writers\/bloggers in Japan<\/em><\/p>\n<h3 class=\"\" style=\"text-align: left;\">TL;DR<\/h3>\n<p>If you use a class as a namespace, it can produce a bug that doesn&#8217;t always show up on the surface. You should different names for your model class and your namespace in Rails applications.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class Staff &lt; ApplicationRecord\r\nend\r\n\r\n# Shoud not use class as namespace\r\nclass Staff::ItemsController &lt; ApplicationController\r\nend\r\n\r\n# Should use a different name for namespace\r\nclass Staffs::ItemsController &lt; ApplicationController\r\nend<\/pre>\n<p><!--more--><\/p>\n<h3>Hidden bug with a class name as a namespace<\/h3>\n<p>You can use both classes and modules as a namespace. But if you use a class, it might lead to unexpected behavior, especially in Rails applications.<br \/>\nFor example, you have two classes called Item and Staff:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class Item &lt; ApplicationRecord\r\n  scope :published, -&gt; { where(published: true) }\r\nend\r\n\r\nclass Staff &lt; ApplicationRecord\r\nend<\/pre>\n<p>You want to create two pages, one is for end users and the other is for staffs. End users can see published items only, but staffs can see all items. Then, you create these two controllers:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class ItemsController &lt; ApplicationController\r\n  def index\r\n    @items = Item.published\r\n  end\r\nend\r\n\r\nclass Staff::ItemsController &lt; ApplicationController\r\n  def index\r\n    @items = Item.all\r\n  end\r\nend<\/pre>\n<p>But <code>Staff::ItemsController<\/code> sometimes works wrong. It might return only published items. Why? This problem is related to Ruby&#8217;s constant lookup rule.<\/p>\n<p>For an explanation, I&#8217;ll use a simpler example. If you run the code below in irb, you can refer to <code>Staff::ItemsController<\/code> without definition:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class Item; end\r\nclass Staff; end\r\nclass ItemsController; end\r\n# Refer to Staff::ItemsController without definition\r\nStaff::ItemsController\r\n# warning: toplevel constant ItemsController referenced by Staff::ItemsController\r\n#=&gt; ItemsController<\/pre>\n<p>You can see that <code>Staff::ItemsController<\/code> returns <code>ItemsController<\/code>. Here\u2019s why:<\/p>\n<ul>\n<li>Top level constants always belong to an <code>Object<\/code> class, so <code>ItemsController<\/code> is equal to <code>Object::ItemsController<\/code>.<\/li>\n<li>When <code>Staff::ItemsController<\/code> is referred to, Ruby cannot find the constant <code>ItemsController<\/code> in the <code>Staff<\/code> class. Then, Ruby tries to find it from the <code>Staff<\/code> class&#8217;s ancestors.<\/li>\n<li>You can confirm ancestor classes and modules with <code>Staff.ancestors<\/code>. It returns <code>[Staff, Object, Kernel, BasicObject]<\/code> in the sample code above.<\/li>\n<li>Ruby tries to find <code>ItemsController<\/code> in the <code>Object<\/code> class which is next to the <code>Staff<\/code> class in the ancestors array. Does the <code>Object<\/code> class have <code>ItemsController<\/code>? Yes, Ruby could find <code>ItemsController<\/code> successfully, because <code>Object::ItemsController<\/code> is already defined.<\/li>\n<li>But it might be a wrong lookup, so Ruby gives the warning &#8220;toplevel constant ItemsController referenced by Staff::ItemsController.&#8221; In fact, this case does not match our intention.<\/li>\n<\/ul>\n<p>However, when <code>Staff::ItemsController<\/code> is already defined, <code>Staff::ItemsController<\/code> can be referred to not by <code>ItemsController<\/code>, but by <code>Staff::ItemsController<\/code> because Ruby can find <code>ItemsController<\/code> in the <code>Staff<\/code> class:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class Item; end\r\nclass Staff; end\r\nclass ItemsController; end\r\nclass Staff::ItemsController; end\r\n\r\n# Refer Staff::ItemsController after definition\r\nStaff::ItemsController\r\n#=&gt; Staff::ItemsController<\/pre>\n<p>When the application is run, sometimes <code>Staff::ItemsController<\/code> can be referred to after definition and sometimes referred to before definition. The former leads to expected behavior and the latter leads to unexpected behavior. This is the hidden bug.<\/p>\n<p>This problem can also occur in Rails applications. When you run <code>Staff.ancestors<\/code> in the rails console, you can see a lot of classes and modules. But the <code>Object<\/code> class does exist in ancestors. So this means that <code>Staff::ItemsController<\/code> can refer to <code>Object::ItemsController<\/code> when <code>Staff::ItemsController<\/code> is not defined.<\/p>\n<h3>Use different names for your model class and your namespace<\/h3>\n<p>To avoid this problem, you should use different names for your model class and your namespace, for example, <code>Staffs::ItemsController<\/code>.<\/p>\n<p>If <code>Staffs::ItemsController<\/code> is defined in the <code>app\/controllers\/staffs<\/code> directory and the constant <code>Staffs<\/code> is not defined yet, Rails defines the <code>Staffs<\/code> module automatically. (This function is called &#8220;Automatic Modules&#8221;. Please see <a href=\"http:\/\/guides.rubyonrails.org\/autoloading_and_reloading_constants.html#automatic-modules\" target=\"_blank\" rel=\"noopener\">Rails Guides<\/a> for details.)<\/p>\n<p>When the namespace is a module, the constant lookup leads to different results from the class namespace, because the module does not have ancestors other than itself. Let us see the results in irb:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-title=\"\">class Item; end\r\nclass Staff; end\r\nclass ItemsController; end\r\nmodule Staffs; end\r\n\r\n# Confirm Staffs module&#039;s ancestors\r\nStaffs.ancestors\r\n#=&gt; [Staffs]\r\n\r\n# Refer to Staff::ItemsController without definition\r\nStaffs::ItemsController\r\n#=&gt; NameError: uninitialized constant Staffs::ItemsController\r\n#   Did you mean? ItemsController\r\n#       from (irb):6\r\n#       from \/Users\/jit\/.rbenv\/versions\/2.4.0\/bin\/irb:11:in `&lt;main&gt;&#039;<\/pre>\n<p>This time, <code>Staffs::ItemsController<\/code> was considered to be an uninitialized constant. This is because the <code>Staffsmodule<\/code> does not have <code>Object<\/code> in its ancestors. So Ruby won&#8217;t look up <code>Object::ItemsController<\/code> and the constant lookup fails.<\/p>\n<h3>Rails can load Staffs::ItemsController without errors<\/h3>\n<p>In irb <code>Staffs::ItemsController<\/code> is an uninitialized constant and Ruby produces an error, but this scenario is different in Rails applications. Rails implements the const_missing hook and tries to find <code>Staffs::ItemsController<\/code> in <code>autoload_paths<\/code>. If <code>Staffs::ItemsController<\/code> is defined in autoload_paths like <code>app\/controllers\/staffs\/<\/code>, Rails can load <code>Staffs::ItemsController<\/code> without any errors. Please see <a href=\"http:\/\/guides.rubyonrails.org\/autoloading_and_reloading_constants.html\" target=\"_blank\" rel=\"noopener\">Autoloading and Reloading Constants<\/a> in Rails Guides for details.<\/p>\n<h3>NOTE: The problem described in this post might be fixed in the future<\/h3>\n<p>The top-level constant lookup rule in Ruby might be changed in a future version, because the topic &#8220;remove top-level constant lookup&#8221; is being discussed here:<\/p>\n<p><a href=\"https:\/\/bugs.ruby-lang.org\/issues\/11547\" target=\"_blank\" rel=\"noopener\">https:\/\/bugs.ruby-lang.org\/issues\/11547<\/a><\/p>\n<p>Please check it out if you are interested.<\/p>\n<p><em>By <a href=\"https:\/\/twitter.com\/jnchito\" target=\"_blank\" rel=\"noopener\">Junichi Ito<\/a>.<\/em><\/p>\n<p>\u2014<\/p>\n<p>Have a Ruby\/Rails case to share with the community? Let us know in the comments below!<\/p>\n<p>Your RubyMine Team<\/p>\n","protected":false},"author":574,"featured_media":0,"comment_status":"open","ping_status":"open","template":"","categories":[942],"tags":[617],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ruby\/9829"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ruby"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/types\/ruby"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/users\/574"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/comments?post=9829"}],"version-history":[{"count":0,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ruby\/9829\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/media?parent=9829"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/categories?post=9829"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/tags?post=9829"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/cross-post-tag?post=9829"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}