I have built a Ruby project and I want to switch from using a ton of require_relative
statements to a smarter autoloader like Zeitwerk.
There are a lot of files/folders in the project, hence I will only present some of them in order to not make my question too bloated with unnecessary details. You can find the whole project on Github.
The structure of the repo is the following
chess/
bin/
chess
lib/
chess.rb
chess/
board.rb
serialize.rb
coordinates.rb
pieces/
(other files and folders)
(other files)
bin/chess
is the executable file and contains
#!/usr/bin/env ruby
require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setup
Chess.new.start # method that starts the whole game
lib/chess.rb
contains the class Chess
, which runs the logic of the game.
All the other files inside of lib/
follow the Zeitwerk file structure.
Therefore, lib/chess/serialize.rb
contains
class Chess
module Serialize
...
end
end
and lib/chess/pieces/piece.rb
contains
class Chess
module Pieces
module Piece
...
end
end
end
When I try to run the script, with bin/chess
(or cd
ing into the bin/
folder and running ./chess
), the terminal returns
uninitialized constant Chess (NameError)
It seems like the files are not loaded by Zeitwerk. I read all of their documentation but I was not able to discover what's the problem.
edit:
As one of the answers pointed out, I removed the for_gem
call in bin/chess
with
loader = Zeitwerk::Loader.new
loader.tag = File.basename(__FILE__, ".rb")
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
loader.push_dir("#{__dir__}/../lib")
loader.setup
However, now all the classes/modules inside of the Chess
namespace are loaded, while all the ones located in deeper directories, like lib/chess/display/chess_display.rb
(which is Chess::Display::ChessDisplay
) don't get loaded.
CodePudding user response:
According to the Readme, for_gem
corresponds to:
# lib/my_gem.rb
require "zeitwerk"
loader = Zeitwerk::Loader.new
loader.tag = File.basename(__FILE__, ".rb")
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
loader.push_dir(__dir__)
note the push_dir(__dir__)
and that it's expected to be called from the lib/
directory. You're calling it from a file in the bin/
directory.
I have not used zeitwork like this, but I think you might want to either:
- directly require just
lib/chess.rb
and perform theZeitwork::Loader.for_gem
in that file - Skip
for_gem
and use the snippet above, but adjust__dir
__ to be../lib
.
CodePudding user response:
You use for_gem
in lib/chess.rb
. That way, the setup is ready no matter how you load the library.
The bin/chess
executable then loads the entry point. It should not issue a require_relative
, but a regular require 'chess'
assuming lib
is in the load path.
Same for the test suite, just require 'chess'
in the test or spec helper that sets the test suite up.