palkan / active_event_store

Rails Event Store in a more Rails way
MIT License
181 stars 13 forks source link

It is currently not possible to use classes that inherit from `Class` as async subscribers. #8

Closed caws closed 2 years ago

caws commented 2 years ago

Is your feature request related to a problem? Please describe.

When setting up my classes as subscribers (example below) like RES allows, I noticed it is not possible to use classes that inherit from Class as subscribers.

The issue is that when dispatching an event to a subscriber, SubscriberJob expects subscriber to answer to #call directly.

And it is not possible to use an instance as an async subscriber because callable must respond to #name.

This closes the door for class setups like the following (using DI):

  class ProfileCompletedClassWithInstanceMethodSubscriber
    def initialize(some_nice_thing: some_nice_thing)
     @some_nice_thing = some_nice_thing
    end

    def call(event)
      puts "It works from #call as an instance method!!"
      @some_nice_thing.call("It works from #call as an instance method!!")
    end
  end

Describe the solution you'd like

I expected to be able to use classes like the following as async subscribers.

  class ProfileCompletedClassWithInstanceMethodSubscriber
    def initialize(some_nice_thing: some_nice_thing)
     @some_nice_thing = some_nice_thing
    end

    def call(event)
      puts "It works from #call as an instance method!!"
      @some_nice_thing.call("It works from #call as an instance method!!")
    end
  end

Describe alternatives you've considered

I have considered adding the #name method to my classes to get past the fact that callable must respond to :name, it still does not work because of the fact that the method #call is only available as an instance method,

Additional context

The issue can be checked using the following initializer:

#initializers/active_event_store.rb
class ProfileCompleted < ActiveEventStore::Event
  attributes :user_id
end

class PublishProfileCompleted
  def initialize(active_event_store: ActiveEventStore)
    @active_event_store = active_event_store
  end

  def call(event: ProfileCompleted.new(user_id: 123456))
    @active_event_store.publish(event)
  end
end

ActiveSupport.on_load :active_event_store do |store|
  module ProfileCompletedModuleSubscriber
    class << self
      def call(event)
        puts 'It works from #call as part of a module!!'
      end
    end
  end
  class ProfileCompletedClassWithInstanceMethodSubscriber
    def call(event)
      puts "It works from #call as an instance method!!"
    end
  end

  store.subscribe ProfileCompletedModuleSubscriber, to: ProfileCompleted, sync: false
  store.subscribe ProfileCompletedClassWithInstanceMethodSubscriber, to: ProfileCompleted, sync: false
end

Which will raise NoMethodError when the ProfileCompleted event gets published (Use PublishProfileCompleted.new.call to test).

PS: Maybe this is a very particular need of mine, but hopefully it may help other people.

palkan commented 2 years ago

Closed by #9