Login
or
register
Source Guided Tour
Anonymous users must enter
captcha
below.
Don't put anything here
Don't put anything here
Don't put anything here
Page Name (for URL)
Page Title
Advanced Fields
Category
Prototype
Don't put anything here
Page Content
<span class="teaser"> This page guides you through Sputnik source code should you want to read it. </span> ## Get the Code This guided tour provides links to the mentioned source files in our [repository](http://gitorious.org/projects/sputnik/repos/mainline/tree/master), so you don't need to check out the source to follow along. But if you want, you can get the source from <http://gitorious.org/projects/sputnik/repos/mainline>. Note that we'll be ignoring everything in it except for two subdirectories: "sputnik" and "versium". (Note that this page does not assume that you've _installed_ Sputnik. But if you want to install it from source, you should see [[Source]].) Start with "sputnik", assuming for now that "versium" does two things for you automagically: 1. saving and retrieving nodes, 2. keeping track of history, 3. "inflating" and "activating" nodes. The latter means that depending on the type of node, when Sputnik gets it, node.content may be equal to a string (the default) or a table of data. Let's ignore for now how this works. Just trust me that when we get a node like "_config", with `node = sputnik:get_node("_config")`, we can then do things like `node.content.HOME_PAGE` to get variables defined in the body of the node. ## sputnik.cgi Sputnik can be launched in two ways: through CGILua and WSAPI. I'll focus on WSAPI here, this is the direction in which we are moving. So, let's look at `sputnik.cgi`. This file is not in the repository per se - it is created by `install.sh`. It looks something like: #! /home/yuri/sputnik/bin/lua5.1 require'luarocks.require' require'wsapi.cgi' require'sputnik' SPUTNIK_CONFIG = { VERSIUM_PARAMS = { dir = "/home/yuri/sputnik/wiki-data/" }, BASE_URL = '/cgi-bin/sputnik.cgi' } wsapi.cgi.run(sputnik.wsapi_run) In other words, we require `sputnik` and `wsapi` then call `wsapi.cgi.run()` giving it one of `sputnik`'s functions as a parameter. WSAPI will call this function every time it needs to process a request. Note that we also set a global `SPUTNIK_CONFIG` - that's our bootstrapping configuration. ## sputnik/init.lua Now let's look at [`sputnik/init.lua`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/init.lua) (`sputnik/lua/sputnik/init.lua`, to be more precise). Follow the flow of execution starting with `wsapi_run()` - the function that wsapi will call. Follow the order of execution, which goes as follows: 1. WSAPI calls `sputnik.wsapi_run(wsapi_env)` 2. `wsapi_run()` calls `sputnik.protected_run()` (a function that runs sputnik for one request catching all errors) 3. `protected_run()` calls `sputnik.unprotected_run()` (same but throws errors) 4. `unprotected_run()` creates an instance of Sputnik(), and then calls `Sputnik:run()` (note that this will change slightly - we'll start keeping track of Sputnik instances between requests). [`Sputnik:run()`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/init.lua#line412) is the function you should read most carefully: 1. It calls [`self:translate_request(request)`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/init.lua#line337) to fill in the missing requests and to do authentication. * Let's look quickly at `translate_request()` -- it accepts a WSAPI request, and the pre-processes the request parameters, doing two things main things: 1. It checks of a presense of parameter called "post_token" and unhashes them. This is a part of how we block link spam. 2. It authenticates the user, so that `request.user` ends up either being set to authenticated user name or to nil. Note that the actual authentication is done by a module that is pluggable. 2. It loads a node and "activates" it 3. It uses the suffix attached to the node name (e.g., "Node.edit") to determine what "action" it needs and looks up that action, then calls it. Now let's read the rest of Sputnik class definition. Ignore `make_url()`, `make_link()`, `add_urls()`, `add_links()`. Focus on [`Sputnik:new()`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/init.lua#line17), `Sputnik:init()` and `Sputnik:activate_node()`. Note that most of the work is offloaded to `versium.smart.repository` and `versium.smart.smartnode` (soon to be renamed `saci`). `activate_nodes()` adds some functionality to node that goes beyond what the Repository can do for us. This includes loading translations and actions. ## Actions Read parts of [`sputnik/actions/wiki.lua`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/actions/wiki.lua) (or, rather `sputnik/lua/sputnik/actions/wiki.lua`). It's a long file, but note that all functions in it have the same signature: they all accept three parameters: a node, a request (which includes the parameters in request.params) and a pointer to sputnik. They return a string and (optionally) a content type. So, basically, when the user asks for `SomeNode.foo` (note that asking for just "SomeNode" is interpreted as asking for "SomeNode.show"), we load "SomeNode" and then call `wiki.actions.foo()` with this node, the request, and an instance of sputnik as parameters. Don't read all of wiki.lua - just enough to get the pattern. Then have a look at [`sputnik/actions/css.lua`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/sputnik/lua/sputnik/actions/css.lua) to see that we can have them in more than one file. ## The Nodes Now look at [[@Root.raw|@Root.raw]]. Note that this is what actually gets saved - it's a lua file that gets evaluated into a table. This is a node from which all nodes inherit. Scroll to the bottom and look at where the variable "actions" is set: actions= [=[show = "wiki.show" show_content = "wiki.show_content" history = "wiki.history" edit = "wiki.edit" post = "wiki.post" rss = "wiki.rss" diff = "wiki.diff" code = "wiki.code" raw = "wiki.raw" raw_content = "wiki.raw_content" login = "wiki.show_login_form" sputnik_version = "wiki.sputnik_version" ]=] So, I lied: if you ask for "SomeNode.history" we don't automatically call "wiki.actions.history". We actually look in the "actions" field of the node. We can redefine it either for all nodes (by editing @Root) or for some nodes, by editing their "action" field individually or by having them all inherit from the same node. E.g.: [[@Ticket.raw|@Ticket.raw]] has: actions= [=[show = "wiki.edit"]=] This means that for a node that inherits from @Ticket, asking for `Node.show` will actually call `wiki.actions.edit`! Sneaky! Back to [[@Root.raw]]. There is another interesting field in it: "fields": fields= [=[-- Think twice before editing this ------------------------ fields = {0.0, proto="concat", activate="lua"} title = {0.1 } category = {0.2 } actions = {0.3, proto="concat", activate="lua"} config = {0.4, proto="concat" } templates = {0.5, proto="concat", activate="node_list"} translations = {0.51, proto="concat", activate="node_list"} prototype = {0.6 } permissions = {0.7, proto="concat"} content = {0.8 } edit_ui = {0.9, proto="concat"} ... ]=] This is is a special field that is used by the `versium.smart` (now "SACI") to "activate" the node. For instance, we have in it: fields = [=[ ... actions = {0.3, proto="concat", activate="lua"} ... ]=] this means: inherit the value of "actions" from the prototype, by concattenating the local value of "actions" (the one actually saved in the node) with the prototype values. E.g.: [[Raw_Dot_File.raw]], a part of [[Graphviz Demo]], says: actions= [=[show="wiki.code" svg="graphviz.dot2svg" png="graphviz.dot2png" gif="graphviz.dot2gif"]=] It doesn't specify an explicit prototype, so it inherits from "@Root" `@Root` says: actions= [=[show = "wiki.show" show_content = "wiki.show_content" history = "wiki.history" edit = "wiki.edit" post = "wiki.post" rss = "wiki.rss" diff = "wiki.diff" code = "wiki.code" raw = "wiki.raw" raw_content = "wiki.raw_content" login = "wiki.show_login_form" sputnik_version = "wiki.sputnik_version" ]=] Since `fields.actions.proto` is set to `"concat"`, we put the two together: show = "wiki.show" show_content = "wiki.show_content" history = "wiki.history" edit = "wiki.edit" post = "wiki.post" rss = "wiki.rss" diff = "wiki.diff" code = "wiki.code" raw = "wiki.raw" raw_content = "wiki.raw_content" login = "wiki.show_login_form" sputnik_version = "wiki.sputnik_version" show="wiki.code" svg="graphviz.dot2svg" png="graphviz.dot2png" gif="graphviz.dot2gif" Then we check `fields.actions.activate`. It says "lua". This means: use the function provided by `versium.inflators.lua`. We run the string through that function and it gives us back a table. Note that in that table "show" field is set to "wiki.source", since local actions come after the prototypes actions. We can also look at the `edit_ui` field in [[@Root.raw]]. This specifies what the edit form would look like. We can change this - see [[@Ticket.raw]]. ## Versium Now we get to `versium` and `versium.smart` (=SACI). Versium itself is very simple - just read [`versium/lua/versium.lua`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/versium/lua/versium.lua). It's just a simple façade to several modules that do the actual storage. [`versium.smart`](http://gitorious.org/projects/sputnik/repos/mainline/tree/master/versium/lua/versium/smart) is tricky. Most of what it does is in [`versium.smart.smartnode`](http://gitorious.org/projects/sputnik/repos/mainline/blob/master/versium/lua/versium/smart/smartnode.lua). Don't look at the code until you get a general idea of how it is _used_. But if you understood everything so far, then you are ready. Basically, it's a module that sits on top of versium, assumes that versium is storing Lua code that evaluates into a table of strings, then looks for a few special fields, such as "proto" and "fields". It uses the information in those fields to do inheritance and to figure out what to do with the rest of the fields. It keeps the original values (straight out of versium) by pushing them into metatables. It knows how to accept a list of parameters, and generate an updated payload to be stored into versium (`SmartNode:update()`)
Don't put anything here
About This Edit
Minor Edit
Edit Summary
Don't put anything here
Start
Introduction
Features
Demos
Why Lua?
What is Kepler?
Sightings
License
Sandbox
Whodunit?
Install
Installation
Basic Configuration
URLs
Custom Installation
Deployment
Troubleshooting
Problems
Tweak
Configuration
More Parameters
Permissions
Styles
Templates
I18n
Plugins
Storage
Markup
Authentication
Spam
Track
News
Releases
Project Planning
Recent Wiki Edits
Edits by Recent Users
Talk
Mailing List(s)
This Wiki
Report a Bug
Grok
Basic Concepts
Saci
Versium
Actions
WSAPI
LuaRocks
Hack
Source
Rocks
Architecture
Guided Tour
"Earth"
Coding Standard
Tasks
Powered by
Sputnik
|
XHTML 1.1