18

I'm dynamically defining a module name from an argument passed on the cli, for example Required::Module::#{ARGV.first}

Is there any way to check if that module exists? Also, how would I run methods on it not knowing it's exact name?

Smar
  • 8,109
  • 3
  • 36
  • 48
Andrei Serdeliuc ॐ
  • 5,828
  • 5
  • 39
  • 66

8 Answers8

37

Use const_defined? for this.

Required::Module.const_defined?(:ModuleName)

returns true or false.

horseyguy
  • 29,455
  • 20
  • 103
  • 145
14
defined?(Required::Module)

gives "constant" if it exists, and nil if it doesn't.

Update: Sorry, didn't read your question properly.

defined?(eval("Required::Module::"+string))

should give you what you're after.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • 1
    But this doesn't seem to work with dynamically named modules, for example `defined?("Parade::Procedure::#{procedure.capitalize}")` returns `"expression"`, even though the module I'm calling doesn't exist. – Andrei Serdeliuc ॐ Aug 19 '10 at 14:16
  • I'd want to avoid using `eval`, especially if `string` is a user entered value. You could cause some serious damage. (read more about it https://stackoverflow.com/questions/637421/is-eval-supposed-to-be-nasty) – Andrew K Oct 15 '18 at 20:19
11

Check for module existence using the const_get method:

begin
    mod = Required::Module::const_get "ModuleName"
    #It exists
rescue NameError
    #Doesn't exist
end
Daniel O'Hara
  • 13,307
  • 3
  • 46
  • 68
  • 10
    I do not believe exception handling is the best approach, it's both slow and bloated (code-wise). Use `const_defined?` instead. – horseyguy Aug 24 '10 at 15:19
  • You should edit the answer to do something like `mod = Required::Module.const_defined?("ModuleName") ? Required::Module::const_get "ModuleName" : nil` – Leo Correa Jun 02 '13 at 00:15
  • 1
    Note that if the module is known statically it's easier to just use `Required::Module::ModuleName` - both will through `NameError`. This is useful if the module name is only known at runtime though. – Aram Kocharyan Aug 18 '13 at 03:56
  • 1
    Do not forget to pass inherited = false as a second argument, it is important. Otherwise you will conflict with ancestors. – puchu Sep 02 '18 at 11:47
2

You have to check if:

  • a constant referring to a module exists,
  • an object that the constant holds reference to is a module.

Try that:

 def module_exists?(name, base = self.class)
   base.const_defined?(name) && base.const_get(name).instance_of?(::Module)
 end

Then in your code:

 module_exists?(ARGV.first, Required::Module)

It will return true if there is a module of the given name within a given namespace base. The difference from the examples given in other answers is that it will return false if the queried name reffers to a class, not to a module.

Include classes in a test

If you want to change that behavior and force the method to also return true for classes (not just modules) change instance_of? to is_a?.

OOP way

You can also code it in a more object-oriented way if your Required::Module module is the only module you're testing for submodules:

 module Required::Module
   def submodule_exists?(name)
     const_defined?(name) && const_get(name).instance_of?(::Module)
   end
 end
 module_function :submodule_exists?

Then in your code:

 Required::Module.submodule_exists?(ARGV.first)
siefca
  • 1,007
  • 13
  • 16
2

If you got ActiveSupport

mod = ("Required::Module::#{ARGV.first}".constantize rescue nil)
ybs
  • 59
  • 3
1

In the case where you require something that extends something else, you can't base your test on a constant because the extension may not define a new one. Instead, base it on the presence of something else, like a new method.

I use the below to test if open_uri_redirections has been required:

if OpenURI.methods.include?(:redirectable_safe?)
  # extension loaded
else
  # extension not loaded
fi
starfry
  • 9,273
  • 7
  • 66
  • 96
1

The currently selected answer is not correct. const_get and const_defined look for any constant name, regardless of the object calling that method. For example, if I wanted to check for MyModule::Rails inside a Rails application, using const_get would return the normal Rails module.

To check for a constant within a specific namespace, use the constants method and check for your class:

MyModule.constants.include?("Rails") # => false

saquino88
  • 71
  • 1
  • 5
  • 1
    Kudos. In the manner of `instance_methods` etc., `const_defined?` includes a second parameter of `true` (default, inheritance is considered) and `false` (only search the receiver). I think I prefer using that even though it's a rather ambiguous boolean property, because there's a good chance that the Ruby interpreter will have a more optimised search than creating a Ruby array via `constants` and asking Ruby to search it with `include?`. – Andrew Hodgkinson Apr 12 '16 at 21:03
0

Get the class if it exists:

dynamic_klass = "Required::Module::#{ARGV.first}".classify.safe_constantize

Call a method on the class if it does:

dynamic_klass.send("some_method") if dynamic_klass.present?
Eric Norcross
  • 4,177
  • 4
  • 28
  • 53