Friday, January 14, 2011

Nitrogen, an Erlang web application library/framework Part 3

Slide 28: What is a Nitrogen Action?


I'm feeling increasingly perturnbed by these form elements used outside a form. Can you tell I'm an HTML newbie? After implementing the example and refreshing the page, the alert fires immediately, which surprised me some. I'm going to guess that the submit button will be effectively a refresh.

Nope. Apparently it's a no-op. The alert didn't fire on clicking Submit. I'm going to try wireshark on the loopback interface to see if there was any browser traffic.

OK; it seems neither the submit button nor the alert generates any activity from the browser.

Slide 29: What is a Nitrogen Action?


So "alert('hello');" is the same thing as #alert { text="hello" }.

Does page source differ? Except for generated names, no. Something I didn't know before: when Firefox does a "save page as" it stores the referenced javascript files and alters the page to refer to the store version. Very useful.

Slide 30: Wiring an action


First use of #effect. pulsate fades the element in and out four times, then fades it in permanently.

Slide 31: Conditional Actions with #event{}


I infer that if I wanted the alert to occur when "Submit" in the previous examples was clicked, then it should have been attached to a click event. First, the example as shown.

OK, let's associate the alert with the click:

body() ->
wf:wire(mybutton, #event {
type=click,
actions=#alert { text="hello" }
}),
[
#button { id = mybutton, text = "Submit" }
].

Yup, that worked.

Slide 32: Triggers and Targets


I found this opaque until running the example. Here's how I think of it:

  • Events Have Types, e.g., "click"

  • Event Types Have Triggers

  • Triggers Refer to Elements


Thus, an event of a specific type must happen within a specific element.


  • Events Have Actions

  • Actions Have Targets

  • Targets Refer to Elements


Slide 33: Triggers and Targets


Important summary.

Slide 37: My First postback


Message sent back to ("queued") to server.

Slide 38: Postback Shortcuts


Different elements, different implicit events.

Slide 40: More Event Examples


"OtherEvent" is misspelled as "MyEvent" in the example. Should look like:

event(my_click_event) ->
?PRINT({click, now()});
event(OtherEvent) ->
?PRINT({other, OtherEvent, now()}).

PROBLEM: only the mouseout event is generating a postback? Examining the page source, only the mouseout event appears to be defined. What am I doing wrong?

%% -*- mode: nitrogen -*-
-module (my_page).
-compile(export_all).
-include_lib("nitrogen/include/wf.hrl").
-include("records.hrl").

main() -> #template { file="./site/templates/bare.html" }.

title() -> "Hello from my_page.erl!".

body() ->
wf:wire(mybutton, [
#event { type=mouseover, postback=my_mouseover_event }
#event { type=click, postback=my_click_event }
#event { type=mouseout, postback=my_mouseout_event }
]),
[
#button { id=mybutton, text="Submit" }
].

event(my_click_event) ->
?PRINT({click, now()});
event(OtherEvent) ->
?PRINT({other, OtherEvent, now()}).

Thursday, January 13, 2011

Nitrogen, an Erlang web application library/framework Part 2

Slide 17


Why are we backslash escaping the underscore? Or is that backslash supposed to be a DOS directory separator? It will get removed by any Unix shell, so, at my peril, I'm going to omit it.


bash-4.1$ ./dev page my_page
Created page: ./site/src/my_page.erl
Remember to recompile!
bash-4.1$ emacs ../site/src/my_page.erl
^Z
[1]+ Stopped emacs ../site/src/my_page.erl
bash-4.1$ bg
[1]+ emacs ../site/src/my_page.erl &
bash-4.1$ pwd
/home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
bash-4.1$ ./dev compile
:: MAKE - site
Recompile: ./src/my_page
:: MAKE - ./site
:: Done!


Seems to be working...

That's odd: tutorial says use port 8080 - how'd we switch from port 8000 to port 8080?

Browser says: Unable to connect. Let's try port 8000.

That worked.


Slide 19


Puzzling: "module:body()" seems to mean a module name is explicitly needed when interpolating(?) erlang "callouts", and "script" seems to imply that the script name is globally scoped within the browser (let's assume that erlang doesn't contain an internal javascript interpreter, ok?). Or is "script" supposed to also follow the "module:function(Args)" format, with the erlang result comprised of javascript code to be executed? Presumably this will be revealed...


Slide 20


Default template is what? "page:body()" doesn't occur in any version of site/src/my_page.erl nor site/src/index.erl. grep for it.


bash-4.1$ grep -Rl 'page:body()' ..
../site/templates/bare.html
../doc/org-mode/tutorial.html
../doc/org-mode/tutorial.org
../doc/html/tutorial.html

Ah, it must be ../site/templates/bare.html.

OK, that's me being dumb: site/templates/bare.html is explicitly referenced in index.erl and my_page.erl.

Doing the replacement suggested by the tutorial, without doing any compilation, removes all the page content, but provokes no error messages. Confirm by defining body1/0 in index.erl - which needs compilation. Interpolation confirmed.

"page" refers to the generic corresponding erlang module, e.g. "index.erl" or "my_page.erl" (for .../my/page). Any specific module:function can be referenced from within the template; an arbitrary expression causes a failure in "-convert_callback_tuple_to_function/4-fun-0-".

Slide 22

.
No surprises, although a <label> element normally occurs in a form, which isn't present here.

Slide 25


Again, where is the <form>?

Slide 26


A #panel is equivalent to a <div>.

Nitrogen, an Erlang web application library/framework Part 1

The point to this blog is to record my experience following various tutorials.
First up: http://nitrogenproject.com/doc/tutorial.html

I'm working on Fedora 14 with the erlang packages installed.

Fifth screen "Install & Run Nitrogen":


bash-4.1$ bin/nitrogen console
cat: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: Is a directory
bin/nitrogen: line 102: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: is a directory
Exec: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/erts-5.8.1/bin/erlexec -boot /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/releases/2.0.4/nitrogen -embedded -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/app.config -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/inets.config -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: -config dev -config nitrogen -args_file /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/vm.args -- console
Root: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen
bin/nitrogen: line 128: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: is a directory
bin/nitrogen: line 128: exec: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: cannot execute: Success


That "Success" at the bottom was difficult to see...

And firefox says "unable to connect"...

And netstat shows nothing listening to port 8000.


Aha! What works is


bash-4.1$ cd bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
bash-4.1$ ls
dev nitrogen
bash-4.1$ ./nitrogen
Usage: nitrogen {start|stop|restart|reboot|ping|console|attach}
bash-4.1$ ./nitrogen console
Exec: /home/sps/erlang-libs/nitrogen/rel/nitrogen/erts-5.8.1/bin/erlexec -boot /home/sps/erlang-libs/nitrogen/rel/nitrogen/releases/2.0.4/nitrogen -embedded -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/app.config -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/inets.config -args_file /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/vm.args -- console
Root: /home/sps/erlang-libs/nitrogen/rel/nitrogen
Erlang R14B (erts-5.8.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:5] [kernel-poll:true]

Eshell V5.8.1 (abort with ^G)
(nitrogen@127.0.0.1)1>


Slide 10, they don't mention that the "Welcome..." string occurs in two places. Why is that not an expanded variable?

I had aborted out to explore the directory tree from the command line, so I needed to start nitrogen again. Restarting did not automatically reflect the changes, so here we go: sync:go() from the command line:


bash-4.1$ ./nitrogen console
Exec: /home/sps/erlang-libs/nitrogen/rel/nitrogen/erts-5.8.1/bin/erlexec -boot /home/sps/erlang-libs/nitrogen/rel/nitrogen/releases/2.0.4/nitrogen -embedded -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/app.config -config /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/inets.config -args_file /home/sps/erlang-libs/nitrogen/rel/nitrogen/etc/vm.args -- console
Root: /home/sps/erlang-libs/nitrogen/rel/nitrogen
Erlang R14B (erts-5.8.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:5] [kernel-poll:true]

Eshell V5.8.1 (abort with ^G)
(nitrogen@127.0.0.1)1> sync:go().
:: MAKE - site
Recompile: ./src/index
:: MAKE - ./site
:: Done!
ok


That worked.


Slide 11: again, invoking
bin/dev compile
from its parent directory failed.

But invoking from within the bin directory succeeds:


bash-4.1$ bin/dev compile
cat: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: Is a directory
bin/dev: line 60: /home/sps/erlang-libs/nitrogen/rel/nitrogen/bin: is a directory
Node is not running!
bash-4.1$ pwd
/home/sps/erlang-libs/nitrogen/rel/nitrogen
bash-4.1$ cd bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
bash-4.1$ ./dev compile
:: MAKE - site
Recompile: ./src/index
:: MAKE - ./site
:: Done!


Slide 12: doesn't say where in the file to add those lines; I'm going to guess that ?DEBUG is on its own line immediately after the -include_libs on the fourth line.

Bad guess:


bash-4.1$ cd bin
/home/sps/erlang-libs/nitrogen/rel/nitrogen/bin
bash-4.1$ ./dev compile
:: MAKE - site
Recompile: ./src/index
./src/index.erl:5: syntax error before: ':'
:: Errors!


That looks like a macro expansion. Let's google for an example in other code...

No specific example, but let's try adding this line instead:

-define(DEBUG, 1)


Nope:


bash-4.1$ ./dev compile
:: MAKE - site
Recompile: ./src/index
./src/index.erl:5: redefining macro 'DEBUG'
:: Errors!


Next, I'll try adding ?DEBUG to the inner_body() list.


bash-4.1$ ./dev compile
:: MAKE - site
Recompile: ./src/index
:: MAKE - ./site
:: Done!

Promising...

But this is all that came back from a browser refresh:

Internal Server Error


This is what is on the console:

=INFO REPORT==== 13-Jan-2011::16:19:53 ===
DEBUG: index:29

=INFO REPORT==== 13-Jan-2011::16:19:53 ===
{error,throw,
{unanticipated_case_in_render_elements,ok},
[{wf_render_elements,render_elements,2},
{lists,foldl,3},
{wf_render_elements,render_elements,2},
{lists,foldl,3},
{wf_render_elements,render_elements,2},
{wf_render_elements,call_element_render,2},
{wf_render_elements,render_element,1},
{wf_render_elements,render_elements,2}]}


Maybe that was supposed to happen. Try restarting the server...

Same result.

Instead of ?DEBUG try "?DEBUG": renders the page with the literal string ?DEBUG on it. I suppose ?DEBUG should be when the analog to an assert fails?

Trying ?PRINT(node()) instead of "?DEBUG":


=INFO REPORT==== 13-Jan-2011::16:30:29 ===
DEBUG: index:30
"node ( )"
'nitrogen@127.0.0.1'

=INFO REPORT==== 13-Jan-2011::16:30:29 ===
{error,throw,
{unanticipated_case_in_render_elements,ok},
[{wf_render_elements,render_elements,2},
{lists,foldl,3},
{wf_render_elements,render_elements,2},
{lists,foldl,3},
{wf_render_elements,render_elements,2},
{wf_render_elements,call_element_render,2},
{wf_render_elements,render_element,1},
{wf_render_elements,render_elements,2}]}


inner_body() doesn't seem to like these macros as part of its list argument; how about as an result-ignored function at the beginning of its definition?

Much better! Page renders, and console simply says:

DEBUG: index:16
"node ( )"
'nitrogen@127.0.0.1'


Slide 13: first attempt at .emacs config generates Symbol's function definition is void: erlang-mode on attempting M-x nitrogen-mode. Try to add erlang mode...

Hmmm. Fedora didn't include the emacs erlang mode in the erlang super-package. So:


sudo yum -y install emacs-erlang-el.noarch emacs-erlang.noarch

OK, now its working.