-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support class reloading for Event Subscribers #3232
Support class reloading for Event Subscribers #3232
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Left some initial change requests and questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still WIP, but before we put too much affort into this:
Is there any other way of fixing these (Rails.env.development
only related) problems?
I really don't like to add this amount of code into every store that gets created out there. Could you elaborate why this change is necessary and maybe discuss another way of fixing those issues?
From what I can tell in the moment this looks like an implemenation issue we have with the current subscribers implementation, right? If so, could we try to fix those instead?
Thanks 🌹
0075584
to
bd65b2e
Compare
@tvdeyen thanks for the early heads up!
I'll defer to the section below for explaining how this is necessary for the current subscriber implementation ( Dir.glob(Rails.root.join('app/subscribers/*_subcriber.rb)) do |path|
Spree::Event.register_subscriber(path)
end Alternatively we can also define the glob in a configuration, e.g.
That's correct, namely the problem is with AS::Notifications, although, that's likely something that will be needed for any other subscriber implementation that organizes subscribers in different classes/modules across different files (which seems a pretty standard way of doing it). And allowing an implementation to provide a list of constant-names to be re-subscribed at each reload seems a general-enough api. To better exemplify the problem I'll write some psedo-code that will hopefully make it clear for anyone not super-familiar with rails reloading, the following is based on the current implementation / examples. I apologize if this is known stuff, but I thought it might be a useful refresher for some 🙂 Scenario 1: loading subscribers with an initializerThe app starts and subscribes all subscribers # config/initializers/spree_event_subscribers.rb
OrderSubscriber.subscribe! # Rails will load app/subscribers/order_subscriber.rb
PaymentSubscriber.subscribe! # Rails will load app/subscribers/payment_subscriber.rb Rails will load the missing constants in this way unless const_defined? :OrderSubscriber
load 'app/subscribers/order_subscriber.rb'
mark_as_autoloaded :OrderSubscriber, 'app/subscribers/order_subscriber.rb'
p OrderSubscriber # => #<Module:123>
end AS::Notification will store the contents of AS::Notification.subscribers << OrderSubscriber # that's an instance of Module: #<Module:123> The developer changes the contents of app/subscribers/order_subscriber.rb if file_changed? 'app/subscribers/order_subscriber.rb'
remove_const :OrderSubscriber
end
# at this point AS::Notification still holds a reference to the old version of OrderSubscriber
p AS::Notification.subscribers # => [#<Module:123>, …] The new version of OrderSubscriber is never loaded since the constant is not referenced anywhere except in the initializer Scenario 2: loading subscribers with
|
7da8058
to
6a8ee25
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @elia. I think that this PR is way better now. I left a comment about moving some changes into another PR, let me know!
# Load application's model / class decorators | ||
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c| | ||
require_dependency(c) | ||
application <<-RUBY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with keeping these changes (both commits for this file) but maybe now it's better to move them into a separate PR. They don't seem to be related to this one anymore, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ✅
6a8ee25
to
036868c
Compare
core/lib/spree/core/engine.rb
Outdated
initializer 'spree.core.subscribe_event_mailer_processor' do | ||
Spree::Event::Processors::MailerProcessor.subscribe! | ||
# Setup Event Subscribers | ||
config.paths.add "app/subscribers", eager_load: true, glob: "**/*_subscriber.rb" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@elia does this apply only to app/subscribers
of Solidus core or it will add to the path also subscribers defined into extensions and host applications?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kennyadsl good call, after reading a bunch of rails code and some trial and error I verified I misinterpreted how paths worked. I'll push an update that fixes the behavior for both engine and app code.
036868c
to
fcd452d
Compare
We are discussing this IRL and we decided to try to use an approach similar to calculators so we'll probably end up having a list of subscribers classes into a preference like spree.config.event.subscribers = [
Spree::Core::Event::MailSubscriber,
SpreeExtensions::AnotherSubscriber,
MyApp::YetAnotherSubscriber
] This looks like a good solution for a couple of reasons:
|
Since it's including Spree::Event::Subscriber makes sense to use the "Subscriber" suffix for the constant name.
To allow firing them as Symbols.
fcd452d
to
1847dbb
Compare
Before this change, editing an instance of Spree::Event::Subscriber would result in not being reloaded or being subscribed twice. With this change each includer of Event::Subscriber is registered by its name and later unsubscribed just before class unload (using old module instance), then resubscribed after the new code is loaded.
1847dbb
to
64ce0d8
Compare
@kennyadsl @tvdeyen ready for a new (final?) pass 🔍🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍Now it looks perfect to me. Thanks, @elia!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work, Elia! Thanks for rethinking the first approach and implementing the alternative, what I consider a great solution to that problem 🍨
Description
Before this change, editing an instance of Spree::Event::Subscriber would
result in not being reloaded or being subscribed twice.
With this change each includer of Event::Subscriber is registered by its name
and later unsubscribed just before class unload (using old module instance),
then resubscribed after the new code is loaded.
Credit: This work has been done together with the estimable @AlessioRocco
Checklist: