Skip to content

Commit

Permalink
Merge pull request #3571 from nebulab/spaghetticode/reload-subscribers
Browse files Browse the repository at this point in the history
[Events] Add subscribers automatically
  • Loading branch information
spaghetticode authored Jun 26, 2020
2 parents fa7aa92 + d617740 commit 08f6301
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 8 deletions.
1 change: 1 addition & 0 deletions core/lib/spree/core/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Engine < ::Rails::Engine
# Setup Event Subscribers
initializer 'spree.core.initialize_subscribers' do |app|
app.reloader.to_prepare do
Spree::Event.require_subscriber_files
Spree::Event.subscribers.each(&:subscribe!)
end

Expand Down
24 changes: 24 additions & 0 deletions core/lib/spree/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ def fire(event_name, opts = {})
end
end

# Loads all Solidus' core and application's event subscribers files.
# The latter are loaded automatically only when the preference
# Spree::Config.events.autoload_subscribers is set to a truthy value.
#
# Files must be placed under the directory `app/subscribers` and their
# name must end with `_subscriber.rb`.
#
# Loading the files has the side effect of adding their module to the
# list in Spree::Event.subscribers.
def require_subscriber_files
pattern = "app/subscribers/**/*_subscriber.rb"

# Load Solidus subscribers
# rubocop:disable Rails/DynamicFindBy
solidus_core_dir = Gem::Specification.find_by_name('solidus_core').gem_dir
# rubocop:enable Rails/DynamicFindBy
Dir.glob(File.join(solidus_core_dir, pattern)) { |c| require_dependency(c.to_s) }

# Load application subscribers, only when the flag is set to true:
if Spree::Config.events.autoload_subscribers
Rails.root.glob(pattern) { |c| require_dependency(c.to_s) }
end
end

# Subscribe to an event with the given name. The provided block is executed
# every time the subscribed event is fired.
#
Expand Down
10 changes: 6 additions & 4 deletions core/lib/spree/event/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ module Spree
module Event
class Configuration
def subscribers
@subscribers ||= ::Spree::Core::ClassConstantizer::Set.new.tap do |set|
set << 'Spree::MailerSubscriber'
end
@subscribers ||= ::Spree::Core::ClassConstantizer::Set.new
end

attr_writer :adapter, :suffix
attr_writer :adapter, :suffix, :autoload_subscribers

def autoload_subscribers
@autoload_subscribers.nil? ? true : !!@autoload_subscribers
end

def adapter
@adapter ||= Spree::Event::Adapters::ActiveSupportNotifications
Expand Down
2 changes: 2 additions & 0 deletions core/lib/spree/event/subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def self.included(base)

base.mattr_accessor :event_actions
base.event_actions = {}

Spree::Event.subscribers << base.name
end

# Declares a method name in the including module that can be subscribed/unsubscribed
Expand Down
48 changes: 48 additions & 0 deletions core/spec/lib/spree/event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
allow(subscriber_name).to receive(:to_s).and_return(subscriber_name)
end

after { described_class.subscribers.clear }

it 'accepts the names of constants' do
Spree::Config.events.subscribers << subscriber_name

Expand All @@ -133,4 +135,50 @@
expect(described_class.subscribers.to_a).to eq([subscriber])
end
end

describe '.require_subscriber_files' do
let(:susbcribers_dir) { Rails.root.join('app', 'subscribers', 'spree') }

def create_subscriber_file(constant_name)
FileUtils.mkdir_p(susbcribers_dir)
File.open File.join(susbcribers_dir, "#{constant_name.underscore}.rb"), 'w' do |f|
f.puts "module Spree::#{constant_name}; include Spree::Event::Subscriber; end"
end
end

after { FileUtils.rm_rf(susbcribers_dir) }

context 'when Spree::Config.events.autoload_subscribers is true (default)' do
let(:events_config) { double(autoload_subscribers: true, subscribers: Set.new) }

before { create_subscriber_file('FooSubscriber') }

it 'requires subscriber files and loads them into Spree::Event.subscribers' do
expect do
described_class.require_subscriber_files
end.to change { described_class.subscribers.count }.by 1

expect(defined? Spree::FooSubscriber).to be_truthy
expect(described_class.subscribers).to include(Spree::FooSubscriber)
end
end

context 'when Spree::Config.autoload_subscribers is false' do
let(:events_config) { double(autoload_subscribers: false, subscribers: Set.new) }

before do
stub_spree_preferences(events: events_config)
create_subscriber_file('BarSubscriber')
end

it 'does not requires subscriber files' do
expect do
described_class.require_subscriber_files
end.not_to change { described_class.subscribers.count }

expect(defined? Spree::BarSubscriber).to be_falsey
expect(described_class.subscribers).to be_empty
end
end
end
end
16 changes: 12 additions & 4 deletions guides/source/developers/events/overview.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,18 @@ method allows to map a method of the subscriber module to an event that
happens in the system. If the `event_name` argument is not specified,
the event name and the method name should match.

These subscribers modules are loaded with the rest of your application but
you need to manually subscribe to them.

For example, you could subscribe to them programmatically with something like:
These subscriber modules are loaded with the rest of your application, you
don't need to manually subscribe them when:

* `Spree::Config.events.autoload_subscribers` returns a truthy value (default);
* you put them in the directory (or any subdirectory of) `app/subscribers`;
* their filename ends with `_subscriber.rb`;

On the other hand, if you need to resort to manual subscription because you did
not follow the naming convention explained above, or you prefer to have complete
control on what is loaded and when, you can override the default behaviour by
setting `Spree::Config.events.autoload_subscribers = false` in a initializer.
At that point you can subscribe your event subscribers, with something similar to:

```ruby
if defined?(SmsLibrary)
Expand Down

0 comments on commit 08f6301

Please sign in to comment.