Results 1 to 2 of 2

Thread: Working with Lua events using the Classes.Publisher object

  1. #1
    Join Date
    Jul 2007
    Location
    Netherlands
    Posts
    370

    Default Working with Lua events using the Classes.Publisher object

    While going through the lua files trying to figure out how stuff works I created some test code to see how the Girder internals use the Classes.Publisher object to provide lua with an event capbility (in code, not the regular Girder events)

    So here is my experiment, with details comments on how it works, both firing events and consuming events
    Code:
    --[[
    Example to explain the working of events using the Classes.Publisher object.
    This class is used in many Girder objects to inform other pieces of lua code
    of changes in/to an object.
    ]]--
     
    -- define the events our SampleObject will be generating
    -- using a local for easier access ('Events' instead of 'self.Events')
    local Events = table.makeset ( {
     'Add',
     'Stopped',
     'Started',
     'SomeOther',
     'OneMore',
    } )
     
    -- This is where we start our SampleObject with events
    local SampleObject = {
     
      -- copy local event list into our object so consumers can examine available events
      Events = Events,
     
      -- We require our own Publisher object to handle the event subscriptions
      Publisher = Classes.Publisher:New(),
     
      -- this is an internal function which can be called to raise an event
      -- event must be a value from the Events list, all extra parameters will be passed
      -- on to the eventhandlers of our subscribers
      Event = function (self,event,...)
        -- Check if an event is provided, also make sure its a valid event
        assert(event, "No event provided")
        assert(Events[event] == event, "Event " .. event .. " is not a supported event")
     
        -- now let our publisher spread the word of our event
        self.Publisher:Event (event, unpack (arg))
      end,
     
      -- anyone can subscribe to our events by calling this function
      -- parameter f should be a function to be called when an event occurs
      Subscribe = function (self, f)
        return self.Publisher:Subscribe(f)
      end,
     
      -- Once subscribed, anyone can unsubscribe from our events by calling this function
      -- parameter f should be the same function that was used to subscribe.
      Unsubscribe = function (self, f)
        return self.Publisher:Unsubscribe (f)
      end,
     
       -- sample methods to start our object and raise an event
       Start = function (self)
         -- this is where actual code would go
     
        -- Now raise the event that we started our object
        self:Event(self.Events.Started, "parameter1", 2, "and parameter3")
      end,
     
      Other = function (self)
        -- this is where actual code would go
     
        -- Now raise the event that we did something
        self:Event(self.Events.SomeOther, "No parameters")
      end,
     
    }  -- SampleObject
     
    -- Lets try our SampleObject here...
    -- create a handler function
    local myEventHandler = function (event, ...)
      if event == SampleObject.Events.Started then
        print ("Object informed us that it started, with parameters: ", unpack(arg) )
      else
        print ("Object informed us about some other event ('" .. event .. "'), with parameters: ", unpack(arg))
      end
    end
     
    -- now register for events
    SampleObject:Subscribe(myEventHandler)
     
    -- go call Start method so we get an event, and see whether our handler prints them in the lua console
    SampleObject:Start()   -->  Object informed us that it started, with parameters:  parameter1 2 and parameter3
    SampleObject:Other()   -->  Object informed us about some other event ('SomeOther'), with parameters:  No parameters
     
    -- Now unregister
    SampleObject:Unsubscribe(myEventHandler)
    Using: Win7 MCE, Girder, xPL, RFXcom, HomeEasy

    http://www.thijsschreijer.nl

  2. #2
    Join Date
    Jul 2007
    Location
    Netherlands
    Posts
    370

    Default Utility function to add event capbilities

    Along the lines of the DRY principle (Don't Repeat Yourself), here's another method with the exact same result, but no need to repeat the code for every object.

    Code:
    --[[
    Here is another approach to the same events methodology
    Lets create a utility function that adds the event capability to any object/table you provide.
    ]]--
     
    -- Here's the utility function; (its a global !!)
    -- target is the object/table to which event capabilities will be added
    -- eventlist is a 1 dimensional list (input to table.makeset()) with event names
    -- The function returns the target object, with event capability added
    table.AddEvents = function ( target, eventlist )
      -- check our inputs
      assert( type(target) == 'table', "Can't enable events; the target must be a table.")
      assert( type(eventlist) == 'table', "Can't enable events; the eventlist must be a table.")
      assert( target.Publisher == nil, "Can't add event capbility, the target already has an event publisher")
     
      -- create event list into our object so consumers can examine available events
      local Events = table.makeset(eventlist)
     
      -- We require our own Publisher object to handle the event subscriptions
      local Publisher = Classes.Publisher:New()
     
      -- this is an internal function which can be called to raise an event
      -- event must be a value from the Events list, all extra parameters will be passed
      -- on to the eventhandlers of our subscribers
      local Event = function (self,event,...)
        -- Check if an event is provided, also make sure its a valid event
        assert(event, "No event provided")
        assert(Events[event] == event, "Event " .. event .. " is not a supported event")
     
        -- now let our publisher spread the word of our event
        self.Publisher:Event (event, unpack (arg))
      end
     
      -- anyone can subscribe to our events by calling this function
      -- parameter f should be a function to be called when an event occurs
      local Subscribe = function (self, f)
          return self.Publisher:Subscribe(f)
      end
     
      -- Once subscribed, anyone can unsubscribe from our events by calling this function
      -- parameter f should be the same function that was used to subscribe.
      local Unsubscribe = function (self, f)
        return self.Publisher:Unsubscribe (f)
      end
     
      -- Now lets add the created locals to our target object
      target.Publisher = Publisher
      target.Events = Events
      target.Event = Event
      target.Subscribe = Subscribe
      target.Unsubscribe = Unsubscribe
     
      return target
    end  -- table.AddEvents
     
    print()
    print("Lets try our second approach...")
     
    -- create a table/object with just the functional code;
    local TestObject = {
      -- sample methods to start our object and raise an event
      Start = function (self)
        -- this is where actual code would go
        -- Now raise the event that we started our object
        self:Event(self.Events.Started, "parameter1", 2, "and parameter3")
      end,
     
      Other = function (self)
        -- this is where actual code would go
        -- Now raise the event that we did something
        self:Event(self.Events.SomeOther, "No parameters")
      end,
    }
     
    -- Call AddEvents, to add the Event capability to our TestObject
    table.AddEvents (
      TestObject,
      {
        'Add',
        'Stopped',
        'Started',
        'SomeOther',
        'OneMore',
      } )
     
    -- Here is our test event handler function
    local myEventHandler = function (event, ...)
      if event == TestObject.Events.Started then
        print ("Object informed us that it started, with parameters: ", unpack(arg) )
      else
        print ("Object informed us about some other event ('" .. event .. "'), with parameters: ", unpack(arg))
      end
    end
     
    -- Now test again, the results should be identical;
    -- now register for events, using the same test eventhandler.
    TestObject:Subscribe(myEventHandler)
     
    -- go call Start method so we get an event, and see whether our handler prints them in the lua console
    TestObject:Start()   -->  Object informed us that it started, with parameters:  parameter1 2 and parameter3
    TestObject:Other()   -->  Object informed us about some other event ('SomeOther'), with parameters:  No parameters
     
    -- Now unregister
    TestObject:Unsubscribe(myEventHandler)
    Additional remark; the event capability can also be added inline like this;
    Code:
    local TestObject = table.AddEvents(
      -- create target
      {
        Start = function (self)
          -- bla bla
        end,
     
        Other = function (self)
          -- bla bla
        end,
      },
      -- create eventlist
      {
        'Add',
        'Stopped',
      }
    )
    Using: Win7 MCE, Girder, xPL, RFXcom, HomeEasy

    http://www.thijsschreijer.nl

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •