PageRenderTime 37ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/elnode/elnode_tutorial.creole

https://github.com/emish/emacs
Creole | 419 lines | 303 code | 116 blank | 0 comment | 0 complexity | 28751fd9bfd7a31347bcc459b1530ee3 MD5 | raw file
  1. = Getting Started with Elnode - the webserver for Emacs =
  2. This is a tutorial that will hopefully show you how to install and get
  3. started making web services with Elnode.
  4. Elnode is a node.js like webserver tool for Emacs. It let's you make
  5. and run web servers and services from inside Emacs.
  6. == Installing Elnode ==
  7. Elnode is now packaged in [[http://marmalade-repo.org/packages/elnode|marmalade]].
  8. For dealing with package repositories check out the
  9. [[http://www.emacswiki.org/emacs/ELPA|Emacs Wiki]] but the short version
  10. is to add the following to your {{{.emacs}}} or your
  11. {{{.emacs.d/init.el}}}:
  12. {{{
  13. (add-to-list
  14. 'package-archives
  15. '("marmalade" . "http://marmalade-repo.org/packages/"))
  16. }}}
  17. And then do:
  18. {{{
  19. M-x list-packages
  20. }}}
  21. find Elnode in the list and press {{{i}}} or {{{RET}}} to install it.
  22. If you don't want to use packages you can just install {{{elnode.el}}}
  23. on your {{{load-path}}} somewhere and:
  24. {{{
  25. (require 'elnode)
  26. }}}
  27. == Hello World! ==
  28. Now we've installed Elnode you'll want to start making web services
  29. with it. Let's start with a Hello World example.
  30. open a new Emacs file
  31. {{{
  32. C-x C-f my-elnode-hello-world.el
  33. }}}
  34. enter the Lisp code for the handler, for that old time feel you could type this in, or if you're under 35, maybe just cut and paste
  35. {{{
  36. (defun my-elnode-hello-world-handler (httpcon)
  37. (elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
  38. (elnode-http-return
  39. httpcon
  40. "<html><body><h1>Hello World</h1></body></html>"))
  41. (elnode-start 'my-elnode-hello-world-handler 8028 "localhost")
  42. }}}
  43. make the Lisp code //live//
  44. {{{
  45. M-x eval-buffer
  46. }}}
  47. now open [[http://localhost:8028]] in your browser - you should see //Hello World!//
  48. == Publish some files ==
  49. Elnode provides a builtin webserver that can serve files from a
  50. directory on your computer. The Elnode webserver is turned on by
  51. default (it's all configurable though).
  52. === The default webserver ===
  53. By default the webserver delivers files from:
  54. {{{
  55. ~/public_html
  56. }}}
  57. so if you have a public_html directory in your home directory then
  58. just browse to [[http://localhost:8000]] and you should see an index
  59. of that directory.
  60. If you don't have a {{{~/public_html}}} directory then just make one
  61. and drop a file or two in it.
  62. Alternately, try configuring the webserver root directory:
  63. {{{
  64. M-x customize-variable RET elnode-webserver-docroot RET
  65. }}}
  66. to another directory. Then try hitting [[http://localhost:8000]]
  67. again.
  68. === Making another webserver ===
  69. Now let's make a new webserver service.
  70. Make a new docroot:
  71. {{{
  72. mkdir ~/myspecialdocroot
  73. }}}
  74. Put an html file in there:
  75. {{{
  76. cat <<EOF > ~/myspecialdocroot/saybum.html
  77. <html>
  78. <h1>BUM!</h1>
  79. </html>
  80. }}}
  81. Now we have something to serve we can use Elnode to make the web service.
  82. Open a new Emacs file:
  83. {{{
  84. C-x C-f my-elnode-webserver.el
  85. }}}
  86. Add this Lisp:
  87. {{{
  88. (defconst my-elnode-webserver-handler
  89. (elnode-webserver-handler-maker "~/myspecialdocroot"))
  90. (elnode-start 'my-elnode-webserver-handler 8001 "localhost")
  91. }}}
  92. Now evaluate that with: {{{M-x eval-buffer}}}
  93. Now open [[http://localhost:8001/saybum.html]]
  94. Now open [[http://localhost:8001]] - you should see an automated index
  95. of {{{~/myspecialdocroot}}}.
  96. == Stopping a server ==
  97. We've started a couple of servers now. Let's stop the two servers that
  98. we've started:
  99. {{{
  100. M-x elnode-stop RET 8028 RET
  101. M-x elnode-stop RET 8001 RET
  102. }}}
  103. Those servers are now stopped and you won't be able to hit them.
  104. == Add a binding to the builtin server ==
  105. Instead of starting new servers all the time we can add bindings to
  106. the standard Elnode server. Why would we do this? I think using a
  107. separate server for developing something initially is a good idea, but
  108. then you either have something you want to package up as it's own
  109. server (a wiki engine you've developed and want to give to other
  110. people, for example) or you have something you want to make available
  111. in your own default server. Of course, it's always a judgement, the
  112. way URLs work mean that you can pretty much always make any service
  113. available on it's own server or under a URL on another one.
  114. Let's make our Hello World example available again by binding it to
  115. the default server (which is still listening on port 8000 if you
  116. haven't changed anything).
  117. Go back to hello world:
  118. {{{
  119. C-x b my-elnode-hello-world.el
  120. }}}
  121. Remove the {{{elnode-start}}} line and add this:
  122. {{{
  123. (add-to-list 'elnode-hostpath-default-table '("/helloworld/" . my-elnode-hello-world-handler))
  124. }}}
  125. So now it should look like this:
  126. {{{
  127. (defun my-elnode-hello-world-handler (httpcon)
  128. (elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
  129. (elnode-http-return
  130. httpcon
  131. "<html><body><h1>Hello World</h1></body></html>"))
  132. (add-to-list 'elnode-hostpath-default-table '("/helloworld/" . my-elnode-hello-world-handler))
  133. }}}
  134. Now eval the buffer with {{{M-x eval-buffer}}}
  135. Now open [[http://localhost:8000/helloworld/]] in your browser.
  136. Just to prove the webserver is still there, open
  137. [[http://localhost:8000/]]. This should still show your
  138. {{{~/public_html}}} directory (or whatever you configured
  139. {{{elnode-webserver-docroot}}} to).
  140. Check the variable {{{elnode-hostpath-default-table}}} with {{{C-h v elnode-hostpath-default-table}}}
  141. The value should be something like:
  142. {{{
  143. (("/helloworld/" . my-elnode-hello-world-handler)
  144. ("[^/]+/.*" . elnode-webserver))
  145. }}}
  146. {{{elnode-hostpath-default-table}}} can also be customized to add more
  147. services. But any handler mapped in there will have to be loaded in at
  148. Emacs startup so you either need to package and load your Elnode code
  149. or put it in your {{{load-path}}} and {{{require}}} it from Emacs
  150. init.
  151. == A more advanced example - publishing a buffer ==
  152. So far, all the examples have been quite trivial. Though I hope you
  153. think it's interesting that you can do all these things quite easily
  154. from inside Emacs.
  155. But now let's try something harder - let's make an web based editor.
  156. This is an exercise that will grow with the tutorial. I hope you'll be
  157. interested in the first draft, even though it's going to be relatively
  158. simple.
  159. Make a new file {{{C-x C-f my-elnode-editor.el}}}.
  160. Add the following Lisp code:
  161. {{{
  162. (defvar my-elnode-editor-buffer (get-buffer-create "*my-elnode-editor-buffer*"))
  163. (defun my-elnode-editor-handler (httpcon)
  164. (elnode-http-start httpcon 200 '("Content-Type" . "text/plain"))
  165. (elnode-http-return
  166. httpcon
  167. (with-current-buffer my-elnode-editor-buffer
  168. (buffer-substring-no-properties (point-min) (point-max)))))
  169. }}}
  170. Eval that with {{{M-x eval-buffer}}}.
  171. Now go and type some text in ~*my-elnode-editor-buffer~*. This will be
  172. served by the editor service.
  173. Now let's start the service:
  174. {{{
  175. M-x elnode-start my-elnode-editor-handler 8002 localhost
  176. }}}
  177. Now try and hit [[http://localhost:8002]] - you should see whatever
  178. you typed in the ~*my-elnode-editor-buffer~*.
  179. Try updating the text in the buffer and refreshing the browser. We're
  180. displaying that buffer whatever it has in it.
  181. Ok. So we've published a buffer. But what about someone else updating
  182. it?
  183. Let's make another handler to handle updates, add this to your {{{my-elnode-editor.el}}}:
  184. {{{
  185. (defun my-elnode-editor-update-handler (httpcon)
  186. (let ((change-text (elnode-http-param httpcon "change")))
  187. (with-current-buffer my-elnode-editor-buffer
  188. (goto-char (point-max))
  189. (if (stringp change-text)
  190. (insert change-text))))
  191. (elnode-http-start httpcon 302 '("Location" . "/"))
  192. (elnode-http-return httpcon))
  193. }}}
  194. Now we have two handlers we'll have to map them together
  195. somehow. Let's map one to the root ({{{/}}}) and one to
  196. {{{/update/}}}. Add the following code to {{{my-elnode-editor.el}}}:
  197. {{{
  198. (defconst my-elnode-editor-urls
  199. `(("$" . my-elnode-editor-handler)
  200. ("update/.*$" . my-elnode-editor-update-handler)))
  201. }}}
  202. And now we need to add a handler to do the dispatching for these URLs,
  203. add this to {{{my-elnode-editor.el}}} as well:
  204. {{{
  205. (defun my-elnode-editor-dispatcher-handler (httpcon)
  206. (elnode-dispatcher httpcon my-elnode-editor-urls))
  207. }}}
  208. //What is a dispatcher?// - a dispatcher is a handler that take a list
  209. of URL pattern mappings and works out, by reading the data from the
  210. HTTP connection, what handler should be invoked for what request.
  211. Now we have our new dispatcher based code we need to stop the old server:
  212. {{{
  213. M-x elnode-stop 8002
  214. }}}
  215. And now start the new server with the dispatcher handler:
  216. {{{
  217. M-x elnode-start my-elnode-editor-dispatcher-handler 8002 localhost
  218. }}}
  219. Now visit [[http://localhost:8002]] and see the buffer as it stands
  220. and then visit
  221. [[http://localhost:8002/update/?change=%0dlah+dee+dah%0d]] and see the
  222. updated buffer.
  223. == More advanced again - Make a webapp around the service ==
  224. Let's take our editor on another step. Let's add some static files and
  225. have the Elnode handlers be called by client side Javascript.
  226. If we're going to add some static files, we'll need a webserver. We
  227. already know how to do that. Once we've got some javascript though,
  228. we'll probably not want to retrieve the text by {{{HTTP GET}}}ing the
  229. root url, so let's alter that binding to {{{/text/}}} as well:
  230. {{{
  231. (defconst my-elnode-editor-webserver-handler
  232. (elnode-webserver-handler-maker "~/my-directory"))
  233. "The webserver handler.")
  234. (defconst my-elnode-editor-urls
  235. '(("text/$" . my-elnode-editor-handler)
  236. ("update/.*$" . my-elnode-editor-update-handler)
  237. ("[^/]+/.*" . my-elnode-editor-webserver-handler)))
  238. }}}
  239. Obviously {{{~/my-directory}}} needs to be the place where you are
  240. going to save your HTML and Javascript files.
  241. Now we need those HTML and Javascript files. Let's make the HTML
  242. first:
  243. {{{
  244. <html>
  245. <head>
  246. <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"
  247. language="Javascript">
  248. </script>
  249. <script src="my-elnode-editor.js" language="Javascript">
  250. </script>
  251. </head>
  252. <body>
  253. <textarea id="text" cols="60" rows="10">
  254. </textarea>
  255. </body>
  256. </html>
  257. }}}
  258. We're going to pull jQuery from Google's Content Delivery
  259. Network. We've put in a placeholder for our own Javascript file and
  260. other than that the HTML is really just a {{{textarea}}}
  261. element. We'll use //that// for putting the buffer text in.
  262. Now, what should the Javascript do?
  263. * when the page loads
  264. * make an AJAX call to Elnode for the buffer text
  265. * stick the received text into the {{{textarea}}}
  266. Ok. So here is {{{my-elnode-editor.js}}}:
  267. {{{
  268. var my_elnode_editor = (function () {
  269. var self = {
  270. /** Get the text from Emacs.
  271. */
  272. get_text: function () {
  273. $.ajax("/text/", {
  274. dataType: "text",
  275. success: function (data, textStatus, jqXHR) {
  276. $("#text").text(data);
  277. }
  278. });
  279. }
  280. };
  281. return self;
  282. })();
  283. $(document).ready(
  284. function () {
  285. my_elnode_editor.get_text();
  286. }
  287. );
  288. }}}
  289. Save this as {{{my-elnode-editor.js}}} (in whatever directory the
  290. webserver is serving) and save the HTML in the same directory, call it
  291. {{{my-elnode-editor.html}}}, say?
  292. You don't even have to restart the Elnode handler, because it already
  293. is pointing to the dispatcher handler. If you just:
  294. {{{
  295. M-x eval-buffer
  296. }}}
  297. this will re-evaluate the URL mappings. Now if you visit
  298. [[http://localhost:8002/my-elnode-editor.html]] you should see the
  299. webpage with the {{{textarea}}} and the text of your buffer.
  300. == That's all for now! ==
  301. This is as far as Nic has got writing the tutorial. More will come soon I hope:
  302. * {{{defer}}} with an example based around the editor service
  303. * debugging a running Elnode service