Intro:
I am migrating my application from Rails 3.2.x to Rails 7.0.4.2. I am much more far already, than I expected.
Although I am a zeitwerk noob, I like that approach - especially the "no require" part.
To be sure, that I do not have any requires I renamed all to require3, that only does something in rails 3 (I also have a require7 for those cases where it is really needed).
In parallel I have a red line for those in console.
The module:
The module is BotCheck (yes, a capture) and is located as bot_check.rb in lib. Needed classes are in lib/bot_check/..
The source of BotCheck is short. One extend and two methods (these are somehow 'global'). All the rest is behind in classes in lib/bot_check/.
# lib/bot_check.rb
module BotCheck
extend Configurable
configurable do
global_attr etw_divider: 5
global_attr etw_min_level: 0
end
# this for debugging …
puts "loading ====================== module BotCheck".blue.on_yellow
def self.bot_check_from_bot_id(id)
…
end
def self.bot_check_level
…
end
def bot_check_from_bot_id(id)
BotCheck.bot_check_from_bot_id(id)
end
def bot_check_level
BotCheck.bot_check_level
end
end
All the needed modules and classes are like
#lib/bot_check/what_ever.rb
module BotCheck
class WhatEver
include BotCheck #only some classes
…
…
end
end
I ran this in the old world Rails 3 an all works as expected (even without a require!)
Running it with Rails 7 (zeitwerk), I got "undefined method BotCheck.bot_check_level" called from BotCheck::TagHelper (not a helper just a module that creates the tags), as if the module BotCheck it self was not loaded.
This is not possible, because BotCheck.config is here and also the logger line from Configurable. that configurable was called.
Configurable: ++++++++++++++++++ extended(BotCheck)
The next try (after 2 hours not understanding anything) was to add the line above (puts "module loaded"
with rails3 I got:
Configurable: ++++++++++++++++++ extended(BotCheck)
loading ==================module BotCheck
with 7
Configurable: ++++++++++++++++++ extended(BotCheck)
"nope" something else
So I put a require7 'bot_check' (remember only for rails 7 not for 3 needed) in application.rb
Now I have my two lines, and everything works.
Conclusion:
Zeitwerk finds the Module, lets me include the module, executes extend within the module, but does not load it.
Now I have questions, because I do not expect me to find a zeitwerk bug after a few days working with it.
What is the idea behind this?
How is the state of such a module called? "somehow"?
What about the no require idea? Or:
how to completely load a module without a require, if not even an include does the job?
Where to put the require if no other solution?
What about side effects? Methods of a module or class that includes such "somehow" loaded, get the included Methods after the module is fully loaded. I do not want to search errors because of that. I made this small check:
module BotCheck class BotCheckController<SiteController include BotCheck
def check puts "botcheck config: #{BotCheck.config} puts "11111111111111 #{self.respond_to?(:bot_check_from_bot_id)}" puts "22222222222222 #{BotCheck.respond_to?(:bot_check_from_bot_id)}" require7 "bot_check" puts "33333333333333 #{self.respond_to?(:bot_check_from_bot_id)}" puts "44444444444444 #{BotCheck.respond_to?(:bot_check_from_bot_id)}" …
with this output:
botcheck config: #<Configurable::BotCheckConfig:0x00007f5080c7c450>
11111111111111 false
22222222222222 false
Warning: Require for 7:'bot_check' in: /home/user/appl/lib/bot_check/bot_check_controller.rb:11:in `check'
Configurable: ++++++++++++++++++ extended(BotCheck) ((<- this is the 2nd time of extend logged))
loading ==========================================================================module BotCheck
33333333333333 true
44444444444444 true
Edit based on comment:
The module Configurable is loaded during initialization. So it is available in initializers. Like (for BotCheck): (please note the String!)
Configurable.setup("BotCheck") do |config|
config.etw_divider=20
config.etw_min_level=0
end
The module sits and waits for extend Configurable and only after this call it injects the config into the other module. (as far as I know, it's the same algorithm, that rails has know built in. Just without parameter checking)
The module it self does not know anything about the modules that are using it until extend
1st thanks to Xavier Noria, his comment helped me to find the problem.
2nd, as I expected, it was my fault. Zeitwerk is clean!
I had a stone old zombie "bot_check.rb" in controllers, that only "extend Configurable" had.
After removing this, all works as designed / expected.
The question was exactly was going on, would need further investigation, but - I be true - two files same name, same module, but different content in different autoload-pathes …