1 | ## Integrating GopherJS with Smartdown in two beers
|
2 |
|
3 | A couple weeks ago on a November 2017 Friday evening, I set my mind to integrating a non-Javascript language into Smartdown in order to learn/teach such a language via Smartdown. After eliminating ClojureScript, ScalaJS, and KotlinJS via a fairly simple process of determining whether their demos worked offline, I settled on [GopherJS](https://github.com/gopherjs/gopherjs), the Javascript implementation of Go, as a worthy target.
|
4 |
|
5 | Why? Because the [GopherJS Playground](https://github.com/gopherjs/gopherjs.github.io) worked wonderfully offline, proving that compilation of my Go source code was occurring in the browser, and satisfying my need for such a capability in Smartdown.
|
6 |
|
7 | ### It took more than two beers
|
8 |
|
9 | I'm going to write a more detailed article about this project, but I eventually succeeded in learning Go, adapting the GopherJS Playground to work as a UI-less body of code, and integrating this code into Smartdown in a reasonable way. This document is an example of the potential of GoDown, the Smartdown+Go integration.
|
10 |
|
11 | ### Producer/Consumer Example
|
12 |
|
13 | This demonstrates the use of multiple Go packages, the use of the GopherJS DOM package, and the use of channels to communicate between multiple processes.
|
14 |
|
15 | #### ProducerA
|
16 |
|
17 | Creates a Button that emits an "A" to the channel
|
18 |
|
19 | ```go/playable/autoplay
|
20 | package producerA
|
21 |
|
22 | import (
|
23 | "honnef.co/go/js/dom"
|
24 | "github.com/gopherjs/gopherjs/js"
|
25 | )
|
26 |
|
27 | func Producer(ch chan string) {
|
28 | d := dom.GetWindow().Document()
|
29 |
|
30 | myDivId := js.Global.Get("godownDiv_producerA").String()
|
31 | println("myDivId", myDivId)
|
32 | div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
|
33 | div.SetInnerHTML("")
|
34 |
|
35 | child := d.CreateElement("button").(*dom.HTMLButtonElement)
|
36 | child.Style().SetProperty("color", "purple", "")
|
37 | child.SetTextContent("Produce A")
|
38 | div.AppendChild(child)
|
39 | child.AddEventListener("click", false, func(event dom.Event) {
|
40 | ch <- "A"
|
41 | })
|
42 | }
|
43 | ```
|
44 |
|
45 | #### ProducerB
|
46 |
|
47 | Creates a Button that emits a "B" to the channel
|
48 |
|
49 | ```go/playable/autoplay
|
50 | package producerB
|
51 |
|
52 | import (
|
53 | "honnef.co/go/js/dom"
|
54 | "github.com/gopherjs/gopherjs/js"
|
55 | )
|
56 |
|
57 | func Producer(ch chan string) {
|
58 | d := dom.GetWindow().Document()
|
59 |
|
60 | myDivId := js.Global.Get("godownDiv_producerB").String()
|
61 | println("myDivId", myDivId)
|
62 | div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
|
63 | div.SetInnerHTML("")
|
64 |
|
65 | child := d.CreateElement("button").(*dom.HTMLButtonElement)
|
66 | child.Style().SetProperty("color", "purple", "")
|
67 | child.SetTextContent("Produce B")
|
68 | div.AppendChild(child)
|
69 | child.AddEventListener("click", false, func(event dom.Event) {
|
70 | ch <- "B"
|
71 | })
|
72 | }
|
73 | ```
|
74 |
|
75 |
|
76 | #### Consumer
|
77 |
|
78 | Reads from the channel and displays a log of all received messages.
|
79 |
|
80 |
|
81 | ```go/playable/autoplay
|
82 | package consumer
|
83 |
|
84 | import (
|
85 | "honnef.co/go/js/dom"
|
86 | "github.com/gopherjs/gopherjs/js"
|
87 | )
|
88 |
|
89 | func Consumer(ch chan string) {
|
90 | d := dom.GetWindow().Document()
|
91 | consumed := ""
|
92 | myDivId := js.Global.Get("godownDiv_consumer").String()
|
93 | println("myDivId", myDivId)
|
94 | div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
|
95 |
|
96 | for {
|
97 | div.SetInnerHTML("<h1>" + consumed + "</h1>")
|
98 | consumedChar, more := <- ch
|
99 | if more {
|
100 | consumed += consumedChar
|
101 | } else {
|
102 | break
|
103 | }
|
104 | }
|
105 | }
|
106 | ```
|
107 |
|
108 |
|
109 | #### Main program invokes Producers and Consumer and then Chills
|
110 |
|
111 | All Go programs start with `main`. In this example, we just use `main` to tie together the various producer/consumer components and give them a shared channel for communication. It is totally possible, and reasonable to combine `producerA` and `producerB` into a parametrized package `producer`; but this demo evolved to show off the multi-package capability.
|
112 |
|
113 |
|
114 | ```go/playable/autoplay
|
115 | package main
|
116 |
|
117 | import (
|
118 | "producerA"
|
119 | "producerB"
|
120 | "consumer"
|
121 | "github.com/gopherjs/gopherjs/js"
|
122 | )
|
123 |
|
124 | var ch chan string
|
125 |
|
126 | func main() {
|
127 | js.Global.Set("Godown_Shutdown", js.InternalObject(func(msg string) {
|
128 | close(ch)
|
129 | }))
|
130 |
|
131 | ch = make(chan string, 5)
|
132 | go producerA.Producer(ch)
|
133 | go producerB.Producer(ch)
|
134 | go consumer.Consumer(ch)
|
135 |
|
136 | println("All Done")
|
137 | }
|
138 |
|
139 | ```
|
140 |
|
141 | ---
|
142 |
|
143 | [Back to Home](:@Home)
|