diff --git a/core/lib/spree/core/engine.rb b/core/lib/spree/core/engine.rb index d7cf4a4e658..c59628de6b0 100644 --- a/core/lib/spree/core/engine.rb +++ b/core/lib/spree/core/engine.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require 'spree/config' +require 'spree/event' +require 'spree/event/adapters/deprecation_handler' module Spree module Core @@ -44,6 +46,18 @@ class Engine < ::Rails::Engine Migrations.new(config, engine_name).check end + # Register core events + initializer 'spree.core.register_events' do + unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter? + %w[ + order_finalized + order_recalculated + reimbursement_reimbursed + reimbursement_errored + ].each { |event_name| Spree::Event.register(event_name) } + end + end + # Setup Event Subscribers initializer 'spree.core.initialize_subscribers' do |app| app.reloader.to_prepare do diff --git a/core/lib/spree/event.rb b/core/lib/spree/event.rb index f017a60f9df..765c5a7fa56 100644 --- a/core/lib/spree/event.rb +++ b/core/lib/spree/event.rb @@ -5,6 +5,7 @@ require_relative 'event/listener' require_relative 'event/subscriber_registry' require_relative 'event/subscriber' +require 'spree/deprecation' module Spree # Event bus for Solidus. @@ -13,6 +14,12 @@ module Spree # Solidus. You can use different underlying adapters to provide the core # logic. It's recommended that you use {Spree::Event::Adapters::Default}. # + # Before firing, subscribing, or unsubscribing an event, you need to + # {#register} it: + # + # @example + # Spree::Event.register 'order_finalized' + # # You use the {#fire} method to trigger an event: # # @example @@ -43,6 +50,33 @@ module Event delegate :activate_autoloadable_subscribers, :activate_all_subscribers, :deactivate_all_subscribers, to: :subscriber_registry + # Registers an event + # + # This step is needed before firing, subscribing or unsubscribing an + # event. It helps to prevent typos and naming collision. + # + # This method is not available in the legacy adapter. + # + # @example + # Spree::Event.register('foo') + # + # @param [String, Symbol] event_name + # @param [Any] adapter the event bus adapter to use. + def register(event_name, adapter: default_adapter) + warn_registration_on_legacy_adapter if deprecation_handler.render_deprecation_message?(adapter) + return if deprecation_handler.legacy_adapter?(adapter) + + adapter.register(normalize_name(event_name), caller_location: caller_locations(1)[0]) + end + + # @api private + def registry(adapter: default_adapter) + warn_registration_on_legacy_adapter if deprecation_handler.render_deprecation_message?(adapter) + return if deprecation_handler.legacy_adapter?(adapter) + + adapter.registry + end + # Allows to trigger events that can be subscribed using {#subscribe}. # # The actual code implementation is delegated to the adapter. @@ -253,6 +287,13 @@ def handle_block_on_fire(block, opts, adapter) end end + def warn_registration_on_legacy_adapter + Spree::Deprecation.warn <<~MSG + Event registration works only on the new adapter + `Spree::Event::Adapters::Default`. Please, update to it. + MSG + end + def deprecation_handler Adapters::DeprecationHandler end diff --git a/core/lib/spree/event/adapters/default.rb b/core/lib/spree/event/adapters/default.rb index dec319219a8..886afd2f51a 100644 --- a/core/lib/spree/event/adapters/default.rb +++ b/core/lib/spree/event/adapters/default.rb @@ -3,6 +3,7 @@ require 'spree/event/event' require 'spree/event/listener' require 'spree/event/firing' +require 'spree/event/registry' module Spree module Event @@ -26,14 +27,21 @@ module Adapters # be the default one. class Default # @api private - attr_reader :listeners + attr_reader :listeners, :registry - def initialize(listeners = []) + def initialize(listeners = [], registry = Registry.new) @listeners = listeners + @registry = registry + end + + # @api private + def register(event_name, caller_location: caller_locations(1)[0]) + registry.register(event_name, caller_location: caller_location) end # @api private def fire(event_name, caller_location: caller_locations(1)[0], **payload) + registry.check_event_name_registered(event_name) event = Event.new(payload: payload, caller_location: caller_location) executions = listeners_for_event(event_name).map do |listener| listener.call(event) @@ -43,6 +51,7 @@ def fire(event_name, caller_location: caller_locations(1)[0], **payload) # @api private def subscribe(event_name_or_regexp, &block) + registry.check_event_name_registered(event_name_or_regexp) if event_name?(event_name_or_regexp) Listener.new(pattern: event_name_or_regexp, block: block).tap do |listener| @listeners << listener end @@ -53,13 +62,14 @@ def unsubscribe(subscriber_or_event_name) if subscriber_or_event_name.is_a?(Listener) unsubscribe_listener(subscriber_or_event_name) else + registry.check_event_name_registered(subscriber_or_event_name) if event_name?(subscriber_or_event_name) unsubscribe_event(subscriber_or_event_name) end end # @api private def with_listeners(listeners) - self.class.new(listeners) + self.class.new(listeners, registry) end private @@ -79,6 +89,10 @@ def unsubscribe_event(event_name) listener.unsubscribe(event_name) end end + + def event_name?(candidate) + candidate.is_a?(String) + end end end end diff --git a/core/lib/spree/event/configuration.rb b/core/lib/spree/event/configuration.rb index 2bc7b8114e4..82a48135d5d 100644 --- a/core/lib/spree/event/configuration.rb +++ b/core/lib/spree/event/configuration.rb @@ -34,7 +34,7 @@ def adapter That will be the new default on Solidus 4. - Take into account there're two critical changes in behavior in the new adapter: + Take into account there're three critical changes in behavior in the new adapter: - Event names are no longer automatically suffixed with `.spree`, as they're no longer in the same bucket that Rails's ones. So, for @@ -59,6 +59,15 @@ def adapter order.do_something Spree::Event.fire 'event_name', order: order + - You need to register your custom events before firing or subscribing + to them (not necessary for events provided by Solidus or other + extensions). It should be done at the end of the `spree.rb` + initializer. Example: + + Spree::Event.register('foo') + Spree::Event.fire('foo') + + MSG end end diff --git a/core/lib/spree/event/registry.rb b/core/lib/spree/event/registry.rb new file mode 100644 index 00000000000..110c31e10ef --- /dev/null +++ b/core/lib/spree/event/registry.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +module Spree + module Event + # Registry of known events + # + # @api privte + class Registry + class Registration + attr_reader :event_name, :caller_location + + def initialize(event_name:, caller_location:) + @event_name = event_name + @caller_location = caller_location + end + end + + attr_reader :registrations + + def initialize(registrations: []) + @registrations = registrations + end + + def register(event_name, caller_location: caller_locations(1)[0]) + registration = registration(event_name) + if registration + raise <<~MSG + Can't register #{event_name} event as it's already registered. + + The registration happened at: + + #{registration.caller_location} + MSG + else + @registrations << Registration.new(event_name: event_name, caller_location: caller_location) + end + end + + def unregister(event_name) + raise <<~MSG unless registered?(event_name) + #{event_name} is not registered. + + Known events are: + + '#{event_names.join("' '")}' + MSG + + @registrations.delete_if { |regs| regs.event_name == event_name } + end + + def registration(event_name) + registrations.find { |reg| reg.event_name == event_name } + end + + def registered?(event_name) + !registration(event_name).nil? + end + + def event_names + registrations.map(&:event_name) + end + + def check_event_name_registered(event_name) + return true if registered?(event_name) + + raise <<~MSG + '#{event_name}' is not registered as a valid event name. + #{suggestions_message(event_name)} + + All known events are: + + '#{event_names.join(" ")}' + + You can register the new events at the end of the `spree.rb` + initializer: + + Spree::Event.register('#{event_name}') + MSG + end + + private + + def suggestions(event_name) + dictionary = DidYouMean::SpellChecker.new(dictionary: event_names) + + dictionary.correct(event_name) + end + + def suggestions_message(event_name) + DidYouMean::PlainFormatter.new.message_for(suggestions(event_name)) + end + end + end +end diff --git a/core/lib/spree/event/test_interface.rb b/core/lib/spree/event/test_interface.rb index ca4d3768a34..713bcb989c0 100644 --- a/core/lib/spree/event/test_interface.rb +++ b/core/lib/spree/event/test_interface.rb @@ -72,6 +72,13 @@ def performing_only(*listeners_and_subscribers) def performing_nothing(&block) performing_only(&block) end + + # Unregisters a previously registered event + # + # @param [String, Symbol] event_name + def unregister(event_name) + registry.unregister(normalize_name(event_name)) + end end extend Methods diff --git a/core/spec/lib/spree/event/adapters/default_spec.rb b/core/spec/lib/spree/event/adapters/default_spec.rb index 08f4732c481..97538cff8d0 100644 --- a/core/spec/lib/spree/event/adapters/default_spec.rb +++ b/core/spec/lib/spree/event/adapters/default_spec.rb @@ -21,10 +21,30 @@ def inc end end + describe '#register' do + it 'adds event to the register' do + bus = described_class.new + + bus.register('foo') + + expect(bus.registry.registered?('foo')).to be(true) + end + + it 'raises when the event is already in the registry' do + bus = described_class.new + bus.register('foo', caller_location: caller_locations(0)[0]) + + expect { + bus.register('foo') + }.to raise_error(/already registered.*#{__FILE__}/m) + end + end + describe '#fire' do it 'executes listeners subscribed as a string to the event name' do bus = described_class.new dummy = counter.new + bus.register('foo') bus.subscribe('foo') { dummy.inc } bus.fire 'foo' @@ -35,6 +55,7 @@ def inc it 'executes listeners subscribed as a regexp to the event name' do bus = described_class.new dummy = counter.new + bus.register('foo') bus.subscribe(/oo/) { dummy.inc } bus.fire 'foo' @@ -45,7 +66,9 @@ def inc it "doesn't execute listeners not subscribed to the event name" do bus = described_class.new dummy = counter.new + bus.register('bar') bus.subscribe('bar') { dummy.inc } + bus.register('foo') bus.fire 'foo' @@ -55,7 +78,9 @@ def inc it "doesn't execute listeners partially matching as a string" do bus = described_class.new dummy = counter.new + bus.register('bar') bus.subscribe('bar') { dummy.inc } + bus.register('barr') bus.fire 'barr' @@ -67,6 +92,7 @@ def inc dummy = Class.new do attr_accessor :box end.new + bus.register('foo') bus.subscribe('foo') { |event| dummy.box = event.payload[:box] } bus.fire 'foo', box: 'foo' @@ -77,6 +103,7 @@ def inc it 'adds the fired event with given caller location to the firing result object' do bus = described_class.new dummy = counter.new + bus.register('foo') bus.subscribe('foo') { :work } firing = bus.fire 'foo', caller_location: caller_locations(0)[0] @@ -87,6 +114,7 @@ def inc it 'adds the triggered executions to the firing result object', :aggregate_failures do bus = described_class.new dummy = counter.new + bus.register('foo') listener1 = bus.subscribe('foo') { dummy.inc } listener2 = bus.subscribe('foo') { dummy.inc } @@ -97,11 +125,20 @@ def inc expect(executions.map(&:listener)).to match([listener1, listener2]) expect(executions.map(&:result)).to match([1, 2]) end + + it "raises when the fired event hasn't been registered" do + bus = described_class.new + + expect { + bus.fire('foo') + }.to raise_error(/not registered/) + end end describe '#subscribe' do it 'registers to matching event as string' do bus = described_class.new + bus.register('foo') block = ->{} bus.subscribe('foo', &block) @@ -121,10 +158,18 @@ def inc it 'returns a listener object with given block' do bus = described_class.new - listener = bus.subscribe('foo') { 'bar' } + listener = bus.subscribe(/foo/) { 'bar' } expect(listener.block.call).to eq('bar') end + + it "raises when given event name hasn't been registered" do + bus = described_class.new + + expect { + bus.subscribe('foo') + }.to raise_error(/not registered/) + end end describe '#unsubscribe' do @@ -132,6 +177,7 @@ def inc it 'unsubscribes given listener' do bus = described_class.new dummy = counter.new + bus.register('foo') listener = bus.subscribe('foo') { dummy.inc } bus.unsubscribe listener @@ -145,18 +191,28 @@ def inc it 'unsubscribes all listeners for that event' do bus = described_class.new dummy = counter.new - + bus.register('foo') bus.subscribe('foo') { dummy.inc } + bus.unsubscribe 'foo' bus.fire 'foo' expect(dummy.count).to be(0) end + + it "raises when given event name hasn't been registered" do + bus = described_class.new + + expect { + bus.unsubscribe('foo') + }.to raise_error(/not registered/) + end end it 'unsubscribes listeners that match event with a regexp' do bus = described_class.new dummy = counter.new + bus.register('foo') bus.subscribe(/foo/) { dummy.inc } bus.unsubscribe 'foo' @@ -168,6 +224,8 @@ def inc it "doesn't unsubscribe listeners for other events" do bus = described_class.new dummy = counter.new + bus.register('foo') + bus.register('bar') bus.subscribe('foo') { dummy.inc } bus.unsubscribe 'bar' @@ -179,6 +237,7 @@ def inc it 'can resubscribe other listeners to the same event', :aggregate_failures do bus = described_class.new dummy1, dummy2 = Array.new(2) { counter.new } + bus.register('foo') bus.subscribe('foo') { dummy1.inc } bus.unsubscribe 'foo' @@ -194,6 +253,7 @@ def inc it 'returns a new instance with given listeners', :aggregate_failures do bus = described_class.new dummy1, dummy2, dummy3 = Array.new(3) { counter.new } + bus.register('foo') listener1 = bus.subscribe('foo') { dummy1.inc } listener2 = bus.subscribe('foo') { dummy2.inc } listener3 = bus.subscribe('foo') { dummy3.inc } @@ -207,6 +267,15 @@ def inc expect(dummy2.count).to be(1) expect(dummy3.count).to be(0) end + + it 'keeps the same registry' do + bus = described_class.new + bus.register('foo') + + new_bus = bus.with_listeners([]) + + expect(new_bus.registry).to be(bus.registry) + end end end end diff --git a/core/spec/lib/spree/event/registry_spec.rb b/core/spec/lib/spree/event/registry_spec.rb new file mode 100644 index 00000000000..c65b3d79449 --- /dev/null +++ b/core/spec/lib/spree/event/registry_spec.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +require 'spree/event/registry' + +RSpec.describe Spree::Event::Registry do + describe '#register' do + it 'adds given event name to the registry' do + registry = described_class.new + + registry.register('foo') + + expect(registry.registered?('foo')).to be(true) + end + + it 'adds given caller location to the registration' do + registry = described_class.new + + registry.register('foo', caller_location: caller_locations(0)[0]) + + expect(registry.registration('foo').caller_location.to_s).to include(__FILE__) + end + + it 'raises with the registration location info when the event has already been registered' do + registry = described_class.new + + registry.register('foo', caller_location: caller_locations(0)[0]) + + expect { + registry.register('foo', caller_location: caller_locations(2)[0]) + }.to raise_error(/already registered.*#{__FILE__}/m) + end + end + + describe '#unregister' do + it 'removes given event name from the registry' do + registry = described_class.new + registry.register('foo') + + registry.unregister('foo') + + expect(registry.registered?('foo')).to be(false) + end + + it "raises when the event hasn't been registered" do + registry = described_class.new + registry.register('bar') + + expect { + registry.unregister('foo') + }.to raise_error(/not registered.*bar/m) + end + end + + describe '#registration' do + it 'finds the registration from given name' do + registry = described_class.new + + registry.register('foo') + + expect(registry.registration('foo').event_name).to eq('foo') + end + + it 'returns nil when the event name is not found' do + registry = described_class.new + + expect(registry.registration('foo')).to be_nil + end + end + + describe '#registered?' do + it 'returns true when given event name is registered' do + registry = described_class.new + registry.register('foo') + + expect(registry.registered?('foo')).to be(true) + end + + it 'returns false when given event name is not registered' do + registry = described_class.new + + expect(registry.registered?('foo')).to be(false) + end + end + + describe '#event_names' do + it 'returns array with the registered event names' do + registry = described_class.new + registry.register('foo') + registry.register('bar') + + expect(registry.event_names).to match_array(['foo', 'bar']) + end + end + + describe '#check_event_name_registered' do + it 'returns true if the event is registered' do + registry = described_class.new + registry.register('foo') + + expect(registry.check_event_name_registered('foo')).to be(true) + end + + it 'raises when the event is not registered' do + registry = described_class.new + + expect { + registry.check_event_name_registered('foo') + }.to raise_error(/not registered/) + end + + it 'includes all available events on the error message' do + registry = described_class.new + registry.register('bar') + registry.register('baz') + + expect { + registry.check_event_name_registered('foo') + }.to raise_error(/bar.*baz/) + end + + it 'hints on the event name on the error message' do + registry = described_class.new + registry.register('order_canceled') + + expect { + registry.check_event_name_registered('order_canceed') + }.to raise_error(/Did you mean\? order_canceled/) + end + end +end diff --git a/core/spec/lib/spree/event/subscriber_registry_spec.rb b/core/spec/lib/spree/event/subscriber_registry_spec.rb index 5cca82fd7ef..f04456428bf 100644 --- a/core/spec/lib/spree/event/subscriber_registry_spec.rb +++ b/core/spec/lib/spree/event/subscriber_registry_spec.rb @@ -22,6 +22,18 @@ def other_event(event) end end + unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter? + before do + Spree::Event.register(:event_name) + Spree::Event.register(:other_event) + end + + after do + Spree::Event.unregister(:event_name) + Spree::Event.unregister(:other_event) + end + end + describe "#activate_all_subscribers" do before { subject.register(N) } diff --git a/core/spec/lib/spree/event/subscriber_spec.rb b/core/spec/lib/spree/event/subscriber_spec.rb index c98ce5db678..da3f34d9004 100644 --- a/core/spec/lib/spree/event/subscriber_spec.rb +++ b/core/spec/lib/spree/event/subscriber_spec.rb @@ -8,6 +8,20 @@ require 'spree/event/listener' RSpec.describe Spree::Event::Subscriber do + is_legacy_adapter = Spree::Event::Adapters::DeprecationHandler.legacy_adapter? + + unless is_legacy_adapter + before do + Spree::Event.register(:event_name) + Spree::Event.register(:foo) + end + + after do + Spree::Event.unregister(:event_name) + Spree::Event.unregister(:foo) + end + end + module M include Spree::Event::Subscriber @@ -70,12 +84,18 @@ def other_event(event) it 'does not subscribe the action' do expect(M).not_to receive(:other_event) + Spree::Event.register('other_event') + Spree::Event.fire 'other_event' + + ensure + Spree::Event.unregister('other_event') unless is_legacy_adapter end end context 'when the action is declared' do before do + Spree::Event.register('other_event') M.event_action :other_event M.activate end @@ -83,6 +103,7 @@ def other_event(event) after do M.deactivate M.event_actions.delete(:other_event) + Spree::Event.unregister('other_event') unless is_legacy_adapter end it 'subscribe the action' do @@ -92,7 +113,7 @@ def other_event(event) end end - unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter? + unless is_legacy_adapter describe '::listeners' do before { M.activate } after { M.deactivate } diff --git a/core/spec/lib/spree/event/test_interface_spec.rb b/core/spec/lib/spree/event/test_interface_spec.rb index 9e31477491d..dffcc57cf45 100644 --- a/core/spec/lib/spree/event/test_interface_spec.rb +++ b/core/spec/lib/spree/event/test_interface_spec.rb @@ -37,6 +37,7 @@ def inc unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter? it 'can be accessed directly from TestInterface' do dummy = counter.new + Spree::Event.register('foo') listener = Spree::Event.subscribe('foo') { dummy.inc } described_class.performing_only(listener) do @@ -44,6 +45,8 @@ def inc end expect(dummy.count).to be(1) + ensure + Spree::Event.unregister('foo') end describe '#performing_only' do @@ -51,6 +54,7 @@ def inc it 'only performs given listeners for the duration of the block', :aggregate_failures do dummy1, dummy2, dummy3 = Array.new(3) { counter.new } + Spree::Event.register('foo') listener1 = Spree::Event.subscribe('foo') { dummy1.inc } listener2 = Spree::Event.subscribe('foo') { dummy2.inc } listener3 = Spree::Event.subscribe('foo') { dummy3.inc } @@ -62,10 +66,13 @@ def inc expect(dummy1.count).to be(1) expect(dummy2.count).to be(1) expect(dummy3.count).to be(0) + ensure + Spree::Event.unregister('foo') end it 'performs again all the listeners once the block is done', :aggregate_failures do dummy1, dummy2 = Array.new(2) { counter.new } + Spree::Event.register('foo') listener1 = Spree::Event.subscribe('foo') { dummy1.inc } listener2 = Spree::Event.subscribe('foo') { dummy2.inc } @@ -79,10 +86,13 @@ def inc Spree::Event.fire('foo') expect(dummy2.count).to be(1) + ensure + Spree::Event.unregister('foo') end it 'can extract listeners from a subscriber module', :aggregate_failures do dummy1, dummy2 = Array.new(2) { counter.new } + Spree::Event.register('foo') Subscriber1 = Module.new do include Spree::Event::Subscriber @@ -112,12 +122,14 @@ def foo(event) expect(dummy1.count).to be(1) expect(dummy2.count).to be(0) ensure + Spree::Event.unregister('foo') Spree::Event.subscriber_registry.deactivate_subscriber(Subscriber1) Spree::Event.subscriber_registry.deactivate_subscriber(Subscriber2) end it 'can mix listeners and array of listeners', :aggregate_failures do dummy1, dummy2 = Array.new(2) { counter.new } + Spree::Event.register('foo') listener = Spree::Event.subscribe('foo') { dummy1.inc } Subscriber = Module.new do include Spree::Event::Subscriber @@ -138,11 +150,13 @@ def foo(event) expect(dummy1.count).to be(1) expect(dummy2.count).to be(1) ensure + Spree::Event.unregister('foo') Spree::Event.subscriber_registry.deactivate_subscriber(Subscriber) end it 'can perform no listener at all' do dummy = counter.new + Spree::Event.register('foo') listener = Spree::Event.subscribe('foo') { dummy.inc } Spree::Event.performing_only do @@ -150,10 +164,13 @@ def foo(event) end expect(dummy.count).to be(0) + ensure + Spree::Event.unregister('foo') end it 'can override through an inner call' do dummy = counter.new + Spree::Event.register('foo') listener = Spree::Event.subscribe('foo') { dummy.inc } Spree::Event.performing_only do @@ -163,6 +180,8 @@ def foo(event) end expect(dummy.count).to be(1) + ensure + Spree::Event.unregister('foo') end end @@ -171,6 +190,7 @@ def foo(event) it 'performs no listener for the duration of the block' do dummy = counter.new + Spree::Event.register('foo') listener = Spree::Event.subscribe('foo') { dummy.inc } Spree::Event.performing_nothing do @@ -178,6 +198,32 @@ def foo(event) end expect(dummy.count).to be(0) + ensure + Spree::Event.unregister('foo') + end + end + + describe '#unregister_event' do + before { Spree::Event.enable_test_interface } + + it 'unregisters an event from the registry' do + Spree::Event.register('foo') + + Spree::Event.unregister('foo') + + expect { + Spree::Event.fire('foo') + }.to raise_error(/not registered/) + end + + it 'coercers names given as symbol' do + Spree::Event.register('foo') + + Spree::Event.unregister(:foo) + + expect { + Spree::Event.fire('foo') + }.to raise_error(/not registered/) end end end diff --git a/core/spec/lib/spree/event_spec.rb b/core/spec/lib/spree/event_spec.rb index 7afff24618d..2cab8b712d3 100644 --- a/core/spec/lib/spree/event_spec.rb +++ b/core/spec/lib/spree/event_spec.rb @@ -4,6 +4,7 @@ require 'spree/event/adapters/default' require 'spree/event/adapters/deprecation_handler' require 'spree/event/adapters/active_support_notifications' +require 'spree/event/registry' RSpec.describe Spree::Event do subject { described_class } @@ -40,10 +41,64 @@ def build_bus end end + describe '.register' do + it 'adds given event name to the registry' do + bus = build_bus + + subject.register('foo', adapter: bus) + + expect(subject.registry(adapter: bus).registered?('foo')).to be(true) + end + + it 'coerces event names given as symbols' do + bus = build_bus + + subject.register(:foo, adapter: bus) + + expect(subject.registry(adapter: bus).registered?('foo')).to be(true) + end + + it 'raises with caller location when the event has already been registered' do + bus = build_bus + subject.register(:foo, adapter: bus) + + expect { + subject.register(:foo, adapter: bus) + }.to raise_error(/already registered.*#{__FILE__}/m) + end + + unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter_set_by_env + it 'raises when the adapter is ActiveSupportNotifications' do + expect(Spree::Deprecation).to receive(:warn).with(/only on the new adapter/) + + subject.register('foo', adapter: Spree::Event::Adapters::ActiveSupportNotifications) + end + end + end + + describe '#registry' do + it 'forwards to adapter' do + bus = build_bus + + registry = subject.registry(adapter: bus) + + expect(registry).to be_a(Spree::Event::Registry) + end + + unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter_set_by_env + it 'warns when the adapter is ActiveSupportNotifications' do + expect(Spree::Deprecation).to receive(:warn).with(/only on the new adapter/) + + subject.registry(adapter: Spree::Event::Adapters::ActiveSupportNotifications) + end + end + end + describe '.fire' do it 'forwards to adapter', :aggregate_failures do bus = build_bus dummy = counter.new + subject.register('foo', adapter: bus) subject.subscribe('foo', adapter: bus) { dummy.inc } firing = subject.fire 'foo', adapter: bus @@ -55,6 +110,7 @@ def build_bus it 'coerces event names given as symbols' do bus = build_bus dummy = counter.new + subject.register('foo', adapter: bus) subject.subscribe('foo', adapter: bus) { dummy.inc } subject.fire :foo, adapter: bus @@ -63,8 +119,11 @@ def build_bus end it 'raises error if a block is given and the adapter is not ActiveSupportNotifications' do + bus = build_bus + subject.register('foo', adapter: bus) + expect do - subject.fire :foo, adapter: build_bus do + subject.fire :foo, adapter: bus do 1 + 1 end end.to raise_error(ArgumentError, /Blocks.*are ignored/) @@ -86,17 +145,25 @@ def build_bus it "provides caller location to the event" do bus = build_bus + subject.register('foo', adapter: bus) subject.subscribe('foo', adapter: bus) { :work } firing = subject.fire 'foo', adapter: bus expect(firing.event.caller_location.to_s).to include(__FILE__) end + + it 'raises error if the event is not registerd' do + bus = build_bus + + expect { subject.fire('baz', adapter: bus) }.to raise_error(/'baz' is not registered/m) + end end describe '.subscribe' do it 'forwards to adapter' do bus = build_bus + subject.register('foo', adapter: bus) listener = subject.subscribe('foo', adapter: bus) {} @@ -105,6 +172,7 @@ def build_bus it 'coerces event names given as symbols' do bus = build_bus + subject.register('foo', adapter: bus) listener = subject.subscribe(:foo, adapter: bus) {} @@ -116,6 +184,7 @@ def build_bus it 'delegates to the adapter' do bus = build_bus dummy = counter.new + subject.register('foo', adapter: bus) listener = subject.subscribe('foo', adapter: bus) { dummy.inc } subject.unsubscribe listener, adapter: bus @@ -127,6 +196,7 @@ def build_bus it 'coerces event names given as symbols' do bus = build_bus dummy = counter.new + subject.register('foo', adapter: bus) subject.subscribe('foo', adapter: bus) { dummy.inc } subject.unsubscribe :foo, adapter: bus @@ -139,6 +209,8 @@ def build_bus describe '#listeners' do it 'returns mapping of all listeners by event name' do bus = build_bus + subject.register('foo', adapter: bus) + subject.register('bar', adapter: bus) listener_foo = subject.subscribe('foo', adapter: bus) {} listener_bar = subject.subscribe('bar', adapter: bus) {} diff --git a/core/spec/rails_helper.rb b/core/spec/rails_helper.rb index 226c9633f5d..2da8e4b28da 100644 --- a/core/spec/rails_helper.rb +++ b/core/spec/rails_helper.rb @@ -9,6 +9,10 @@ gem_root: File.expand_path('..', __dir__), lib_name: 'solidus_core' ) +unless Spree::Event::Adapters::DeprecationHandler.legacy_adapter? + require 'spree/event/test_interface' + Spree::Event.enable_test_interface +end require 'rspec/rails' require 'rspec-activemodel-mocks'