Skip to content

Commit

Permalink
Dynamically (re)load event subscribers
Browse files Browse the repository at this point in the history
Until now, adding subscribers to Spree::Event.subscribers was
developers' responsibility.

This commit aims at automating the task by making Rails load
subscriber files located in standard paths both in Solidus core
and in the store app during the Rails initialization processs.

When each file is required, it's now also added to the
`Spree::Event.subscribers` list.

Solidus core event subscribers are always loaded automatically,
while the ones included in the store app can be loaded manually
when the preference Spree::Config.events.autoload_subscribers
is changed to a falsey value.

A similar feature will be added to `solidus_support` in order
to automatically subscribe subscribers from extensions.
  • Loading branch information
spaghetticode committed May 29, 2020
1 parent c964e5d commit 6ad36d3
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 3 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
4 changes: 1 addition & 3 deletions core/lib/spree/event/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ 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, :autoload_subscribers
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

0 comments on commit 6ad36d3

Please sign in to comment.