/plugins/elnode/elnode_tutorial.creole
Creole | 419 lines | 303 code | 116 blank | 0 comment | 0 complexity | 28751fd9bfd7a31347bcc459b1530ee3 MD5 | raw file
- = Getting Started with Elnode - the webserver for Emacs =
- This is a tutorial that will hopefully show you how to install and get
- started making web services with Elnode.
- Elnode is a node.js like webserver tool for Emacs. It let's you make
- and run web servers and services from inside Emacs.
- == Installing Elnode ==
- Elnode is now packaged in [[http://marmalade-repo.org/packages/elnode|marmalade]].
- For dealing with package repositories check out the
- [[http://www.emacswiki.org/emacs/ELPA|Emacs Wiki]] but the short version
- is to add the following to your {{{.emacs}}} or your
- {{{.emacs.d/init.el}}}:
- {{{
- (add-to-list
- 'package-archives
- '("marmalade" . "http://marmalade-repo.org/packages/"))
- }}}
- And then do:
- {{{
- M-x list-packages
- }}}
- find Elnode in the list and press {{{i}}} or {{{RET}}} to install it.
- If you don't want to use packages you can just install {{{elnode.el}}}
- on your {{{load-path}}} somewhere and:
- {{{
- (require 'elnode)
- }}}
- == Hello World! ==
- Now we've installed Elnode you'll want to start making web services
- with it. Let's start with a Hello World example.
- open a new Emacs file
- {{{
- C-x C-f my-elnode-hello-world.el
- }}}
- 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
- {{{
- (defun my-elnode-hello-world-handler (httpcon)
- (elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
- (elnode-http-return
- httpcon
- "<html><body><h1>Hello World</h1></body></html>"))
- (elnode-start 'my-elnode-hello-world-handler 8028 "localhost")
- }}}
- make the Lisp code //live//
- {{{
- M-x eval-buffer
- }}}
- now open [[http://localhost:8028]] in your browser - you should see //Hello World!//
- == Publish some files ==
- Elnode provides a builtin webserver that can serve files from a
- directory on your computer. The Elnode webserver is turned on by
- default (it's all configurable though).
- === The default webserver ===
- By default the webserver delivers files from:
- {{{
- ~/public_html
- }}}
- so if you have a public_html directory in your home directory then
- just browse to [[http://localhost:8000]] and you should see an index
- of that directory.
- If you don't have a {{{~/public_html}}} directory then just make one
- and drop a file or two in it.
- Alternately, try configuring the webserver root directory:
- {{{
- M-x customize-variable RET elnode-webserver-docroot RET
- }}}
- to another directory. Then try hitting [[http://localhost:8000]]
- again.
- === Making another webserver ===
- Now let's make a new webserver service.
- Make a new docroot:
- {{{
- mkdir ~/myspecialdocroot
- }}}
- Put an html file in there:
- {{{
- cat <<EOF > ~/myspecialdocroot/saybum.html
- <html>
- <h1>BUM!</h1>
- </html>
- }}}
- Now we have something to serve we can use Elnode to make the web service.
- Open a new Emacs file:
- {{{
- C-x C-f my-elnode-webserver.el
- }}}
- Add this Lisp:
- {{{
- (defconst my-elnode-webserver-handler
- (elnode-webserver-handler-maker "~/myspecialdocroot"))
- (elnode-start 'my-elnode-webserver-handler 8001 "localhost")
- }}}
- Now evaluate that with: {{{M-x eval-buffer}}}
- Now open [[http://localhost:8001/saybum.html]]
- Now open [[http://localhost:8001]] - you should see an automated index
- of {{{~/myspecialdocroot}}}.
- == Stopping a server ==
- We've started a couple of servers now. Let's stop the two servers that
- we've started:
- {{{
- M-x elnode-stop RET 8028 RET
- M-x elnode-stop RET 8001 RET
- }}}
- Those servers are now stopped and you won't be able to hit them.
- == Add a binding to the builtin server ==
- Instead of starting new servers all the time we can add bindings to
- the standard Elnode server. Why would we do this? I think using a
- separate server for developing something initially is a good idea, but
- then you either have something you want to package up as it's own
- server (a wiki engine you've developed and want to give to other
- people, for example) or you have something you want to make available
- in your own default server. Of course, it's always a judgement, the
- way URLs work mean that you can pretty much always make any service
- available on it's own server or under a URL on another one.
- Let's make our Hello World example available again by binding it to
- the default server (which is still listening on port 8000 if you
- haven't changed anything).
- Go back to hello world:
- {{{
- C-x b my-elnode-hello-world.el
- }}}
- Remove the {{{elnode-start}}} line and add this:
- {{{
- (add-to-list 'elnode-hostpath-default-table '("/helloworld/" . my-elnode-hello-world-handler))
- }}}
- So now it should look like this:
- {{{
- (defun my-elnode-hello-world-handler (httpcon)
- (elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
- (elnode-http-return
- httpcon
- "<html><body><h1>Hello World</h1></body></html>"))
- (add-to-list 'elnode-hostpath-default-table '("/helloworld/" . my-elnode-hello-world-handler))
- }}}
- Now eval the buffer with {{{M-x eval-buffer}}}
- Now open [[http://localhost:8000/helloworld/]] in your browser.
- Just to prove the webserver is still there, open
- [[http://localhost:8000/]]. This should still show your
- {{{~/public_html}}} directory (or whatever you configured
- {{{elnode-webserver-docroot}}} to).
- Check the variable {{{elnode-hostpath-default-table}}} with {{{C-h v elnode-hostpath-default-table}}}
- The value should be something like:
- {{{
- (("/helloworld/" . my-elnode-hello-world-handler)
- ("[^/]+/.*" . elnode-webserver))
- }}}
- {{{elnode-hostpath-default-table}}} can also be customized to add more
- services. But any handler mapped in there will have to be loaded in at
- Emacs startup so you either need to package and load your Elnode code
- or put it in your {{{load-path}}} and {{{require}}} it from Emacs
- init.
- == A more advanced example - publishing a buffer ==
- So far, all the examples have been quite trivial. Though I hope you
- think it's interesting that you can do all these things quite easily
- from inside Emacs.
- But now let's try something harder - let's make an web based editor.
- This is an exercise that will grow with the tutorial. I hope you'll be
- interested in the first draft, even though it's going to be relatively
- simple.
- Make a new file {{{C-x C-f my-elnode-editor.el}}}.
- Add the following Lisp code:
- {{{
- (defvar my-elnode-editor-buffer (get-buffer-create "*my-elnode-editor-buffer*"))
- (defun my-elnode-editor-handler (httpcon)
- (elnode-http-start httpcon 200 '("Content-Type" . "text/plain"))
- (elnode-http-return
- httpcon
- (with-current-buffer my-elnode-editor-buffer
- (buffer-substring-no-properties (point-min) (point-max)))))
- }}}
- Eval that with {{{M-x eval-buffer}}}.
- Now go and type some text in ~*my-elnode-editor-buffer~*. This will be
- served by the editor service.
- Now let's start the service:
- {{{
- M-x elnode-start my-elnode-editor-handler 8002 localhost
- }}}
- Now try and hit [[http://localhost:8002]] - you should see whatever
- you typed in the ~*my-elnode-editor-buffer~*.
- Try updating the text in the buffer and refreshing the browser. We're
- displaying that buffer whatever it has in it.
- Ok. So we've published a buffer. But what about someone else updating
- it?
- Let's make another handler to handle updates, add this to your {{{my-elnode-editor.el}}}:
- {{{
- (defun my-elnode-editor-update-handler (httpcon)
- (let ((change-text (elnode-http-param httpcon "change")))
- (with-current-buffer my-elnode-editor-buffer
- (goto-char (point-max))
- (if (stringp change-text)
- (insert change-text))))
- (elnode-http-start httpcon 302 '("Location" . "/"))
- (elnode-http-return httpcon))
- }}}
- Now we have two handlers we'll have to map them together
- somehow. Let's map one to the root ({{{/}}}) and one to
- {{{/update/}}}. Add the following code to {{{my-elnode-editor.el}}}:
- {{{
- (defconst my-elnode-editor-urls
- `(("$" . my-elnode-editor-handler)
- ("update/.*$" . my-elnode-editor-update-handler)))
- }}}
- And now we need to add a handler to do the dispatching for these URLs,
- add this to {{{my-elnode-editor.el}}} as well:
- {{{
- (defun my-elnode-editor-dispatcher-handler (httpcon)
- (elnode-dispatcher httpcon my-elnode-editor-urls))
- }}}
- //What is a dispatcher?// - a dispatcher is a handler that take a list
- of URL pattern mappings and works out, by reading the data from the
- HTTP connection, what handler should be invoked for what request.
- Now we have our new dispatcher based code we need to stop the old server:
- {{{
- M-x elnode-stop 8002
- }}}
- And now start the new server with the dispatcher handler:
- {{{
- M-x elnode-start my-elnode-editor-dispatcher-handler 8002 localhost
- }}}
- Now visit [[http://localhost:8002]] and see the buffer as it stands
- and then visit
- [[http://localhost:8002/update/?change=%0dlah+dee+dah%0d]] and see the
- updated buffer.
- == More advanced again - Make a webapp around the service ==
- Let's take our editor on another step. Let's add some static files and
- have the Elnode handlers be called by client side Javascript.
- If we're going to add some static files, we'll need a webserver. We
- already know how to do that. Once we've got some javascript though,
- we'll probably not want to retrieve the text by {{{HTTP GET}}}ing the
- root url, so let's alter that binding to {{{/text/}}} as well:
- {{{
- (defconst my-elnode-editor-webserver-handler
- (elnode-webserver-handler-maker "~/my-directory"))
- "The webserver handler.")
- (defconst my-elnode-editor-urls
- '(("text/$" . my-elnode-editor-handler)
- ("update/.*$" . my-elnode-editor-update-handler)
- ("[^/]+/.*" . my-elnode-editor-webserver-handler)))
- }}}
- Obviously {{{~/my-directory}}} needs to be the place where you are
- going to save your HTML and Javascript files.
- Now we need those HTML and Javascript files. Let's make the HTML
- first:
- {{{
- <html>
- <head>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"
- language="Javascript">
- </script>
- <script src="my-elnode-editor.js" language="Javascript">
- </script>
- </head>
- <body>
- <textarea id="text" cols="60" rows="10">
- </textarea>
- </body>
- </html>
- }}}
- We're going to pull jQuery from Google's Content Delivery
- Network. We've put in a placeholder for our own Javascript file and
- other than that the HTML is really just a {{{textarea}}}
- element. We'll use //that// for putting the buffer text in.
- Now, what should the Javascript do?
- * when the page loads
- * make an AJAX call to Elnode for the buffer text
- * stick the received text into the {{{textarea}}}
- Ok. So here is {{{my-elnode-editor.js}}}:
- {{{
- var my_elnode_editor = (function () {
- var self = {
- /** Get the text from Emacs.
- */
- get_text: function () {
- $.ajax("/text/", {
- dataType: "text",
- success: function (data, textStatus, jqXHR) {
- $("#text").text(data);
- }
- });
- }
- };
- return self;
- })();
- $(document).ready(
- function () {
- my_elnode_editor.get_text();
- }
- );
- }}}
- Save this as {{{my-elnode-editor.js}}} (in whatever directory the
- webserver is serving) and save the HTML in the same directory, call it
- {{{my-elnode-editor.html}}}, say?
- You don't even have to restart the Elnode handler, because it already
- is pointing to the dispatcher handler. If you just:
- {{{
- M-x eval-buffer
- }}}
- this will re-evaluate the URL mappings. Now if you visit
- [[http://localhost:8002/my-elnode-editor.html]] you should see the
- webpage with the {{{textarea}}} and the text of your buffer.
- == That's all for now! ==
- This is as far as Nic has got writing the tutorial. More will come soon I hope:
- * {{{defer}}} with an example based around the editor service
- * debugging a running Elnode service