bitcoin-atm
bitcoin atm for pyc inc.
git clone https://9o.is/git/bitcoin-atm.git
ClientActorBridge.scala
(3576B)
1 package inc.pyc.lift_akka
2
3 import scala.xml._
4 import net.liftweb._
5 import net.liftweb.common._
6 import net.liftweb.actor._
7 import net.liftweb.http._
8 import net.liftweb.http.js._
9 import net.liftweb.http.js.JE._
10 import net.liftweb.http.js.JsCmds._
11 import net.liftweb.json._
12 import net.liftmodules.extras.JsExtras
13 import akka.event.{EventBus, LookupClassification}
14
15 /**
16 * Lift snippet to initiate a comet actor that subscribes to a
17 * topic in an Akka event bus system. Client-side javascript Actor
18 * Bridge must have a function with the topic name to receive updates.
19 */
20 abstract class EventRegister extends ClientEventPushBridge {
21 def comet(in: NodeSeq): NodeSeq = connectActorBridge(in)
22 }
23
24 /**
25 * Bridge to push messages from akka's event bus system to Actor Bridge in javascript.
26 *
27 * The topic being listened client-side should be the same as the
28 * topic in the event bus server-side.
29 */
30 trait ClientEventPushBridge extends ClientActorBridge {
31
32 type Receive = PartialFunction[Any, Unit]
33
34
35 /**
36 * The event bus
37 */
38 val bus: LiftActorEventBus
39
40 /**
41 * Callback function after topic subscription of the event bus.
42 */
43 val postSubscribe: () => Unit = () => {}
44
45 /**
46 * The partial function to handle requests
47 * coming from the server or client.
48 */
49 val receive: Receive = {
50 case JString(str) =>
51 }
52
53 protected def connectActorBridge(in: NodeSeq): NodeSeq =
54 super.connectActorBridge(topic, in, {
55 (session, clientProxy) =>
56
57 val serverActor = new ScopedLiftActor with LiftActorCompare {
58 override def lowPriority = receive orElse {
59 case event =>
60 clientProxy ! event
61 }
62 }
63
64 bus.subscribe(serverActor, topic)
65 postSubscribe()
66 serverActor
67 })
68 }
69
70 /**
71 * The bridge between ActorBridge in javascript and server-side actors.
72 */
73 trait ClientActorBridge extends Logger {
74
75 /**
76 * Topic (or function) to execute client-side.
77 */
78 val topic: String
79
80 /**
81 * Connects a given `ScopedLiftActor` to the given function name listening from
82 * the client-side Actor Bridge.
83 */
84 protected def connectActorBridge(topic: String, in: NodeSeq, f: (LiftSession, LiftActor) => ScopedLiftActor): NodeSeq =
85 (for (sess <- S.session) yield {
86 val className = this.getClass.getName.split("""\.""").last
87 val instaName = lowercase(className)
88
89 val clientProxy = sess.serverActorForClient(s"window.$instaName.apply")
90 val serverActor: ScopedLiftActor = f(sess, clientProxy)
91
92 S.appendJs(SetExp(JsVar(s"window.$instaName"),
93 JsExtras.CallNew(s"window.$className", sess.clientActorFor(serverActor)) ))
94
95 in
96 }) openOr NodeSeq.Empty
97
98 private def lowercase(s: String): String = s(0).toLower + s.substring(1)
99 }
100
101
102 /**
103 * Message to update `LiftActorEventBus`.
104 */
105 case class EventUpdate(topic: String, payload: Any)
106
107 /**
108 * Bridge between Akka's event bus and Lift actors.
109 */
110 abstract class LiftActorEventBus extends EventBus with LookupClassification {
111 type Event = EventUpdate
112 type Classifier = String
113 type Subscriber = LiftActorCompare
114
115 override protected def classify(event: Event): Classifier = event.topic
116
117 override protected def publish(event: Event, subscriber: Subscriber): Unit = {
118 subscriber ! event.payload
119 }
120
121 override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
122 a.compareTo(b)
123 }
124
125 trait LiftActorCompare extends LiftActor with java.lang.Comparable[LiftActor] {
126 override def compareTo(a: LiftActor): Int = -1
127 }