Is there any way, to track dynamically which classes or modules are included in required files.
a short example:
#my_module.rb
module MyCustomModule
def foo
end
end
#custom.rb
require 'my_module.rb'
#is here any way to track which modules are in the required 'my_module.rb'
#without parsing the file or having some naming conventions?
The aim is, to require dynamically a bunch of files and including the contained modules in a class, regardless of how they are named.
You can use ObjectSpace to determine newly defined Modules.
#custom.rb
existing_modules = ObjectSpace.each_object(Module).to_a
require 'my_module.rb'
new_modules = ObjectSpace.each_object(Module).to_a - existing_modules
# => [MyCustomModule]
class X
new_modules.each{|m| include m}
end
Note: You probably want to only include the top level ones, though, so check the names for ::
Related
I would like to namespace my Ruby classes by putting them in a module. Indeed, this is a good idea if I decide to publish my Ruby gem so that the class names do not clash with existing classes in another gem. I know I can do the following for a class A::B:
module A
class B
end
end
However, the above is quite cumbersome in that I need to put all my class definitions into a single Ruby source file in order to scope them under the module. I would rather keep my class definitions in separate source files, much like a Java project, so how can I add classes to a module when they are all defined in separate files?
The accepted practice in this case is to wrap every file in module block
# a/b.rb
module A
class B
end
end
# a/c.rb
module A
class C
end
end
Btw. because of the way constant are resolved, it is advisable to use the long form I quoted above instead class A::B
(http://blog.honeybadger.io/avoid-these-traps-when-nesting-ruby-modules/).
I currently creating a gem, so, I have a folder with different files, which contains different classes, this folder will be updated with more files and also current ones will be updated as well, in another file I have a module that should contain these classes.
Currently, I add manually to the module all the classes:
File1.rb:
module MyModule
class ClassA
# code here
end
end
File2.rb:
module MyModule
class ClassB
# code here
end
end
But, since I will add more classes and current classes will be updated this is not optimal and very dangerous to maintain clean, so is there any other way to add classes in different files in one module set in another file?
Thanks in advance
No there is no another way and I don't see any danger in doing it the way you did (correct way).
I am developing a Gem that will allow users to auto-require, instantiate, and register classes in a specific directory. I'm just not sure how to achieve this. This is what I've come up with so far...
Dir[Dir.pwd + '/extensions/*.rb'].each do |file|
require file
extension_class = # instantiate the class here
MyApp.extensions << extension_class
end
How can I instantiate the class without knowing what it is called?
It sounds like you are making a system for writing extensions to software. Since the extension classes are probably all similar in some way, such as sharing some common methods, it might make sense to have a base class called Extension from which all the extension classes inherit.
In fact, this is very useful to the stated problem because when a user inherits from your class you can detect it, and add the subclass to a list. Here is some proof of concept code:
class Extension
class << self
attr_reader :list
end
#list = []
def self.inherited(klass)
#list << klass.new
end
end
# The MyExt class would be in another file, loaded by require.
class MyExt < Extension
end
p Extension.list # => [#<MyExt:0xb777d884>]
If you don't want to use subclassing for some reason, it is equally easy to make Extension be a module and use the included hook instead of inherited.
Personally, I would just remove the call to .new because I don't see a reason to instantiate the class right away, but that's up to you. I would just store a list of classes.
this is an interesting idea which had me scratching my head for a while. i couldn't find an immediate solution with googling, so i played around with the language a bit. i came up with three basic ideas:
keep track of all loaded classes, and grab new ones as they come in
use a naming scheme to relate filenames to classes
use some sort of introspector on the required classes so they can register themselves for future inclusion
i decided that i liked the first option the most; it doesn't require you to structure your files in a particular way, or augment the classes in order to know they will be required this way. here is a rudimentary implementation, borrowing your initial structure:
classes = ObjectSpace.each_object(Class).to_a
Dir[Dir.pwd + '/extensions/*.rb'].each do |file|
require file
changed_classes = ObjectSpace.each_object(Class).to_a
(changed_classes - classes).each do |extension_class|
MyApp.extensions << extension_class
end
classes = changed_classes
end
I have different definitions of a module in 3 different files and I want to load them dynamically depending on user interaction. Using Kernel.load() method only works successfully for me the first time a certain file loads. Does anyone know which method should I use to make a file load always, not regarding if it had been already loaded before?
The module contains some constants that are used in other parts of the application. Depending on user choice, one concrete module with specific values for that constants should be loaded so they will have the appropriate values.
Most likely, you are not taking the right approach to your problem.
Why to define the same module differently if you can define many different modules with the same API and use the one you wish dynamically?
What are you doing with the module?
Are you using as a mixin?
if condition
object.extend Module1
else
object.extend Module2
end
object.method(bla,blabla)
Are you just invoking its methods as static methods?
module = if condition
Module1
else
Module2
end
module.method(bla, bla bla)
Instead of using constants you should use either static methods or a static method returning an hash with the value of your "constants". Constants are not very pratical.
Given that you want to use the modules to store data instead of logic, maybe you should use classes instead:
class Options
attr_reader :constant_1,:constant_2
end
class Options1 < Options
def initialize
#constant_1="value1"
#constant_2="value2"
end
end
class Options2 < Options
def initialize
#constant_1="value3"
#constant_2="value4"
end
end
options= condition ? Options1.new : Options2.new
options.constant_1 # => intended value
I have a Date class which I would like to use to overwrite Ruby's Date class. However, whenever I do a require 'Date' in my other files, it includes Ruby's Date class and not my own.
I thought that putting it in a module would work well, so I did so within the Date.rb file:
module myModule
class Date
#...
end
end
However I still can't figure out how to make my other classes include THIS Date class and not the built-in class. How can I achieve this?
All help is appreciated and thanks in advance!
Adam,
Your best bet is to simply follow some conventions:
Always name your filenames lower case (date.rb not Date.rb)
Put your files in a specific directory inside your library (lib is a good candidate)
Don't name your files the same thing as built in Ruby classes (call it my_date.rb or something) or if your class/module is name-spaced inside a module, put it in a folder of the module name (lib/my_module/date.rb).
This removes any ambiguity in which file you are trying to load. If you absolutely must keep it named date.rb, then load it with the full path by doing something like: File.join(File.dirname(__FILE__), "date.rb").
For debugging purposes you can look at the following special variables to see what's being loaded instead of your file:
$: will show the load path (i.e. every directory it looks in to find files to require. You will note that the current directory (.) is last. This is why your file isn't loaded -- it looks in the system path first. You can always move your current directory to the front of the load path as a solution by doing $:.unshift(File.dirname(__FILE__)), but I'd try one of the above approaches before resorting to this
$" shows every file that has been required into your current environment so far.
require 'path/to/Date.rb'
class MyClass
include MyModule::Date
end
First, you need to require the correct file. Often the right thing happens when you do require 'date' and it's resolved to a file based on your $LOAD_PATH. You can be more specific by putting your date.rb in a directory so you can require 'my_module/date' or just use a relative path like ./date
You can then specify the module hierarchy when referring to this class:
::MyModule::Date
Or you can include it wherever you prefer to call this Date over the standard one without specifying:
class Event
include MyModule
def initialize
#date = Date.today # refers to MyModule::Date
end
end