Reputation: 1259
I'm trying to write a game in Ruby (not Rails) as a way to teach it to myself better. (Meaning, I'd like to do this right, but if I'm trying to shoehorn something that just won't work into Ruby, I'll switch languages.) I'm running into a problem with require order, and I'm wondering if there's a clean way to do the following.
Here's my structure so far:
game
Gemfile
src
models
character.rb
game_object.rb
init.rb
Instead of listing each file individually, init.rb requires files like this:
Dir['./src/**/*.rb'].each do |app|
require app
end
game_object.rb is, so far, very simple, but character.rb looks like this:
module Game
class Character < Game::GameObject
attr_accessor :name
def initialize(name)
@name = name
end
end
end
Unfortunately, if I do that, I get "uninitialized constant Game::GameObject (NameError)", unless I explicitly require game_object before other files.
It seems to me that I have a few options here:
These all seem to be more complicated than it should be. Is there a cleaner way?
Upvotes: 4
Views: 771
Reputation: 35308
I haven't done it before, but you could add your own autoloading logic to Module#const_missing
.
class Module
def const_missing_with_autoload(c)
components = []
(self.to_s.split("::") + c.to_s.split("::")).reverse.each do |comp|
components.unshift(comp.gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase)
begin
require File.join(*components)
return const_get(c)
rescue LoadError
end
end
const_missing_without_autoload(c)
end
alias_method :const_missing_without_autoload, :const_missing
alias_method :const_missing, :const_missing_with_autoload
end
I assumed this would work without the method swizzling, but I couldn't get that working. I'd love somebody to post a clean solution that achieves what I'm trying to do. FWIW, just adding ActiveSupport to your project would probably be useful anyway.
Upvotes: 0
Reputation: 9605
Just putting it out there: more code does not mean "dirty", just as often as less code does not mean "clean."
Requiring your files manually will give you less headache in the long run—it solves load order issues and prevents you from requiring files you don't actually want. Plus, anyone looking at your code later on has a nice, clean manifest of what's being loaded in your app.
My suggestions:
src
folder, it's redundant—your Ruby app is all source.Example:
game/
bin/
gamerunner
Gemfile
lib/
game.rb
game/
game_object.rb
models/
character.rb
spec/
spec_helper.rb
models/
character_spec.rb
You are writing specs / tests, aren't you?
Then, in lib/game.rb
:
require 'game/game_object.rb'
# require the rest of your library as you build it
module Game
end
And in your init:
require 'game'
require 'models/character.rb'
Much cleaner, much easier to extract from later on and should solve your problem.
Upvotes: 5