صِل بين مداريّين بحدث خارجي واستماع مؤهَّل، دون أن يمد أيٌّ منهما يده إلى الآخر.
لا تستدعي المداريّات بعضها ولا تتشارك آلات الحالات. واحد يبث حدثًا، وآخر يستمع إليه. يصل هذا الدرس بين طلب ولوحة إشعارات.
الحدث الذي يغادر مداريّه external، ويجب أن يعلن حمولته:
1emits {
2 ORDER_SHIPPED -> external {
3 orderId : string!
4 }
5}SHIP -> shipped
(set @entity.status shipped)
(emit ORDER_SHIPPED { orderId: "A-1042" })داخل مداريّها تكتب السمة Trait.EVENT. وعبر المداريّات يُؤهَّل المصدر باسم المداريّ:
1listens {
2 OrderOrbital.OrderLifecycle.ORDER_SHIPPED -> SHOW
3 SHOW {
4 orderId : string!
5 }
6}المصدر ثنائي الأجزاء الذي يسمّي سمة لا توجد إلا في مداريّ آخر خطأ، والرسالة تسمّي الصيغة المؤهَّلة الواجب استخدامها.
البرنامج أعلاه كآلات حالات، واحدة لكل سمة: كل مربع حالة، وكل سهم حدث ينقلها، والأيقونات على السهم هي التأثيرات التي يشغّلها ذلك الحدث. الرمز ◇ على السهم شرط حارس؛ مرّر المؤشر فوقه لتقرأ الشرط. كيف تقرأ AVL
OrderLifecycle
on Order · states: 2 · transitions: 2NoticeBoard
on Notice · states: 1 · transitions: 1كيف تتحادث السمات فيما بينها، بالترتيب:
1app std-orb-docex-relay "1.0.0"
2"std-orb-docex-relay — two orbitals: one emits an external event, the other listens with the qualified three-part source."
3orbital OrderOrbital {
4 entity Order [runtime] {
5 id : string!
6 status : "pending" | "shipped" = pending
7 }
8
9 trait OrderLifecycle -> Order [interaction, instance] {
10 initial: pending
11
12 state pending {
13 INIT -> pending
14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [
15 { type: badge, label: "Pending", variant: warning },
16 { type: button, label: "Ship order", action: SHIP, variant: primary }
17 ] })
18 SHIP -> shipped
19 (set @entity.status shipped)
20 (emit ORDER_SHIPPED { orderId: "A-1042" })
21 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [
22 { type: badge, label: "Shipped", variant: success },
23 { type: button, label: "New order", action: RESET, variant: ghost },
24 { type: button, label: "See notifications", href: "/notices", variant: link }
25 ] })
26 }
27
28 state shipped {
29 RESET -> pending
30 (set @entity.status pending)
31 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [
32 { type: badge, label: "Pending", variant: warning },
33 { type: button, label: "Ship order", action: SHIP, variant: primary }
34 ] })
35 }
36
37 emits {
38 ORDER_SHIPPED -> external {
39 orderId : string!
40 }
41 @description "An order left the warehouse."
42 }
43 }
44
45 page "/orders" -> OrderLifecycle
46}
47
48orbital NotificationOrbital {
49 entity Notice [runtime] {
50 id : string!
51 message : string = ""
52 }
53
54 trait NoticeBoard -> Notice [interaction, instance] {
55 initial: idle
56
57 state idle {
58 INIT -> idle
59 (render-ui main { type: stack, direction: vertical, gap: sm, children: [
60 { type: typography, content: "No notifications yet.", variant: body, color: muted },
61 { type: button, label: "Back to orders", href: "/orders", variant: link }
62 ] })
63 SHOW -> idle
64 (set @entity.message (str/concat "Order " ?orderId " shipped"))
65 (render-ui main { type: alert, variant: info, message: @entity.message })
66 }
67
68 listens {
69 OrderOrbital.OrderLifecycle.ORDER_SHIPPED -> SHOW
70 SHOW {
71 orderId : string!
72 }
73 }
74 }
75
76 page "/notices" -> NoticeBoard
77}يتحقق المدقق من التوصيل من طرفه إلى طرفه: الحدث معلن، وحمولته تطابق ما يقرؤه المستمع، والحدث الخارجي الذي لا مستمع له في أي مكان يُبلَّغ عنه.
السلوك الذي يحتاج أن يُخبَر بالحدث الذي يطلقه يأخذ مفتاحًا من نوع event، يُعنون بالصيغ نفسها المستخدمة في listens: EVENT أو Trait.EVENT أو Orbital.Trait.EVENT. ويجب أن يسمّي العنوان سمة في البرنامج وأحد مدخلاتها المعلنة.
ابنِ تطبيقًا كاملًا يجمع عدة مداريّات والمكتبة القياسية.