In Ruby, how can I reflect on classes contained within a module? - ruby

I'm using a library that lays its library out like this:
module Lib
class A; end
class B; end
...
end
I know that I can use send on an object to "call" a method known only at runtime (e.g., foo.send(:bar, :baz_param=>42). How can I do this at the class level?
In other words, I suspect there's a way to write something like this:
label = :Klass
MyModule.some_method(label).new
that executes, in effect, as:
MyModule::Klass.new
Am I right?

As soon as I posted the question, I had a brainwave:
const_get
Class names are treated as constants, and the method is defined for all modules, too, so the lookup scope can be restricted to that module only. Just remember to get the capitalization right:
MyModule.const_get(:Klass).new # => #<Klass:> #CORRECT
MyModule.const_get(:klass).new # => NameError: wrong constant name

Related

Make nested modules available globally

Given I have the following data structure:
module A
module B
module C
class D
end
end
end
end
I want to access it in client code like this:
C::D
rather than
A::B::C::D
One solution would be to define a method in module A like this:
module A
def self.get_d
B::C::D
end
end
A.get_d
But the thing is that C::D is defined in many places already in system. So I need to be backward compatible. How can I address this situation?
There is no such thing as a "nested module". It's just a module that is assigned to a namespaced constant. If you want to assign to a different constant … just assign it to a different constant:
C::D = A::B::C::D
Note: this assumes that a constant named C exists in the current namespace and that it refers to a module or class. However, that assumption is already present in your question.

What is the scope of variables and methods included via ruby modules?

Suppose I have the following:
module MyModule
module SubModule
Var = 'this is a constant'
var = 'this is not a constant'
def hello_world
return 'hello world!'
end
end
end
In the same file, I can only seem to access MyModule::SubModule::Var, but not any the constant or the method. If I now create a class and include these modules in different ways, I get additional strange behavior:
class MyClass
include MyModule
def initialize()
puts SubModule::Var
end
def self.cool_method
puts SubModule::Var
end
end
In this case, I can again only access Var, but not the other two. SubModule::var and SubModule::hello_world do not work. Finally:
class MyClass
include MyModule::SubModule
def initialize()
puts Var
puts hello_world
end
def self.cool_method
puts Var
puts hello_world
end
end
In this case, I can now access both Var and the method hello_world but not var, and, the weirdest thing, is that hello_world appears to have become an instance method! That is, the call to hello_world in initialize works, but the one in self.cool_method doesn't. This is pretty strange, considering that Var seems to have been included as a class variable, since outside the class, I must access them like so:
MyClass::Var
x = MyClass.new
x.hello_world
So, I have a few major questions.
What is going on behind the scenes with regards to Var vs var? It appears that capitalizing a variable name is more than just a convention after all.
When includeing a module, what kinds of things are passed to the including class, and at what scope?
Is there a way to do the opposite? That is, use include to include an instance variable or a class method?
What is going on behind the scenes with regards to Var vs var? It appears that capitalizing a variable name is more than just a convention after all.
Yes, of course, it's not a convention. Variables which start with an uppercase letter are constants, variables which start with a lowercase letter are local variables. The two are completely different.
When includeing a module, what kinds of things are passed to the including class, and at what scope?
Nothing gets passed anywhere. includeing a mixin simply makes that mixin the superclass of the class you are includeing it into. That's all. Everything else then works exactly as with classes.
Is there a way to do the opposite? That is, use include to include an instance variable or a class method?
I don't understand this question. Instance variables have nothing to do with mixins or classes. They belong to instances, that's why they are called "instance" variables.
There are no such things as "class methods" in Ruby. Ruby only knows one kind of methods: instance methods. When Rubyists talk to each other, they will sometimes use the term "class method" to mean "singleton method of an object that happens to be a class", but they do that knowing full well that class methods don't actually exist, it's just a shorthand in conversation. (And, of course, singleton methods don't exist either, they are just a convenient way of saying "instance method of the singleton class".)

Loading external files within a class/module

I have an external file: path_to_external_file.rb with some class definition:
class A
some_definitions
end
And I want to load that within module B so that the class A defined above can be referred to as B::A. I tried:
class B
load('path_to_external_file.rb')
end
but A is defined in the main environment, not in B:
A #=> A
B.constants # => []
How can I load external files within some class/module?
Edit
Should I read the external files as strings, and evaluate them within Class.new{...}, and include that class within B?
You cannot. At least using load or require, the Ruby files will always be evaluated in a top context.
You can work around that problem in two ways:
Define class B::A directly (but you are probably trying to avoid that)
Use eval(File.read("path_to_external_file.rb")) within your B class
Edit: Maybe, this library is interesting for you: https://github.com/dreamcat4/script/blob/master/intro.txt
Generally, it's a bad idea to define a class as "class A" but then "magically" make it contained by module B. If you want to refer to class A as B::A, you should define it using either:
module B
class A
# contents
end
end
or:
class B::A
# contents
end
Otherwise anyone who reads your code will be confused. In this case, you don't gain anything in clarity, brevity, or convenience by using "tricks", so straightforward code is better. There is a lesson here: the metaprogramming features of Ruby are great, but there is no need to use them gratuitously. Only use them when you really gain something from doing so. Otherwise you just make your code hard to understand.
BUT, having read your comment, it looks like there is really a good reason to do something like this in your case. I suggest that the following solution would be even better than what you are envisioning:
m = Module.new
m.module_eval("class C; end")
m.constants
=> [:C]
m.const_get(:C)
=> #<Module:0xfd0da0>::C
You see? If you want a "guaranteed unique" namespace, you can use an anonymous module. You could store these modules in a hash or other data structure, and pull the classes out of them as needed. This solves the problem you mentioned, that the users of your app are going to be adding their own classes, and you don't want the names to collide.

How to load a unknown class in a module?

I have some rb files, all with the same structure:
class RandomName < FooBar
end
The randomname is a random class name which changes in each rb file but all inherits from Foobar.
how i can load all randomclass from there rb files?
I think there are 2 parts to the solution:
How to dynamically instantiate a class
a. Using String#constantize from ActiveSupport
klass = "SomeNamespace::SomeClassName".constantize
klass.new
b. Use Module#const_get (which doesn't handle namespaces)
klass = const_get(:SomeClassName)
klass.new
How to detect a class name
A convention followed widely in ruby is to name the file after the class that it contains, so random_name.rb would contain the RandomName class. If you follow this convention, then you could do something like:
Dir["/path/to/directory/*.rb"].each do |file|
require file
file_name = File.basename(file.path, '.rb')
# using ActiveSupport for camelcase and constantize
file_name.camelcase.constantize.new
end
I think you should explain what you are trying to accomplish. The approach you are taking seems unconventional and there may be a much more effective way of reaching your goal without doing all this loading of files and dynamic instantiation of classes with random names.
Remember, just because ruby lets you do something, it doesn't mean it's a good idea to actually do it!
you can define a method called inherited in the FooBar class. look here
class FooBar
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
end
Every time a subclass is created, you will get it in the callback. Then you can do whatever you want with all those subclasses.
I have a similar requirement, passing a class name in as a string. One trick with require is that it doesn't have to be at the start, so I prefer to only load the class I need.
I used eval because it doesn't have any Rails dependencies (I'm writing pure Ruby code here).
The following relies on convention (that the Class is in a file of the same name), but if you do know the class and file, this approach has the advantage of not requiring every file in a directory and only dynamically loading the one you need at the time you need it.
klass = "classname"
begin
# Load the file containing the class from same directory I'm executing in
require_relative klass # Or pass a local directory like "lib/#{klass}"
# Use eval to convert that string to a Constant (also capitalize it first)
k = eval(klass.capitalize).new
rescue
# Do something if the convention fails and class cannot be instantiated.
end
k.foo # Go ahead and start doing things with your new class.

What is Ruby's double-colon `::`?

What is this double-colon ::? E.g. Foo::Bar.
I found a definition:
The :: is a unary operator that allows: constants, instance methods and class methods defined within a class or module, to be accessed from anywhere outside the class or module.
What good is scope (private, protected) if you can just use :: to expose anything?
:: is basically a namespace resolution operator. It allows you to access items in modules, or class-level items in classes. For example, say you had this setup:
module SomeModule
module InnerModule
class MyClass
CONSTANT = 4
end
end
end
You could access CONSTANT from outside the module as SomeModule::InnerModule::MyClass::CONSTANT.
It doesn't affect instance methods defined on a class, since you access those with a different syntax (the dot .).
Relevant note: If you want to go back to the top-level namespace, do this: ::SomeModule – Benjamin Oakes
This simple example illustrates it:
MR_COUNT = 0 # constant defined on main Object class
module Foo
MR_COUNT = 0
::MR_COUNT = 1 # set global count to 1
MR_COUNT = 2 # set local count to 2
end
puts MR_COUNT # this is the global constant: 1
puts Foo::MR_COUNT # this is the local constant: 2
Taken from http://www.tutorialspoint.com/ruby/ruby_operators.htm
:: Lets you access a constant, module, or class defined inside another class or module. It is used to provide namespaces so that method and class names don't conflict with other classes by different authors.
When you see ActiveRecord::Base in Rails it means that Rails has something like
module ActiveRecord
class Base
end
end
i.e. a class called Base inside a module ActiveRecord which is then referenced as ActiveRecord::Base (you can find this in the Rails source in activerecord-n.n.n/lib/active_record/base.rb)
A common use of :: is to access constants defined in modules e.g.
module Math
PI = 3.141 # ...
end
puts Math::PI
The :: operator does not allow you to bypass visibility of methods marked private or protected.
What good is scope (private, protected) if you can just use :: to expose anything?
In Ruby, everything is exposed and everything can be modified from anywhere else.
If you're worried about the fact that classes can be changed from outside the "class definition", then Ruby probably isn't for you.
On the other hand, if you're frustrated by Java's classes being locked down, then Ruby is probably what you're looking for.
Surprisingly, all 10 answers here say the same thing. The '::' is a namespace resolution operator, and yes it is true. But there is one gotcha that you have to realize about the namespace resolution operator when it comes to the constant lookup algorithm. As Matz delineates in his book, 'The Ruby Programming Language', constant lookup has multiple steps. First, it searches a constant in the lexical scope where the constant is referenced. If it does not find the constant within the lexical scope, it then searches the inheritance hierarchy. Because of this constant lookup algorithm, below we get the expected results:
module A
module B
PI = 3.14
module C
class E
PI = 3.15
end
class F < E
def get_pi
puts PI
end
end
end
end
end
f = A::B::C::F.new
f.get_pi
> 3.14
While F inherits from E, the B module is within the lexical scope of F. Consequently, F instances will refer to the constant PI defined in the module B. Now if module B did not define PI, then F instances will refer to the PI constant defined in the superclass E.
But what if we were to use '::' rather than nesting modules? Would we get the same result? No!
By using the namespace resolution operator when defining nested modules, the nested modules and classes are no longer within the lexical scope of their outer modules. As you can see below, PI defined in A::B is not in the lexical scope of A::B::C::D and thus we get uninitialized constant when trying to refer to PI in the get_pi instance method:
module A
end
module A::B
PI = 3.14
end
module A::B::C
class D
def get_pi
puts PI
end
end
end
d = A::B::C::D.new
d.get_pi
NameError: uninitialized constant A::B::C::D::PI
Did you mean? A::B::PI
Adding to previous answers, it is valid Ruby to use :: to access instance methods. All the following are valid:
MyClass::new::instance_method
MyClass::new.instance_method
MyClass.new::instance_method
MyClass.new.instance_method
As per best practices I believe only the last one is recommended.
No, it is not to access every method, it is a "resolution" operator, that is, you use it to resolve the scope (or location you can say) of a constant/static symbol.
For example in the first of your line, Rails use it to find the Base class inside the ActiveRecord.Module, in your second one it is used to locate the class method (static) of the Routes class, etc, etc.
It is not used to expose anything, its used to "locate" stuff around your scopes.
http://en.wikipedia.org/wiki/Scope_resolution_operator
Ruby on rails uses :: for namespace resolution.
class User < ActiveRecord::Base
VIDEOS_COUNT = 10
Languages = { "English" => "en", "Spanish" => "es", "Mandarin Chinese" => "cn"}
end
To use it :
User::VIDEOS_COUNT
User::Languages
User::Languages.values_at("Spanish") => "en"
Also, other usage is : When using nested routes
OmniauthCallbacksController is defined under users.
And routed as:
devise_for :users, controllers: {omniauth_callbacks: "users/omniauth_callbacks"}
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
It is all about preventing definitions from clashing with other code linked in to your project. It means you can keep things separate.
For example you can have one method called "run" in your code and you will still be able to call your method rather than the "run" method that has been defined in some other library that you have linked in.
module Amimal
module Herbivorous
EATER="plants"
end
end
Amimal::Herbivorous::EATER => "plants"
:: Is used to create a scope . In order to access Constant EATER from 2 modules we need to scope the modules to reach up to the constant
In simple it is a namespace,
now namespace is container for modules, classes, function and other. and it also help to solve the problem name conflict.
and in ruby you can access namespace by module like
module A
class Article
def Base
end
module B
end
end
so to access the class Article we use A::Article.
and in some cases you see that
A::Article<Application::Base
this mean that the Article class of module A inherit the Base class from Application module.
Reviving this thread a little.
Can we create a 'ruby' name for this operator ::
Surprised that we haven't already since we have splats, spreads, hash rockets etc.
Surely we can come up with something more fun than 'double colon' or 'unary operator'
Thinking
quad dot
four eyes
tetra dot
Nibble (half a byte - 4 bits)

Resources