Requiring a file inside a module context - ruby

I have a module called WG that all classes inside my application are inside, purely for the purposes of namespacing, so I don't have naming collisions with outside code.
My issue is that every class in my application then needs to be specified as being inside that module, and that's a pain, mainly because it adds one indent level to all code in my application.
In other words, here's a typical class, in a file called "Thing.rb"
module WG
class Thing
def do_things
end
end
end
In my mind, there should be some way of requiring that file inside the context of the WG module, so that I can forego the line at the top and at the end, and the mandatory one-level indentation.
Thanks!

Try using the :: operator:
class WG::Thing
# ...
end

There isn't. requireing a file simply runs the file as-is.
load allows you to run the code inside the context of an anonymous module, but not a specific one. Of course, you can still access the global namespace by using the :: scope resolution operator.

Related

Opening Object to include a module

I'm writing an interactive shell (just as irb) in ruby, for another language. I thought of writing all the functions regarding the shell (i.e read input and parse) in a module, then doing class Object; include Shell; end.
My doubt is: From my readings from Ruby under a microscope, I learned that in CRuby (what I'm using), when you include a module, ruby creates a copy of this module and appends it as the super class of the class that includes it, so it seems that this would waste a bit of memory, but then I looked into the kernel module, which AFAIK is included in object, and this seems okay.
Finally, the question is, Would it be a good practice to include Shell into Object?
running class Object; include Shell; end
is the same as just include Shell, since Object is the default scope.
This is not bad for memory, it's a pretty fundamental behavior.
If you want to avoid working on the Object (global) scope, you can define a custom module that includes Shell:
class App
include Shell
def self.begin
# do something with shell
end
end
App.begin
though if you're making a REPL and do want your methods available on the global scope, then saying include Shell without a wrapper class seems a good approach.

When to use a module and when to use a global scope

This is a simple question: Should I have a module that contains all my classes (and submodules):
module ProjectName
class Something
# code
end
module Abc
# code
end
end
Or simply everything in a global scope:
class Something
# code
end
module Abc
# code
end
It is considered good practice not to pollute your global scope. Namespacing your application into modules, encapsulating related behaviour makes it easier to comprehend, helps avoid naming conflicts, and lets you easily port parts of your code into other applications or contexts.
In Ruby it also gives you a natural way of storing module wide constants, and gives you the option to add methods that don't need a containing object directly to the module.
In some languages, (notably JavaScript) scoping also has an impact on performance, as keeping objects in the global scope might prevent them from getting qualified for garbage collection.

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.

Avoiding globals with dynamically loaded ruby files

I am in a position at the moment where I have a plugins folder where there there could be 1 or 100 plugins to be loaded.
Now the problem is, each plugin requires an instance of a class defined within the startup ruby file.
A really simplified example would be:
#startup.rb
def load_plugins
#... get each plugin file
require each_plugin
end
class MuchUsedClass
def do_something
#...
end
end
muchUsedInstance = MuchUsedClass.new
load_plugins
#some_plugin.rb
class SomePluginClass
def initialize(muchUsedInstance)
#muchUsedInstance = muchUsedInstance
end
def do_something_with_instance
#muchUsedInstance.do_something
end
end
somePluginInstance = SomePluginClass.new(muchUsedInstance)
somePluginInstance.do_something_with_instance
The main problem is that when you call require, it doesn't have any clue about what has happened before it is being required. So I find it nasty making a global variable within the startup file just to satisfy all other required files, but it seems like one of the only ways to be able to pass some data down to an included file, I could also make a singleton class to expose some of this, but that also seems a bit nasty.
As I am still new to ruby and am still looking through the statically typed glasses, I will probably be missing a decent pattern to solve this, in C# I would opt for dependency injection and just hook everything up that way...
Your example code does not have a global variable. Global variables have names that start with $. The code as you wrote it won't work, because muchUsedInstance is just a local variable and will not be shared between different Ruby files.
If you are not going to change the instance ever, you could easily store it as a constant:
MuchUsedInstance = MuchUsedClass.new
You could store it as a nested constant inside the class:
MuchUsedClass::Instance = MuchUsedClass.new
You could store it as an instance variable inside the class object, with a getter method that automatically creates it if it isn't there already:
def MuchUsedClass.instance
#instance ||= MuchUsedClass.new
end

Ruby style of accessing of elements of modules and classes

module Hints
module Designer
def self.message
"Hello, World!"
end
end
end
is there any way to use the following code to access the message method?
p Hints.Designer.message
Instead of
p Hints::Designer.message
The period . is only meant for accessing methods.
The double colon :: is used to indicate namespaces.
Both modules and classes can be nested in each other. This creates a namespace for the nested class. (Technically, Module is an instance of Class.) Thus, the following is correct no matter if Hints or Designer are a class or a module.
Hints::Designer.message
You can try yourself by opening irb on the command line. Hints.Designer.message says NoMethodError: undefined method 'Designer' for Hints:Module.
Update (as I am not allowed to comment...):
While many things in Ruby can be overwritten ("monkey patched"), basic operators cannot. :: is a basic language feature that is and should not be customizable (in order to prevent a big mess ;)).
You can use dot to access the message method, but you can't use it to access Designer module, because Designer it is not a method (but a constant). Dot is for invoking methods only.

Resources