Context
In the last post we discussed how Nitrogen inits a new Context for each request and stores it in the process dictionary.
Conceptually, Nitrogen uses Context to pass the page state from the server to browser and then, on postback, it passes this state from the browser back to the server and restores it to the new instance of Context.
This is hugely important, when Nitrogen generates the html for the page, it also serializes whatever data are found in #page_context{} and in the Handlers list of the Context and passes it to the browser in the Response message and then stores it on the page in ‘pageContext’ variable. These data are of course hidden but if you inspect the page in Firebug you can find it in the script section.
Context record looks like this:
Context = #context { request_bridge = RequestBridge, response_bridge = ResponseBridge, page_context = #page_context { series_id = wf:temp_id() }, event_context = #event_context {}, handler_list = [ % Core handlers... make_handler(config_handler, default_config_handler), make_handler(log_handler, default_log_handler), make_handler(process_registry_handler, nprocreg_registry_handler), make_handler(cache_handler, default_cache_handler), make_handler(query_handler, default_query_handler), % Stateful handlers... make_handler(session_handler, simple_session_handler), make_handler(state_handler, default_state_handler), make_handler(identity_handler, default_identity_handler), make_handler(role_handler, default_role_handler), % Handlers that possibly redirect... make_handler(route_handler, dynamic_route_handler), make_handler(security_handler, default_security_handler) ] }
as you can see it has keys for Request/Response Bridge, #page_context{} – which describes html elements that you have on the page and which gets sent to the browser in serialized form, #event_context{} – which describes the javascript actions. It also has a list of handlers – which are the references to the modules which implement certain behaviors and their state, this data also get sent to the browser in serialized form.
#handler_context { name=Name, module=Module, state=[] }.
The example of the handler could be a query_handler, which runs when you use API function wf:q(AtomKey). The implementation logic in the query_handler.erl knows how to retrieve the element from the pageContext and how to find its value. Lots of Nitrogen API functions are just wrappers around corresponding Handler modules. E.g. looking into module wf.erl you can see things like:
params() -> query_handler:get_params(). %%% EXPOSE LOG_HANDLER %%% info(String, Args) -> ok = log_handler:info(String, Args). %%% EXPOSE SESSION_HANDLER %%% session(Key) -> _Value = session_handler:get_value(Key). %%% EXPOSE STATE_HANDLER %%% state(Key) -> _Value = state_handler:get_state(Key).
which allow you to get/set values in the Context via corresponding handler calls.
The really cool part is that handlers could be replaced by your own implementations. If, for example, you need to change Nitrogen logging you can implement your own logic and plug it in Nitrogen during initialization.
You can find the documentation page for Nitrogen Handlers here.
So, at this checkpoint we have :
- RequestBridge object which contains the parsed http request.
- new instance of Context object which is stored in process dictionary
Now, we can run Nitrogen to build a html which will be returned to the client:
{ok, Req2} = nitrogen:run()
calls:
run() -> wf_core:run().
which calls the following code (slightly modified for clarity):
run() -> Request = wf_context:request_bridge(), Response = wf_context:response_bridge(), try run_catched() end.
I have to emphasize that Nitrogen in this instance is used purely as html building library. It is not doing any page routing and it is not even running in its own process but using the process started by Cowboy.
Please correct me if you find something wrong with the above description.
I will continue the explanation in the next post.
Pingback: How Nitrogen processes requests – part 3 | R.Shestakov