You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/07/13 03:10:31 UTC

git commit: Inital pass at new pub/sub system ("stimulus" and "response") - tests are coming!

Updated Branches:
  refs/heads/5.4-js-rewrite 9c7194eea -> 83d6848ea


Inital pass at new pub/sub system ("stimulus" and "response")
- tests are coming!


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/83d6848e
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/83d6848e
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/83d6848e

Branch: refs/heads/5.4-js-rewrite
Commit: 83d6848eaa2ece668d6e1ce9aa7b61ad9a17e5c8
Parents: 9c7194e
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Thu Jul 12 18:10:15 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Thu Jul 12 18:10:15 2012 -0700

----------------------------------------------------------------------
 .../tapestry5/corelib/modulejs/pubsub.coffee       |  150 +++++++++++++++
 1 files changed, 150 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/83d6848e/tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee b/tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee
new file mode 100644
index 0000000..42b98bb
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee
@@ -0,0 +1,150 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Framework/application publish and subscribe that is independent of
+# the DOM. Parts of this are modelled on how jQuery DOM events work.
+
+define ["_"], (_) ->
+
+  exports = {}
+
+  subscribers = { }
+
+  kullSubs = (name, namespaces, responder) ->
+    list = subscribers[name]
+    return if list is null
+
+    # Work backwards to avoid excessive copies
+    for i in [list.length - 1 .. 0]
+      listener = list[i]
+      if (responder is null or listener.fn is responder) and
+         # TODO: _.intersection is expensive
+         (namespaces.length == 0 or _.intersection(namespaces, listener.namespaces).length > 0)
+        if list.iterating
+          subscribers[name] = list = list[..]
+        list.splice i, 1
+
+    return
+
+  addSub = (stimulusName, addFirst, responder) ->
+    [simpleName, namespaces...] = _.split stimulusName, '.'
+
+    subscriber =
+      stimulus: simpleName
+      namespaces: namespaces
+      fn: responder
+
+    list = subscribers[simpleName]
+
+    if list is null
+      subscribers[simpleName] = [subscriber]
+    else
+      # If iterating the list (during a publish), then do a copy-on-write.
+      # This allows a clean way to have listeners unsubscribe one first notification, or
+      # otherwise change the subscriptions.
+      if list.iterating
+        subscribers[simpleName] = list = list.slice[..]
+
+      list[if addFirst then "unshift" else "push"] subscriber
+
+    return
+
+  # Adds a responder for a stimulus.  The stimulus name consists of a simple topic name
+  # (used when publishing a message), and optional set of namespaces; each namespace
+  # is preceded by a '.'.  The namespaces make it easier to identify specific responders
+  # later, such as when removing a responder.
+  # The ordering identifies where the listener will be placed.
+  # The listenerfn is a function to be invoked via the publisher. A topic publisher
+  # will publish a message with an application-specific memo.  The listenerfn receives
+  # the memo parameter, and an event parameter.  The event parameter allows the
+  # message to be terminated early.
+  # stimulus - name of stimulus to respond to, with namespaces appended
+  # responder - function to be invoked when the stimulus is fired. The responder
+  # is passed a memo object, and an event. The event can be used to cancel or
+  # pause the stimulus.
+  # Returns this module's exports, for easy chaining of calls.
+  exports.respondTo = exports.on = (stimulusName, responder) ->
+
+    addSub stimulusName, false, responder
+
+    return exports
+
+  # Adds a responder for a stimulus, but the responder is added first in the list.
+  # Otherwise, the same as respondsTo().
+  # Returns this module's exports, for easy chaining of calls.
+  exports.responderFirst = exports.first = (stimulusName, responder) ->
+    addSub stimulusName, true, responder
+
+    return exports
+
+  # Stops responding to a stimulus. The parameters identify zero or more responders
+  # that will no longer be triggered.
+  # Only previously added responders that match a specifically as possible will be removed.
+  # If a responder is provided, it must match exactly.
+  # If a stimulus name is provided, the name must match exactly.
+  # If any namespaces were provided, then at least ONE namespace must match a namespace provided when
+  # the responder was added.
+  # stimulusName - optional, identifies a stimulus and optional namespaces
+  # responder - optional, identifies a specific responder to remove
+  # Returns this modules exports, to support chaining.
+  exports.stopResponding = exports.off = (stimulusName = "", responder) ->
+    if _.isFunction stimulusName
+      responder = stimulusName
+      stimulusName = ""
+
+    [simpleName, namespaces...] = _.split stimulusName, '.'
+
+    if simpleName isnt ""
+      kullSubs simpleName, namespaces, responder
+    else
+      kullSubs name, namespaces, responder for name of subscribers
+
+    return exports
+
+
+  # Fires the stimulus, passing the memo, and an even, to each responder that was previously added.
+  # The event can be used to terminate the message (it has a stop() method).
+  # The context object defines the value of this for
+  # the invoked listener functions; it is often null (or omitted).
+  # stimulusName - simple name of stimulus to fire (it should not contain namespaces)
+  # memo - object to be passed to each responder (as the first parameter)
+  # context - context value (this) used when invoking responders
+  # Returns this modules exports, to support chaining.
+  exports.fire = (stimulusName, memo, context) ->
+    list = subscribers[stimulusName]
+
+    return exports if list is null
+
+    event =
+      running: true
+      stimulus: stimulusName
+      stop: -> running = false
+      # TODO: pause and resume
+
+    try
+      wasIterating = list.iterating
+      list.iterating = true
+
+      for subscriber in list when event.running
+        # Pass the memo and the event to the responder.
+        subscriber.fn.call context, memo, event
+
+    finally
+      list.iterating = wasIterating
+
+    return exports
+
+  # Result of define:
+  return exports
+