ACF how to write
How to Write an ACF
For some examples please see the Web Configuration Framework projects in the Alpine Linux git repository
- acf-unbound - a simple ACF to control a service
- acf-awall - a slightly more complicated ACF for a firewall
- acf-provisioning - a complicated database application based on ACF
- ...
From <nil> to a running ACF example application
Step 1 - The Programming Language
- ACF uses lua as its programming language. Have a look at lua.org before starting.
Step 2 - The Application Environment
- Setup the ACF web application by running setup-acf
Step 3 - Create A Development Directory
- 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 may have one or more files.
- Controller: The event dispatcher. Most of the controller functionality is handled by the ACF mvc.lua code and some standard controllers (such as acf_www-controller.lua or acf_cli-controller.lua). For the controller layer of your new ACF package, you must export one lua function per action in a lua module named 'myapp-controller.lua'. The ACF controller code will interpret the user interaction to load your new controller and fire the appropriate action - the same-named function in your controller will be called.
- View: The view layer defines what your application will look like. For most actions, such as forms, your application can use the built-in automatic view generation. For others, you can link to standard views which are included in the acf-core package. For other actions, such as lists of data, you may create view files, each presenting a dynamic HTML page with only as much code as necessary to display the data you receive from the controller.
- Model: The 'real work' is done in the Model (e.g. modifying config files, starting/stopping services etc.). Each action exported by your controller will call into model functions to retrieve data and carry out actions.
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:
- config.mk
- Makefile
- myapp-controller.lua
- myapp-model.lua
- myapp.roles
- myapp.menu
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
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=0.1 APP_DIST= \ myapp* \ 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
myapp-controller.lua:
-- the myapp controller local mymodule = {} mymodule.default_action = "myaction" mymodule.myaction = function(self) -- self.clientdata contains the user data -- self.model points to our model -- use the helper function to implement our form return self.handle_form(self, self.model.getdata, self.model.setdata, self.clientdata, "Submit", "Edit data", "Data Submitted") end return mymodule
myapp-model.lua:
-- acf model for myapp local mymodule = {} local cfgfile = "/tmp/myfile" -- This function returns a cfe (table of values) containing the file's -- value as a string. If the file does not exist, we'll -- simply return "" (an empty string, but NOT nil) mymodule.getdata = function(self, clientdata) local retval = cfe({ type="group", value={}, label="Data" }) retval.value.data = cfe({ type="longtext", label="Data" }) local fileptr = io.open(cfgfile, "r") if fileptr ~= nil then retval.value.data.value = fileptr:read("*a") or "" fileptr:close() end return retval end -- This function will write new contents into our file -- The newdata parameter receives the same cfe as returned by getdata, now with the user data filled in mymodule.setdata = function(self, newdata, action) fileptr = io.open( cfgfile, "w+" ) if fileptr ~= nil then fileptr:write(newdata.value.data.value) fileptr:close() else newdata.errtxt = "Failed to save data" end return newdata end return mymodule
myapp.roles:
GUEST=myapp:myaction
myapp.menu:
# Cat Group Tab Action Test MyApp MyAction myaction
Step 6 - What Does It Do?
This program just displays a <textarea> box and a "Submit" button. The user can enter text that is saved into a file once he presses "Submit".
In Depth
Now let us have a closer look at the different files' contents:
myapp-controller.lua
The controller is an event dispatcher. So, 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 myaction - a simple form.
This function can call the model's functions to update and/or retrieve data (e.g. self.model.getdata()).
Anything that this function returns will be passed on to the view
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'.
In our case, we have implemented the getdata/setdata functions required for a form.
The getdata function receives a copy of 'self', a clientdata table, and a string containing the submit action. It will generate a 'CFE' table defining the form and including the current data.
The setdata function is only called when the form is submitted, and it receives a copy of 'self' and the updated form 'CFE' now containing the submitted data. The setdata function will attempt to perform the action, returning the same form 'CFE'. If there is an error, it will fill in the errtxt field of the 'CFE'.
myapp.roles
This file determines which users have access to which controllers and views. A separa ... \n
private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
Offers private label cosmetics with custom design. We produce lip balms, serum, creams and more. Easy to buy with eshop. Private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
Offers private label cosmetics with custom design. We produce lip balms, serum, creams and more. Easy to buy with eshop. Private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
Offers private label cosmetics with custom design. We produce lip balms, serum, creams and more. Easy to buy with eshop. Private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
Offers private label cosmetics with custom design. We produce lip balms, serum, creams and more. Easy to buy with eshop. Private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
Offers private label cosmetics with custom design. We produce lip balms, serum, creams and more. Easy to buy with eshop. Private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]
[private label cosmetics, custom made cosmetic, custom made cream, custom lip stick lip balm]