Triggerful Ruby Gem: create dynamic callbacks to methods The main purpose of Ruby is to be readable. I hope I did a good job with this gem I made. If there's any kind of suggestion of how to make this better, then please tell me.

class Trigger
  def initialize event, *callbacks
    @callbacks = callbacks
    @event = event

    if @callbacks[0].is_a? TrueClass
      @progression = true
      @callbacks.delete_at(0)
    elsif @callbacks[0].is_a? FalseClass
      @progression = false
      @callbacks.delete_at(0)
    else
      @progression = false
    end
  end

  def trigger(*args)
    case @event
    when Proc
      event_data = @event.call
    when Method
      event_data = @event.call
    else
      event_data = self.method(@event).call(*args)
    end
    @callbacks.each do |callback|
      if callback.instance_of? Trigger
        if @progression
          callback.trigger(*args, event_data)
        else
          callback.trigger(*args)
        end
      else
        case callback
        when Proc
          if @progression
            callback.call(*args, event_data)
          else
            callback.call(*args)
          end
        when Method
          if @progression
            callback.call(*args, event_data)
          else
            callback.call(*args)
          end
        else
          if @progression
            method(callback).call(*args, event_data)
          else
            method(callback).call(*args)
          end
        end
      end
    end
  end

  #triggers the callbacks without executing the original method
  def silent_trigger(*args)
    @callbacks.each do |callback|
      if callback.instance_of? Trigger
        callback.trigger(*args)
      else
        case callback
        when Proc
          callback.call
        when Method
          callback.call
        else
          method(callback).call(*args)
        end
      end
    end
  end

  # add callback(s) to instance
  def add(*callbacks)
    @callbacks.concat callbacks
  end

  def insert(index, *callbacks)
    @callbacks.insert(index, callbacks)
  end

  # remove callback(s) from instance
  def remove(*callbacks)
    callbacks.each do |callback|
      @callbacks.delete_at(@callbacks.index(callback) || @callbacks.length)
    end
  end

  def delete_at(index)
    @callbacks.delete_at(index)
  end

  def remove_all
    @callbacks = []
  end

  # fetch info from instance
  def index(callback)
    @callbacks.index(callback)
  end

  def event_name
    @event
  end

  def list
    @callbacks
  end
end


And if you want to try it out, here's a quick little console program to show you how it works:

$foobar = 0

def foo
  $foobar += 1
end

def bar
  puts $foobar
end

# create Trigger and callback, then trigger the Trigger
_foo = Trigger.new(:foo, :bar)
_foo.trigger

# create a method to determine if bar has been called before
$bar_called? = false
def bar_called
  $bar_called = true
end

# create a callback for the callback
_bar = Trigger.new(:bar, :bar_called)

# replace old callback with new one (note: you can use Methods, Procs, Symbols, Strings, or other Triggers too)
_foo.remove(:bar)
_foo.add(_bar)

# methods are not called when added to Trigger callbacks, only when triggered
puts $bar_called?
_foo.trigger
puts $bar_called?


You can find the gem here.
1) get rid of conditionals

I think your main problem here is the nested conditionals that litter your code. This increases complexity and tends to be less readable.

You can get rid of those conditionals using a method like this :

def make_callable(object)
  case object
  when Proc, Method then object
  when Trigger      then -&gt;(*args){ object.trigger(*args) }
  else                   -&gt;(*args){ public_send object, *args }
  end
end


so you can do things like : 

@event = make_callable(event)
@callbacks = callbacks.map{ |c| make_callable c }


This way, all your callbacks will respond to call uniformly, so you won't need conditionals anymore.

2) use inheritance

As i see it, your @progression instance variable masks the need for two different behaviors, which means two different classes : a "silent" trigger, and a "verbose" one that extends the former.

class Trigger

  # factory method to instantiate the right type of callback.
  # I slightly changed the signature from the original #initialize
  # as I thought it would make more sense this way, 
  # but it is possible to keep the original one with minor tweaks
  #
  def self.factory(verbose, event, *callbacks)
    verbose ? Verbose.new(event, *callbacks) : new(event, *callbacks)
  end

  def initialize(event, *callbacks)
    @event = make_callable(event)
    @callbacks = callbacks.map{ |c| make_callable c }
  end

  # SNIP : this class would also expose add_callback, remove_callback, etc.

  def trigger(*args)
    @callbacks.each{ |c| c.call(*args) }
  end

  private 

  def make_callable(object)
    case object
    when Proc, Method then object
    when Trigger      then -&gt;(*args){ object.trigger(*args) }
    else                   -&gt;(*args){ public_send object, *args }
    end
  end
end

class Trigger::Verbose &lt; Trigger
  def trigger(*args)
    event_data = @event.call(*args)
    super(*args, event_data)
  end
end


As you can see, this simplifies the logic a lot, and makes clear that we have two different behaviors, which is invaluable for consumers of your API.