Modifying require to accept Symbol as input and act on said Symbol-input, and then restoring require

Hi guys,

I am currently (slowly) rewriting my old MUD.

While looking at require lines, it is rather tedious
to define the path to all addons - e. g. the path
to any given .rb file. I already am at about 150-200
.rb files or so and I still have to port around 200
more, and it already feels way too tedious to me (hence
why I so often pause on projects when they become too
big haha).

But first things first - I require the MUD like so:

require 'rmud'

Now I also have a unicorn, which is (in the definition)
an animal. So it must also include module Animal, which
I put into std_animals.rb for now.

So the require right now is:

require 'rmud/animals/std_animals.rb'

Perhaps I move this to another location, but here I was
wondering - this require-based way to specify the path
to the .rb in question is tedious. More importantly,
when I do happen to change the name or location of
that file, I have to change ALL other files too.

So I was wondering:

Is there some way to specify a default location to
be used on a per-project base?

For instance:

require :std_animals

And I could manually update just a special file, which
will act on whether the input is a Symbol or not.

I can perhaps patch require (how?), but I also want to
restore the original default way how require works
again after my MUD has been loaded.

I kind of need a template-to-filename mapper and it
should work only for the MUD here (let’s assume the
namespace is module MUD) and nowhere else, even if
I do happen to combine the MUD with other projects
lateron.

How would I go about modifying require, and are there
any alternatives?

Robert H. wrote in post #1165161:

But first things first - I require the MUD like so:

require 'rmud'

Now I also have a unicorn, which is (in the definition)
an animal. So it must also include module Animal, which
I put into std_animals.rb for now.

So the require right now is:

require 'rmud/animals/std_animals.rb'

I would do this with multiple levels of requires (see below).

Perhaps I move this to another location, but here I was
wondering - this require-based way to specify the path
to the .rb in question is tedious. More importantly,
when I do happen to change the name or location of
that file, I have to change ALL other files too.

That is bad - very bad.

So I was wondering:

Is there some way to specify a default location to
be used on a per-project base?

Yes, you can do that by changing $: in your top level include (see at
end).

I kind of need a template-to-filename mapper and it
should work only for the MUD here (let’s assume the
namespace is module MUD) and nowhere else, even if
I do happen to combine the MUD with other projects
lateron.

In any way you should use proper namespaces in order to avoid name
clashes between your MUD and any other lib. I think there is a better
solution than a centralized mapper:

How would I go about modifying require, and are there
any alternatives?

I think the best is to use autoload in such cases, especially since your
number of files to load is so high and you may not always need all of
them - or not need all of them initially. You can even extend this to
several levels. A very simplistic example:

$ find lib
lib
lib/a
lib/a/x.rb
lib/a.rb

With $RUBYLIB set in the shell to point to directory “lib”:

$ cat lib/a.rb
puts FILE

module A
autoload :X, ‘a/x’
end

$ cat lib/a/x.rb

puts FILE

module A
class X
end
end

$ ruby -r a -e ‘puts “test”’
/tmp/robert/tmpcd.4211/lib/a.rb
test
$ ruby -r a -e ‘puts “test”; p A’
/tmp/robert/tmpcd.4211/lib/a.rb
test
A
$ ruby -r a -e ‘puts “test”; p A::X’
/tmp/robert/tmpcd.4211/lib/a.rb
test
/tmp/robert/tmpcd.4211/lib/a/x.rb
A::X

If you want to get fancy you can modify the root file (a.rb in this
case) to add it’s dirname to $: if it is not there:

$ cat lib/a.rb
puts FILE

d = File.dirname(File.absolute_path(FILE))
$:.unshift d unless $:.include? d

module A
autoload :X, ‘a/x’
end

From then on you can use relative paths for the autoloads. In your
example “rmud.rb” would be my “a.rb”.

Now you just need to edit one file (the representative of the parent
directory) if you add a file to require.

PS: You can even load multiple classes / modules from a single file:

$ cat lib/a.rb
puts FILE

d = File.dirname(File.absolute_path(FILE))
$:.unshift d unless $:.include? d

module A
autoload :X, ‘a/x’
autoload :Y, ‘a/x’
end
$ cat lib/a/x.rb

puts FILE

module A
class X
end

class Y
end
end

$ ruby -r ./lib/a -e ‘p A::Y’
/tmp/robert/tmpcd.4211/lib/a.rb
/tmp/robert/tmpcd.4211/lib/a/x.rb
A::Y
$ ruby -r ./lib/a -e ‘p A::Y, A::X’
/tmp/robert/tmpcd.4211/lib/a.rb
/tmp/robert/tmpcd.4211/lib/a/x.rb
A::Y
A::X