ACF how to write: Difference between revisions

From Alpine Linux
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 once called does install our acf application so that we can look at it working.
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 = ...
    option = form.option
  ?>
  ?>
  &lt;h1>MyApp - MyView&lt;/h1>
  &lt;h1>MyApp - MyView&lt;/h1>
Line 156: Line 157:
  module (..., package.seeall)  
  module (..., package.seeall)  
   
   
--- default code up here
--- do not change anything except: self.conf.action for redirect
local list_redir = function( self )
    self.conf.action = "myview"
    self.conf.type = "redir"
    error (self.conf)
end
local pvt = {}
  mvc= {}
  mvc= {}
  mvc.on_load = function( self, parent )
  mvc.on_load = function( self, parent )
     if ( rawget(self.worker, self.conf.action) == nil ) then
     self.conf.default_action = "myview"
      list_redir(self)
    end
    pvt.parent_on_exec = parent.worker.mvc.post_exec
  end
  end
-- This is where 'our' code starts
   
   
  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 only just displays a &lt;textarea> box and a submit "update" button. The user can enter text which is saved into a file once he presses "update".
This program just displays a &lt;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.menu=====
=====myapp-model.lua=====
In this file you define:
The functions defined in here can be accessed by the controller
* '''The Category''' in which a menu entry for your program will appear
to update/set/retrieve data, start/stop services, basically do
* '''The Group''', resp. the subheading's name under Category
any 'real work'.
* '''The Action''' with-in your controller that will be called once the user klicks on the menu entry defined by Category and Group.
 
=====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 .. &lt;? .. ?&gt;)''.


=====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, so can define a separate view file using the nameage: myapp-''action''-html.lsp
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-model.lua=====
=====myapp.roles=====
The functions defined in here can be accessed by the controller
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 update/set/retrieve data, start/stop services, basically do
group=controller:action[,controller:action]
any 'real work'.
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-myview-html.lsp=====
=====myapp.menu=====
This is our ''view''. It receives the data to be displayed via ''controller''. What ever is returned by a controller action (function) can be accessed by the view ''(see the first three lines .. &lt;? .. ?&gt;)''.
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 the so called
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

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.

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?

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: