Check if module exists in ruby - ruby

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?

Use const_defined? for this.
Required::Module.const_defined?(:ModuleName)
returns true or false.

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.

Check for module existence using the const_get method:
begin
mod = Required::Module::const_get "ModuleName"
#It exists
rescue NameError
#Doesn't exist
end

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)

If you got ActiveSupport
mod = ("Required::Module::#{ARGV.first}".constantize rescue nil)

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

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

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?

Related

Is it possible to execute a method from a module with a String ? - Ruby 2.7.1

Greetings to everyone.
This question is the continuation of a previous one :
Is it possible to extend a class by using a string as a module ? - Ruby 2.7.1
So here it is. I am currently doing some tests with Ruby 2.7.1 on my FreeBSD 12.1 workstation. My objective is to find a way to load all the script within a directory. These scripts are modules with predictable names. For instance, if I got a script named mymodule.rb, it will contain a module named : Mymodule and a method : mymodule. So I can make a list of all scripts within a directory by using an Array. I can use that list to load/require all my script files easily. And with the help of some .sub, .chop or .capitalize, I can can extract what I need from each index of my array. But the result of this operation is always a String. The problem is that I cannot execute a method with a String. Previously I was having problem with extending my main class with module name from a String, but answers were given and solved this little issue. Here is my main class :
load "mymodule.rb"
class Myclass
def mymethod
var1 = "Mymodule"
extend self.class.const_get(var1)
var2 = "mymodule"
#I need something here to call the method from the module.
#puts #varmod
end
end
a = Myclass.new
a.mymethod
and here is my module :
module Mymodule
def mymodule
#varmod = "TEST"
end
end
So, I would like to know if there is a way to execute the method within Mymodule the same fashion we did with "extend self.class.const_get(var1)".
Thanks in advance for your responses !
In order to send a message with a name that is not statically known at design time, you can use the Object#public_send method:
public_send(var2)
It is not necessary to use Object#send in this case, since your methods are not private.
I think it is the send method your are looking for. The following should work:
send(var2)

In a Ruby module, how do you test if a method exists in the context which use the module?

Some context
I'm playing with Ruby to deepen my knowledge and have fun while at the same time improving my knowledge of Esperanto with a just starting toy project called Ĝue. Basically, the aim is to use Ruby facilities to implement a DSL that matches Esperanto traits that I think interesting in the context of a programming language.
The actual problem
So a first trait I would like to implement is inflection of verbs, using infinitive in method declaration (ending with -i), and jussive (ending with -u) for call to the method.
A first working basic implementation is like that:
module Ĝue
def method_missing(igo, *args, &block)
case igo
when /u$/
celo = igo.to_s.sub(/u$/, 'i').to_s
send(celo)
else
super
end
end
end
And it works. Now the next step is to make it more resilient, because there is no guaranty that celo will exists when the module try to call it. That is, the module should implement the respond_to? method. Thus the question, how do the module know if the context where module was required include the corresponding infinitive method? Even after adding extend self at the beginning of the module, inside of the module methods.include? :testi still return false when tested with the following code, although the testu call works perfectly:
#!/usr/bin/env ruby
require './teke/ĝue.rb'
include Ĝue
def testi; puts 'testo!' ;end
testu
Note that the test is run directly into the main scope. I don't know if this makes any difference with using a dedicated class scope, I would guess that no, as to the best of my knowledge everything is an object in Ruby.
Found a working solution through In Ruby, how do I check if method "foo=()" is defined?
So in this case, this would be checkable through
eval("defined? #{celo}") == 'method'

How to check whether a variable is an instance of a module's subclass using rspec?

I have a class structure that looks like this:
module MyModule
class MyOuterClass
class MyInnerClass
end
end
end
I'm trying to make sure that a variable was correctly instantiated as a MyInnerClass using Rspec. printing the type of the class, it was MyModule::MyOuterClass::MyInnerClass. However, if I try to run the line
expect{#instance_of_MyInnerClass}.to be_an_instance_of(MyModule::MyOuterClass::MyInnerClass)
I get the error "You must pass an argument rather than a block to use the provided matcher." Additionally, the classes are in another location, so I can't just check
[...] be_an_instance_of(MyInnerClass)
Rspec complains that MyInnerClass is an uninitialized constant. So, I would like to ask how to verify that a variable is an instance of MyInnerClass using RSpec.
Don't Pass a Block
Rspec 3.x uses an expect method rather than a block syntax (see RSpec 3 Expectations 3.0). To get your spec to pass, and clean it up, you can use the following:
module MyModule
class MyOuterClass
class MyInnerClass
end
end
end
describe MyModule::MyOuterClass::MyInnerClass do
it "is correctly instantiated" do
expect(subject).to be_an_instance_of MyModule::MyOuterClass::MyInnerClass
end
end
Note the use of the implicit subject, passed as an argument to #expect. You can certainly pass other local or instance variables instead, but in this case subject is already defined for you as MyModule::MyOuterClass::MyInnerClass.new.
Most of us are using the preferred Rspec syntax, so it would be:
expect(#instance_of_MyInnerClass).to be_a MyInnerClass

Accessing a file in ruby - differences

What is the different between the following statements?
#(not working)
File.exists?("path to file")
#(working)
::File.exists?("path to file")
I used above statements in Chef framework of Ruby.
There is another constant named File in the scope where you are using File.exists?("path to file"). But when you use the :: operator, you are telling ruby to find the File constant in Object (Object::File)
Here is possible try to replicate your issue :
Not working :
class Foo< BasicObject
def self.file_size
File.size(__FILE__)
end
end
p Foo.file_size # uninitialized constant Foo::File (NameError)
The reason is File class is available to the top level ( i.e. in the scope of the class Object) and inside any class which is a direct/ indirect subclass of Object. But Foo has no relation with Object, you wouldn't be able to access it inside Foo, if you don't tell it, from where File class ( or constant ) actually accessible.
Working :
class Foo< BasicObject
def self.file_size
::File.size(__FILE__)
end
end
p Foo.file_size # => 132
Although here also, Foo has no relation with Object, but we are explicitly ( by using :: constant scope resolution operator ) telling Ruby that from where we are trying to access the File class ( Remember class(s) are also constant in Ruby) inside Foo class. Thus here is no objection from Ruby.
Check out if such situation is present in your code too.
On a side-note, File.exists? is deprecated - use File.exist?
According to maty, the question should be asked in this way:
"object, do you exist?"
"object.exist?"
Keep this in mind - yes, "if file exist" is not proper english,
but asking it that way would be wrong from the ruby object
point of view.
As for the leading :: - this refers to toplevel scope.
It is not often required, usually only when you have the same name
of a class or a module.

Extending Enumerable in Rails 3

UPDATE TO QUESTION
Here is what I have done based on some research and findings.
STEP 1 - I have this module in my Rails 3 project and place it in my lib folder
# lib/enumerable.rb
module Enumerable
def sum
return self.inject(0){|acc,i|acc +i}
end
def average
return self.sum/self.length.to_f
end
def sample_variance
avg=self.average
sum=self.inject(0){|acc,i|acc +(i-avg)**2}
return(1/self.length.to_f*sum)
end
def standard_deviation
return Math.sqrt(self.sample_variance)
end
end
STEP 2 - According to this blog article, in Rails 3 your lib folder will not get loaded automatically. In order to load this module you need to go to your config / application.rb and type this in:
config.autoload_paths += %W(#{config.root}/lib)
STEP 3 - Then in your model my understanding is you type this in to get the module picked up.
class MyModel < ActiveRecord::Base
include Enumerable
end
STEP 4 - I then try restart the rails server and try this out and I get false when I would expect it to be true.
MyModel.respond_to?('sample_variance')
# false, when it should be true
What am I doing wrong? Should I not be getting true?
Your inclusion of the main Enumerable module (not your extension) undoubtedly worked, and you can test it by simply checking for any of the methods that were mixed in. The problem is, your 'Include Enumerable' may not have included your file, but rather the main module.
One suggestion is to rename the file name for your extension, and have it loaded through an initializer with a
require 'my_enumerable.rb'
That way you for sure get both Enumerable and your extension to Enumerable loaded.
If I understand what you're driving at, you're trying to use Enumerable's sum method in ActiveRecord. You can do that by converting the current object to an array, then calling Enumerable's sum method on that array.
One more thing: you don't need to use return like you are using it. Ruby will return the last calculated thing from your method. You don't need to use self like that either -- in Ruby, self is the current object.
So if you have a method:
def charlie
inject{|i, j| i + j + 1}
end
and you call it like this:
(1..2).charlie
self is the current object (1..2).
The output will be 4, with no self or return.
I highly recommend Dave Thomas' lecture on Ruby metaprogramming, I tried to find it, but I could not, it's out there on the web somewhere.
You might want to take a look at this:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
You can include a module in a class, and thereby make that module's methods available to that class.
If you include Enumerable into a Rails model, then its methods would be available to that model. But since Enumerable's methods are already available to certain types of objects inside your Rails project, and those objects are available to be instantiated from inside your model, I don't see why you might do that, because Enumerable's methods are working just fine for the purposes they were designed.
Anyway, you might find that one of the following might work for you:
-- use Activerecord's sum method
-- convert your object to an array, and use Enumerable's sum method
-- write your own method, but don't call it sum, because you don't want to confuse yourself.
Try commenting out the second occurrence of module Neuone in the following snippet, and see what happens. Then try commenting out the Charlie.one method, and see what happens.
module Neuone
def one
'neuone one'
end
def two
'neuone two'
end
end
module Neuone
def two
'neuone two two'
end
end
class Charlie
include Neuone
def one
'charlie one'
end
end
c = Charlie.new
p c.one
p c.two

Resources