* Added ffluci.util.instanceof function
[project/luci.git] / src / ffluci / cbi.lua
1 --[[
2 FFLuCI - Configuration Bind Interface
3
4 Description:
5 Offers an interface for binding confiugration values to certain
6 data types. Supports value and range validation and basic dependencies.
7
8 FileId:
9 $Id$
10
11 License:
12 Copyright 2008 Steven Barth <steven@midlink.org>
13
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at 
17
18         http://www.apache.org/licenses/LICENSE-2.0 
19
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
25
26 ]]--
27 module("ffluci.cbi", package.seeall)
28 require("ffluci.template")
29 require("ffluci.util")
30 local class = ffluci.util.class
31 local instanceof = ffluci.util.instanceof
32
33
34 -- Node pseudo abstract class
35 Node = class()
36
37 function Node.__init__(self, title, description)
38         self.children = {}
39         self.title = title
40         self.description = description
41         self.template = "cbi/node"
42 end
43
44 function Node.append(self, obj)
45         table.insert(self.children, obj)
46 end
47
48
49 --[[
50 Map - A map describing a configuration file 
51 ]]--
52 Map = class(Node)
53
54 function Map.__init__(self, config, ...)
55         Node.__init__(self, ...)
56         self.config = config
57         self.template = "cbi/map"
58 end
59
60 function Map.render(self)
61         ffluci.template.render(self.template)
62 end
63
64 function Map.section(self, class, ...)
65         if instanceof(class, AbstractClass) then
66                 local obj = class(...)
67                 obj.map = self
68                 table.insert(self.children, obj)
69                 return obj
70         else
71                 error("class must be a descendent of AbstractSection")
72         end
73 end
74
75
76 --[[
77 AbstractSection
78 ]]--
79 AbstractSection = class(Node)
80
81 function AbstractSection.__init__(self, ...)
82         Node.__init__(self, ...)
83 end
84
85 function AbstractSection.option(self, class, ...)
86         if instanceof(class, AbstractValue) then
87                 local obj = class(...)
88                 obj.section = self
89                 obj.map     = self.map
90                 table.insert(self.children, obj)
91                 return obj
92         else
93                 error("class must be a descendent of AbstractValue")
94         end     
95 end
96         
97
98
99 --[[
100 NamedSection - A fixed configuration section defined by its name
101 ]]--
102 NamedSection = class(AbstractSection)
103
104 function NamedSection.__init__(self, section, ...)
105         AbstractSection.__init__(self, ...)
106         self.section = section
107         self.template = "cbi/nsection"
108 end
109
110
111 --[[
112 TypedSection - A (set of) configuration section(s) defined by the type
113         addremove:      Defines whether the user can add/remove sections of this type
114         anonymous:  Allow creating anonymous sections
115         valid:          a table with valid names or a function returning nil if invalid
116 ]]--
117 TypedSection = class(AbstractSection)
118
119 function TypedSection.__init__(self, sectiontype, ...)
120         AbstractSection.__init__(self, ...)
121         self.sectiontype = sectiontype
122         self.template  = "cbi/tsection"
123         
124         self.addremove = true
125         self.anonymous = false
126         self.valid     = nil
127 end
128
129
130 --[[
131 AbstractValue - An abstract Value Type
132         null:           Value can be empty
133         valid:          A table with valid names or a function returning nil if invalid
134         depends:        A table of option => value pairs of which one must be true
135 ]]--
136 AbstractValue = class(Node)
137
138 function AbstractValue.__init__(self, option, ...)
139         Node.__init__(self, ...)
140         self.option  = option
141         
142         self.null    = true
143         self.valid   = nil
144         self.depends = nil
145 end
146         
147
148 --[[
149 Value - A one-line value 
150         maxlength:      The maximum length
151         isnumber:       The value must be a valid (floating point) number
152         isinteger:  The value must be a valid integer
153 ]]--
154 Value = class(AbstractValue)
155
156 function Value.__init__(self, ...)
157         AbstractValue.__init__(self, ...)
158         self.template  = "cbi/value"
159         
160         self.maxlength = nil
161         self.isnumber  = false
162         self.isinteger = false
163 end
164
165
166 --[[
167 Boolean - A simple boolean value        
168 ]]--
169 Boolean = class(AbstractValue)
170
171 function Boolean.__init__(self, ...)
172         AbstractValue.__init__(self, ...)
173         self.template = "cbi/boolean"
174 end