Reputation: 21
I'm encountering a peculiar issue on Rails.
We've set up a Hook
class to handle a small pub/sub mechanism inside our app it goes something like this
class Hook
@subscriptions = {}
class << self
def subscribe(message, &block)
@subscriptions[message] ||= []
@subscriptions[message] << block
end
def publish(message)
@subscriptions[message].each(&:call)
end
end
end
(It's a tiny bit more fleshed out but you get the idea).
The problem is:
In an initializer we've got Hook.subscribe(:change) { ... }
But when the model calls Hook.publish(:change)
, nothing happens. A quick check shows that until Hook
is called again by the model, a defined? Hook
returns nil
.
The code in the model is
after_commit do
# byebug
Hook.publish(:change)
end
Even weirder: In the initializer I set up a global variable to hold the constant $hook = Hook
, the constant is indeed here inside the $hook
variable in the model but doesn't match the Hook it autoloads.
# inside a debugger in the model
> defined? Hook
=> nil
> $hook
=> Hook
> Hook # will trigger autoloading
=> Hook
> defined? Hook
=> "constant"
> Hook == $hook
=> false
> Hook.instance_variable_get(:@subscriptions)
=> {}
> $hook.instance_variable_get(:@subscriptions)
=> { change: [<Proc:...>] }
> Hook.object_id == $hook.object_id
=> false
If I set it back as the constant Hook = $hook
through the debugger everything behaves properly again — the constant is maintained with the proper variables across requests, even.
I'm reaching the end of my wits here. As anybody got any idea what might be going on?
Upvotes: 0
Views: 70
Reputation: 21
I finally found a fix, even if I don't fully understand it (I'm still a bit unclear on how Rails handles autoloading I guess — I'll need to investigate deeper when I have the time).
The Hook
class was inside a module (OurApp::Hook
) which I had omitted for clarity in the example I shared (my mistake, obviously).
I noticed that OurApp.constants
was losing a few constants other than Hook
.
I went inside lib/our_app.rb
and modified it so:
module OurApp
autoload :Hook, 'lib/our_app/hook.rb'
# ...
end
That seems to do the trick, no more issues.
Thanks everybody !
Upvotes: 2