Skip to content
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

Revert legacy event compat support removal #95

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/solidus_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'solidus_support/version'
require 'solidus_support/deprecator'
require 'solidus_support/migration'
require 'solidus_support/legacy_event_compat'
require 'solidus_support/engine_extensions'
require 'solidus_core'

Expand Down
19 changes: 19 additions & 0 deletions lib/solidus_support/engine_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ def self.included(engine)
module ClassMethods
def activate
load_solidus_decorators_from(solidus_decorators_root)
load_solidus_subscribers_from(solidus_subscribers_root)
end

# Loads Solidus event subscriber files.
#
# This allows to add event subscribers to extensions without explicitly subscribing them,
# similarly to what happens in Solidus core.
def load_solidus_subscribers_from(path)
if SolidusSupport::LegacyEventCompat.using_legacy?
path.glob("**/*_subscriber.rb") do |subscriber_path|
require_dependency(subscriber_path)
end

if Spree::Event.respond_to?(:activate_all_subscribers)
Spree::Event.activate_all_subscribers
else
Spree::Event.subscribers.each(&:subscribe!)
end
end
end

# Loads decorator files.
Expand Down
47 changes: 47 additions & 0 deletions lib/solidus_support/legacy_event_compat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require 'solidus_support/legacy_event_compat/bus'
require 'solidus_support/legacy_event_compat/subscriber'

module SolidusSupport
# Compatibility middleman for {Spree::Event} and {Spree::Bus}
#
# Solidus v3.2 changed to use [Omnes](https://github.com/nebulab/omnes) as the
# backbone for event-driven behavior (see {Spree::Bus}) by default. Before
# that, a custom adapter based on {ActiveSupport::Notifications} was used (see
# {Spree::Event}. Both systems are still supported on v3.2.
#
# This module provides compatibility support so that extensions can easily
# target both systems regardless of the underlying circumstances:
#
# - Solidus v3.2 with the new system.
# - Solidus v3.2 with the legacy system.
# - Solidus v2.9 to v3.1, when only {Spree::Event} existed.
# - Possible future versions of Solidus, whether the legacy system is
# eventually removed or not.
module LegacyEventCompat
# Returns whether the application is using the legacy event system
#
# @return [Boolean]
def self.using_legacy?
legacy_present? &&
(legacy_alone? ||
legacy_chosen?)
end

def self.legacy_present?
defined?(Spree::Event)
end
private_class_method :legacy_present?

def self.legacy_alone?
!Spree::Config.respond_to?(:use_legacy_events)
end
private_class_method :legacy_alone?

def self.legacy_chosen?
Spree::Config.use_legacy_events
end
private_class_method :legacy_chosen?
end
end
34 changes: 34 additions & 0 deletions lib/solidus_support/legacy_event_compat/bus.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module SolidusSupport
module LegacyEventCompat
# Compatibility for some event-driven operations
module Bus
# Publication of an event
#
# If extensions want to support the legacy sytem, they need to use a
# compatible API. That means it's not possible to publish an instance as
# event, which is something supported by Omnes but not the legacy adapter.
# Instead, a payload can be given. E.g.:
#
# ```
# SolidusSupport::LegacyEventCompat::Bus.publish(:foo, bar: :baz)
# ```
#
# Legacy subscribers will receive an
# `ActiveSupport::Notifications::Fanout`, while omnes subscribers will get
# an `Omnes::UnstructuredEvent`. Both instances are compatible as they
# implement a `#payload` method.
#
# @param event_name [Symbol]
# @param payload [Hash<Symbol, Any>]
def self.publish(event_name, **payload)
if SolidusSupport::LegacyEventCompat.using_legacy?
Spree::Event.fire(event_name, payload)
else
Spree::Bus.publish(event_name, **payload, caller_location: caller_locations(1)[0])
end
end
end
end
end
54 changes: 54 additions & 0 deletions lib/solidus_support/legacy_event_compat/subscriber.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

begin
require "omnes"
rescue LoadError
end

module SolidusSupport
module LegacyEventCompat
# Compatibility for subscriber modules
#
# Thanks to this module, extensions can create legacy subscriber modules
# (see {Spree::Event::Subscriber}) and translate them automatically to an
# {Omnes::Subscriber}). E.g.:
#
# ```
# module MyExtension
# module MySubscriber
# include Spree::Event::Subscriber
# include SolidusSupport::LegacyEventCompat::Subscriber
#
# event_action :order_finalized
#
# def order_finalized(event)
# event.payload[:order].do_something
# end
# end
# end
#
# MyExtension::MySubscriber.omnes_subscriber.subscribe_to(Spree::Bus)
# ```
#
# The generated omnes subscriptions will call the corresponding legacy
# subscriber method with the omnes event. It'll compatible as long as the
# omnes event responds to the `#payload` method (see
# {Omnes::UnstructuredEvent}).
module Subscriber
# @api private
ADAPTER = lambda do |legacy_subscriber, legacy_subscriber_method, _omnes_subscriber, omnes_event|
legacy_subscriber.send(legacy_subscriber_method, omnes_event)
end

def self.included(legacy_subscriber)
legacy_subscriber.define_singleton_method(:omnes_subscriber) do
@omnes_subscriber ||= Class.new.include(::Omnes::Subscriber).tap do |subscriber|
legacy_subscriber.event_actions.each do |(legacy_subscriber_method, event_name)|
subscriber.handle(event_name.to_sym, with: ADAPTER.curry[legacy_subscriber, legacy_subscriber_method])
end
end.new
end
end
end
end
end
31 changes: 31 additions & 0 deletions spec/solidus_support/legacy_event_compat/bus_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

RSpec.describe SolidusSupport::LegacyEventCompat::Bus do
describe '#publish' do
if SolidusSupport::LegacyEventCompat.using_legacy?
it 'forwards to Spree::Event' do
box = nil
subscription = Spree::Event.subscribe(:foo) { |event| box = event.payload[:bar] }

described_class.publish(:foo, bar: :baz)

expect(box).to be(:baz)
ensure
Spree::Event.unsubscribe(subscription)
end
else
it 'forwards to Spree::Bus' do
box = nil
Spree::Bus.register(:foo)
subscription = Spree::Bus.subscribe(:foo) { |event| box = event.payload[:bar] }

described_class.publish(:foo, bar: :baz)

expect(box).to be(:baz)
ensure
Spree::Bus.unsubscribe(subscription)
Spree::Bus.registry.unregister(:foo)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# frozen_string_literal: true

require 'omnes'

RSpec.describe SolidusSupport::LegacyEventCompat::Subscriber, if: Spree.solidus_version < Gem::Version.new("4.0.0.dev") do
subject { Module.new.include(Spree::Event::Subscriber).include(described_class) }

describe '#omnes_subscriber' do
it 'returns an Omnes::Subscriber' do
subject.module_eval do
event_action :foo

def foo(_event); end
end

expect(subject.omnes_subscriber.is_a?(Omnes::Subscriber)).to be(true)
end

it 'adds single-event definitions matching legacy event actions' do
subject.module_eval do
event_action :foo

def foo(_event); end
end
bus = Omnes::Bus.new
bus.register(:foo)

subscriptions = subject.omnes_subscriber.subscribe_to(bus)

event = Struct.new(:omnes_event_name).new(:foo)
expect(subscriptions.first.matches?(event)).to be(true)
end

it 'coerces event names given as Strings' do
subject.module_eval do
event_action 'foo'

def foo(_event); end
end
bus = Omnes::Bus.new
bus.register(:foo)

subscriptions = subject.omnes_subscriber.subscribe_to(bus)

event = Struct.new(:omnes_event_name).new(:foo)
expect(subscriptions.first.matches?(event)).to be(true)
end

it 'executes legacy event action methods as handlers with the omnes event' do
subject.module_eval do
event_action :foo

def foo(event)
event[:bar]
end
end
bus = Omnes::Bus.new
bus.register(:foo)

subscriptions = subject.omnes_subscriber.subscribe_to(bus)

expect(
bus.publish(:foo, bar: :baz).executions.first.result
).to be(:baz)
end

it 'distingish when event name is given explicitly' do
subject.module_eval do
event_action :foo, event_name: :bar

def foo(_event)
:bar
end
end
bus = Omnes::Bus.new
bus.register(:bar)

subscriptions = subject.omnes_subscriber.subscribe_to(bus)

expect(
bus.publish(:bar).executions.first.result
).to be(:bar)
end

it "returns the same omnes subscriber instance if called again" do
expect(subject.omnes_subscriber).to be(subject.omnes_subscriber)
end

it "doesn't fail when no event action has been defined" do
expect { subject.omnes_subscriber }.not_to raise_error
end
end
end