ACF how to write: Difference between revisions
Line 36: | Line 36: | ||
* myapp-myview-html.lsp | * myapp-myview-html.lsp | ||
* myapp-controller.lua | * myapp-controller.lua | ||
* myapp.roles | |||
* myapp.menu | * myapp.menu | ||
Line 41: | Line 42: | ||
'''Makefile:''' | '''Makefile:''' | ||
The Makefile | The Makefile is called to install our acf application so that we can see it working. | ||
APP_NAME=myapp | APP_NAME=myapp | ||
PACKAGE=acf-$(APP_NAME) | PACKAGE=acf-$(APP_NAME) | ||
Line 49: | Line 50: | ||
myapp-myview-html.lsp \ | myapp-myview-html.lsp \ | ||
myapp-controller.lua \ | myapp-controller.lua \ | ||
myapp.roles \ | |||
myapp.menu | myapp.menu | ||
Line 143: | Line 145: | ||
<? | <? | ||
form = ... | form = ... | ||
?> | ?> | ||
<h1>MyApp - MyView</h1> | <h1>MyApp - MyView</h1> | ||
Line 156: | Line 157: | ||
module (..., package.seeall) | module (..., package.seeall) | ||
mvc= {} | mvc= {} | ||
mvc.on_load = function( self, parent ) | mvc.on_load = function( self, parent ) | ||
self.conf.default_action = "myview" | |||
end | end | ||
myview = function( self ) | myview = function( self ) | ||
Line 190: | Line 177: | ||
end | end | ||
'''myapp.roles:''' | |||
ALL=myapp:myview | |||
'''myapp.menu:''' | '''myapp.menu:''' | ||
Line 196: | Line 186: | ||
===Step 6 - What Does It Do?=== | ===Step 6 - What Does It Do?=== | ||
This program | This program just displays a <textarea> box and a submit "update" button. The user can enter text which is saved into a file once he presses "update". | ||
====In Depth==== | ====In Depth==== | ||
Now let us have a closer look at the different files' contents: | Now let us have a closer look at the different files' contents: | ||
=====myapp. | =====myapp-model.lua===== | ||
The functions defined in here can be accessed by the controller | |||
to update/set/retrieve data, start/stop services, basically do | |||
any 'real work'. | |||
=====myapp-myview-html.lsp===== | |||
This is our ''view''. It receives the data to be displayed from the ''controller''. Whatever is returned by a controller action (function) can be accessed by the view ''(see the first three lines .. <? .. ?>)''. | |||
=====myapp-controller.lua===== | =====myapp-controller.lua===== | ||
The controller is an event dispatcher. So in here you define all the actions that the user can call or that are defined in the menu. Each action is a separate function that will receive ''self'' as the only parameter. | The controller is an event dispatcher. So, in here you define all the actions that the user can call or that are defined in the menu. Each action is a separate function that will receive ''self'' as the only parameter. | ||
In our case the action is ''myview''. | In our case the action is ''myview''. | ||
For every action you define here, | For every action you define here, you can define a separate view file using the nameage: myapp-''action''-html.lsp | ||
This function can call the ''model's'' functions to update and/or retrieve | This function can call the ''model's'' functions to update and/or retrieve | ||
Line 219: | Line 211: | ||
Anything that this function returns will be passed on to the ''view'' | Anything that this function returns will be passed on to the ''view'' | ||
=====myapp | =====myapp.roles===== | ||
The | This file determines which users have access to which controllers and views. A separate ''roles'' file is generally defined for each ACF. The format of the files is as follows: | ||
to | group=controller:action[,controller:action] | ||
Each line defines controller:action combinations that are permitted for a particular group. '''ALL''' is a special group to which all users, including anonymous users, are members. | |||
=====myapp | =====myapp.menu===== | ||
In this file you define: | |||
* '''The Category''' in which a menu entry for your program will appear | |||
* '''The Group''' menu name under Category for this controller | |||
* '''The Tab''' name on the controller page | |||
* '''The Action''' with-in your controller that will be called once the user clicks on the menu entry or tab defined by Category, Group, and Tab. | |||
====How to exchange data between model-view-controller?==== | ====How to exchange data between model-view-controller?==== | ||
To exchange data between model, view and controller ACF uses | To exchange data between model, view and controller ACF uses ''Configuration Framework Entities (CFE)''. | ||
''Configuration Framework Entities (CFE)''. | |||
Please see [[ACF_core_principles]] for further details on CFEs. | Please see [[ACF_core_principles]] for further details on CFEs. |
Revision as of 15:30, 24 April 2008
How to Write an ACF Under Construction
For some examples please see svn
svn co svn://svn.alpinelinux.org/acf
- shorewall
- dhcp
From <nil> to a running ACF example application
Step 1 - The Programming Language
- ACF uses lua as programming language. Have a look at lua.org [1] before starting.
Step 2 - The Development Environment
- Setup an ACF Development Environment: Getting_started_with_ACF_development
Step 3 - Create A Development Directory
Once you entered the ACF Development Environment as described in step 2:
- in your user home create a directory for your application (e.g. mkdir ~/myapp)
- and cd into it (e.g. cd ~/myapp)
Step 4 - MVC, How Does It Affect My Coding?
ACF is an MVC based framework. What does this mean to you? Your application is separated into three layers: Model, View, Controller - each of which has one or more files.
- Model: In Model the 'real work' is done (e.g. modifying config files, starting/stopping services etc.)
- View: This is where you define what your application will look like. You can have one or more files, each presenting a dynamic html page which only as much code as neccessary to format the data you retrieve from Model.
- Controller: The event dispatcher. In controller you place one function per event. If the user calls the respective 'event page' (web), acf will fire an action - the same-named function in controller will be called. This function then retrieves neccessary data from Model and passes it to View to be displayed to the user.
Step 5 - The Example Files To Start With
Now let us have a look at the files we need to place into our application directory:
- Makefile
- config.mk
- myapp-model.lua
- myapp-myview-html.lsp
- myapp-controller.lua
- myapp.roles
- myapp.menu
Makefile:
The Makefile is called to install our acf application so that we can see it working.
APP_NAME=myapp PACKAGE=acf-$(APP_NAME) VERSION=1.0_alpha1 APP_DIST=myapp-model.lua \ myapp-myview-html.lsp \ myapp-controller.lua \ myapp.roles \ myapp.menu EXTRA_DIST=README Makefile config.mk DISTFILES=$(APP_DIST) $(EXTRA_DIST) TAR=tar P=$(PACKAGE)-$(VERSION) tarball=$(P).tar.bz2 install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME) all: clean: rm -rf $(tarball) $(P) dist: $(tarball) install: mkdir -p "$(install_dir)" cp -a $(APP_DIST) "$(install_dir)" $(tarball): $(DISTFILES) rm -rf $(P) mkdir -p $(P) cp $(DISTFILES) $(P) $(TAR) -jcf $@ $(P) rm -rf $(P) # target that creates a tar package, unpacks is and install from package dist-install: $(tarball) $(TAR) -jxf $(tarball) $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) rm -rf $(P) include config.mk .PHONY: all clean dist install dist-install
Remark: Should you create additional view files for example, don't forget to place their names in Makefile under APP_DIST otherwise they will not be installed later on and your application will fail with an error message.
config.mk:
For use with the Makefile. Just copy/paste it. We will look at it later.
prefix=/usr datadir=${prefix}/share sysconfdir=${prefix}/etc localstatedir=${prefix}/var acfdir=${datadir}/acf wwwdir=${acfdir}/www cgibindir=${acfdir}/cgi-bin appdir=${acfdir}/app acflibdir=${acfdir}/lib sessionsdir=${localstatedir}/lib/acf/sessions
myapp-model.lsp:
-- acf model for myapp -- Copyright(c) 2007 <Your name here> - Licensed under terms of GPL2 module (..., package.seeall) cfgfile = "/tmp/myfile" -- This function returns a cfe (table of values) containing the files' -- value as string and an error code. If the file does not exist, we'll -- simply return "" (an empty string, but NOT nil) readfile = function() retval = "" error = 0 fileptr = io.open( cfgfile, "r" ) if fileptr ~= nil then retval = fileptr:read( "*a" ) if retval == nil then retval = "" end fileptr:close() end return error, cfe({ msg = retval }) end -- This function will write new contents into our file writefile = function( newcontents ) fileptr = io.open( cfgfile, "w+" ) if fileptr ~= nil then fileptr:write( newcontents ) fileptr:close() end return end
myapp-myview-html.lsp:
<? form = ... ?> <h1>MyApp - MyView</h1> <form action="" method="POST"> <textarea name="textdata"><? io.write( form.value.msg ); ?></textarea> <input type="submit" name="cmd" value="update"> </form>
myapp-controller.lua:
-- the myapp controller module (..., package.seeall) mvc= {} mvc.on_load = function( self, parent ) self.conf.default_action = "myview" end myview = function( self ) -- self.clientdata contains the data from the html form -- in your myapp-myview-html.lsp local clidat = self.clientdata -- user did submit the form (not just call the page) if clidat.cmd then if clidat.cmd == "update" then -- user pressed update button self.model.writefile( clidat.textdata ) end end error, value = self.model.readfile() return cfe({ value = value }) end
myapp.roles:
ALL=myapp:myview
myapp.menu:
# Cat Group Tab Action Test MyApp MyView MyView
Step 6 - What Does It Do?
This program just displays a <textarea> box and a submit "update" button. The user can enter text which is saved into a file once he presses "update".
In Depth
Now let us have a closer look at the different files' contents:
myapp-model.lua
The functions defined in here can be accessed by the controller to update/set/retrieve data, start/stop services, basically do any 'real work'.
myapp-myview-html.lsp
This is our view. It receives the data to be displayed from the controller. Whatever is returned by a controller action (function) can be accessed by the view (see the first three lines .. <? .. ?>).
myapp-controller.lua
The controller is an event dispatcher. So, in here you define all the actions that the user can call or that are defined in the menu. Each action is a separate function that will receive self as the only parameter.
In our case the action is myview.
For every action you define here, you can define a separate view file using the nameage: myapp-action-html.lsp
This function can call the model's functions to update and/or retrieve data (e.g. self.model.readfile()).
Anything that this function returns will be passed on to the view
myapp.roles
This file determines which users have access to which controllers and views. A separate roles file is generally defined for each ACF. The format of the files is as follows:
group=controller:action[,controller:action]
Each line defines controller:action combinations that are permitted for a particular group. ALL is a special group to which all users, including anonymous users, are members.
In this file you define:
- The Category in which a menu entry for your program will appear
- The Group menu name under Category for this controller
- The Tab name on the controller page
- The Action with-in your controller that will be called once the user clicks on the menu entry or tab defined by Category, Group, and Tab.
How to exchange data between model-view-controller?
To exchange data between model, view and controller ACF uses Configuration Framework Entities (CFE).
Please see ACF_core_principles for further details on CFEs.
Step 7 - How To Get It Going?
Once you have completed all the above mentioned steps, go on with:
- sudo make install (this will install your app with the http server)
- point your browser to http://ip-of-your-dev-host/