Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested classes versus compact in Ruby

Tags:

Working on an initial Rails project, and using Rubocop to analyze code style. It led me to question exactly how Ruby's nested classes work in the context of Rails. For example, in my engine, I have a model:

# app/models/app_core/tenant.rb
module AppCore
  class Tenant < ActiveRecord::Base
  end
end

and a controller:

# app/controllers/app_core/tenant/members_controller.rb
module AppCore
  class Tenant::MembersController < ApplicationController
  end
end

In the model's case, the module is the same as the path and the class name is the same as the file name. In the controllers case, the second part of the path, "tenant" is part of the class name.

Rubocop tells me that I should "Use nested class definitions instead of compact style" in the Tenant::MembersController line, so if I understand correctly...

module AppCore  
  class Tenant
    class MembersController < ApplicationController
    end
  end
end

...this shouldn't make a difference.

Now, my question is I have AppCore::Tenant as a model, but then AppCore::Tenant looks to be reopened and the MembersController class is added to it as a nested class. Does this mean that my Tenant class will always have that nested class in it? Do I need to name my models and controller routes something differently? Is this totally fine and nothing to worry about? Not exactly sure what this means.

like image 832
typeoneerror Avatar asked Jun 24 '14 17:06

typeoneerror


People also ask

Are nested classes good practice?

Nested Class can be used whenever you want to create more than once instance of the class or whenever you want to make that type more available. Nested Class increases the encapsulations as well as it will lead to more readable and maintainable code.

What is the scope be used for nested class?

The scope of a nested class is bounded by the scope of its enclosing class. Thus in below example, class NestedClass does not exist independently of class OuterClass. A nested class has access to the members, including private members, of the class in which it is nested.


1 Answers

One subtle difference is that your scope is different, and this can cause errors. In the first case constants will be looked up in AppCore, whereas in the second case constants will be looked up in AppCore::Tenant. If you fully qualify constant names then it doesn't make a difference.

Foo = :problem

module A
  Foo = 42

  # looks up A::Foo because of lexical scope
  module B
    def self.foo
      Foo
    end
  end
end

# looks up ::Foo because of lexical scope
module A::C
  def self.foo
    Foo
  end
end

# Looks up A::Foo, fully qualified ... ok technically ::A::Foo is fully qualified, but meh.
module A::D
  def self.foo
    A::Foo
  end
end

A::B.foo # => 42
A::C.foo # => :problem
A::D.foo # => 42

If you are referring to constants defined in AppCore::Tenant from within MembersController then it might make a difference for you. Subtle but possibly important, and good to be aware of. I've hit this in real life when I had a Util module with a String submodule. I moved a method into Util and it broke because String inside that method now referred to Util::String. I changed some naming conventions after that.

Your Tenant module will always have MembersController as a nested class. Anywhere else in your codebase you can refer to AppCore::Tenant::MembersController. If you want better separation then you should name your model classes differently, or put them inside a module such as AppCore::Model or similar. If you're using Rails you'll have to buck some conventions, but the configuration required for that is not too bad.

like image 52
Sami Samhuri Avatar answered Sep 17 '22 15:09

Sami Samhuri