How do you find the namespace/module name programmatically in Ruby on Rails?
Asked Answered
B

11

69

How do I find the name of the namespace or module 'Foo' in the filter below?

class ApplicationController < ActionController::Base
  def get_module_name
    @module_name = ???
  end
end


class Foo::BarController < ApplicationController
  before_filter :get_module_name
end
Bridge answered 25/9, 2008 at 13:34 Comment(0)
R
107

None of these solutions consider a constant with multiple parent modules. For instance:

A::B::C

As of Rails 3.2.x you can simply:

"A::B::C".deconstantize #=> "A::B"

As of Rails 3.1.x you can:

constant_name = "A::B::C"
constant_name.gsub( "::#{constant_name.demodulize}", '' )

This is because #demodulize is the opposite of #deconstantize:

"A::B::C".demodulize #=> "C"

If you really need to do this manually, try this:

constant_name = "A::B::C"
constant_name.split( '::' )[0,constant_name.split( '::' ).length-1]
Ryanryann answered 3/1, 2013 at 19:20 Comment(2)
This question was written before Rails 3, and modules weren't really supported. Updated accepted answer to reflect more current versions!Bridge
demodulize was the key answerTamworth
R
35

For the simple case, You can use :

self.class.parent
Rosewood answered 9/1, 2015 at 9:1 Comment(3)
Is that available only in recent Rubies ? Can't believe there are so many answers with "dark magic" and none referenced this method until 2015Dryer
Can't find it in the doc... I can't even reproduce :) I prefere this answer now : #14779316 : class C;end; C.new.class.superclass -> ObjectRosewood
I think my solution worked because of rails : rubydoc.info/docs/rails/4.1.7/Module#parent-instance_methodRosewood
F
23

For Rails 6.1

self.class.module_parent


Hettomei answer works fine up to Rails 6.0

DEPRECATION WARNING: Module#parent has been renamed to module_parent. parent is deprecated and will be removed in Rails 6.1.

Forerun answered 29/5, 2020 at 22:41 Comment(2)
module_parent has been around since Rails 6.0Roehm
This is the type of clean approach I was looking for. Thanks!Rothberg
B
22

This should do it:

  def get_module_name
    @module_name = self.class.to_s.split("::").first
  end
Bondholder answered 25/9, 2008 at 13:40 Comment(3)
or u can also use self.class.name.demodulize for that purpose.Cruse
no you can't, actually demodulize does return class name without namespacesDocument
As Jason Harrelson pointed out, this does not take into consideration multiple parent modules.Bilateral
B
7

This would work if the controller did have a module name, but would return the controller name if it did not.

class ApplicationController < ActionController::Base
  def get_module_name
    @module_name = self.class.name.split("::").first
  end
end

However, if we change this up a bit to:

class ApplicatioNController < ActionController::Base
  def get_module_name
    my_class_name = self.class.name
    if my_class_name.index("::").nil? then
      @module_name = nil
    else
      @module_name = my_class_name.split("::").first
    end
  end
end

You can determine if the class has a module name or not and return something else other than the class name that you can test for.

Bridge answered 25/9, 2008 at 14:4 Comment(0)
T
3

No one has mentioned using rpartition?

const_name = 'A::B::C'
namespace, _sep, module_name = const_name.rpartition('::')
# or if you just need the namespace
namespace = const_name.rpartition('::').first
Tympany answered 15/12, 2019 at 17:0 Comment(0)
L
2

I know this is an old thread, but I just came across the need to have separate navigation depending on the namespace of the controller. The solution I came up with was this in my application layout:

<%= render "#{controller.class.name[/^(\w*)::\w*$/, 1].try(:downcase)}/nav" %>

Which looks a bit complicated but basically does the following - it takes the controller class name, which would be for example "People" for a non-namespaced controller, and "Admin::Users" for a namespaced one. Using the [] string method with a regular expression that returns anything before two colons, or nil if there's nothing. It then changes that to lower case (the "try" is there in case there is no namespace and nil is returned). This then leaves us with either the namespace or nil. Then it simply renders the partial with or without the namespace, for example no namespace:

app/views/_nav.html.erb

or in the admin namespace:

app/views/admin/_nav.html.erb

Of course these partials have to exist for each namespace otherwise an error occurs. Now the navigation for each namespace will appear for every controller without having to change any controller or view.

Laryngology answered 6/5, 2010 at 8:12 Comment(0)
J
2

my_class.name.underscore.split('/').slice(0..-2)

or

my_class.name.split('::').slice(0..-2)

Jez answered 4/4, 2014 at 16:35 Comment(0)
P
2

With many sub-modules:

module ApplicationHelper
  def namespace
    controller.class.name.gsub(/(::)?\w+Controller$/, '')
  end
end

Example: Foo::Bar::BazController => Foo::Bar

Peerage answered 24/11, 2015 at 11:35 Comment(0)
M
1

I don't think there is a cleaner way, and I've seen this somewhere else

class ApplicationController < ActionController::Base
  def get_module_name
    @module_name = self.class.name.split("::").first
  end
end
Melton answered 25/9, 2008 at 13:42 Comment(0)
U
1

I recommend gsub instead of split. It's more effective that split given that you don't need any other module name.

class ApplicationController < ActionController::Base
  def get_module_name
    @module_name = self.class.to_s.gsub(/::.*/, '')
  end
end
Ulmaceous answered 23/7, 2013 at 2:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.