Commit | Line | Data |
4536f655 |
1 | |
2 | Stem Object and Cell Creation and Configuration Design Notes |
3 | |
4 | All Stem cells and objects share the same API style in their constructor |
5 | methods (commonly 'new'). All parameters are passed as key/value |
6 | attributes and processed with Stem::Class::parse_args which is driven |
7 | with a simple table of field descriptions. Each field is described by an |
8 | anonymous hash with attribute/values. Each allowed field must have a |
9 | name and it also can have several optional description attributes. Here |
10 | are the supported field description attributes: |
11 | |
12 | name The value of this is the name of this |
13 | field. Obviously the field name is required. |
14 | |
15 | required This is a boolean value that says this field |
16 | is required. If it is not set in the constructor |
17 | call, an error string is returned. |
18 | |
19 | default The value for this is the default for this |
20 | field. It is used if this field is not set in |
21 | the constructor. |
22 | |
23 | class The value for this is a Stem class (Perl |
24 | package) name. It means the value of this |
25 | parameter will be parsed as the arguments to the |
26 | constructor ('new' for now) of that class. The |
27 | object returned is saved as the value of this |
28 | field. If the called constructor returns an |
29 | error string, it is returned. |
30 | |
31 | class_args The value of this is an anonymous list |
32 | of attribute/value pairs for this class |
33 | field. They are passed after the caller's |
34 | arguments and so will override any duplicate |
35 | passed in parameters. |
36 | |
37 | callback The value of this is a code reference which is |
38 | called to do custom construction of this |
39 | field. The code ref is called with the new |
40 | object, the field name and the anonymous |
41 | reference which has the field's passed in |
42 | values. |
43 | |
44 | type The value for this attribute is the type of the |
45 | field. Currently unsupported, it is meant for |
46 | stronger type checking of parameters such as |
47 | booleans. This will be supported soon. |
48 | |
49 | env The value for this attribute is a name of a Stem |
50 | environment variable. If this name is found in |
51 | %Stem::Vars::Env then the value in that |
52 | hash is used as the value for this |
53 | attribute. This overrides any value passed in |
54 | the the constructor or a default option. |
55 | NOTE: Stem environment variables can be set from |
56 | many places including the command line, the |
57 | shell environment, command messages and the terminal. |
58 | |
59 | Here is a typical constructor from the Stem::Log class. It has 3 fields |
60 | of which the first is required the other two have defaults. The |
61 | beginning of the constructor sub is shown below and that same two lines |
62 | of code is used in almost every class constructor. |
63 | |
64 | |
65 | my $field_info = [ |
66 | |
67 | { |
68 | 'name' => 'log', |
69 | 'required' => 1, |
70 | }, |
71 | { |
72 | 'name' => 'level', |
73 | 'default' => 'info', |
74 | }, |
75 | { |
76 | 'name' => 'text', |
77 | 'default' => '', |
78 | }, |
79 | |
80 | ] ; |
81 | |
82 | sub new |
83 | |
84 | my $self = Stem::Class::parse_args( $field_info, @_ ) ; |
85 | return $self unless ref $self ; |
86 | |
87 | |
88 | Object Creation Error Handling |
89 | |
90 | Stem cells and objects are being created all the time and in many |
91 | ways. There is a standard way Stem constructor subs return errors. If the |
92 | object construction works, it returns the object. If there is an error, |
93 | it returns the error string. The caller must test constructor |
94 | returns with ref to see if they worked. This makes it easy to pass back |
95 | error messages to higher level objects with this code shown above. |
96 | |
97 | The first line parses the passed arguments (in @_) with a field |
98 | description table. The second line tests if an object was created. If it |
99 | was (ref $self is true), then the constructor continues. Otherwise, the |
100 | error string is just returned to the caller. So the exact low level |
101 | error is propagated up the call tree. This is used consistently in all |
102 | of the constructors so even if you have a class which has a field which |
103 | is a class (see the 'class' field type above), and it has a parse error, |
104 | that error will be passed all the way up to the highest level call |
105 | (typically in the Stem::Config module). |
106 | |
107 | Stem Cell Configuration |
108 | |
109 | Stem cells are typically created by the Stem::Conf class. The primary |
110 | source of configuration data is from a file and that is handled by the |
111 | load method. Currently config files are legal Perl and are just parsed |
112 | with string eval. (Don't yell, I know it sucks but it is simple to |
113 | bootstrap.) The next revision of this module will tighten up the |
114 | specifications of config files and create a proper parser for it. The |
115 | parser will probably use Parse::RecDescent (written by our advisor |
116 | Damian Conway who will probably write the parser for us :). The config |
117 | syntax will probably be similar to what it is now, but barewords |
118 | (actually any token without whitespace) will be allowed anywhere. Only |
119 | value strings with white space will need to be quoted. Config keywords will |
120 | always be barewords. Fat comma will be supported and [] will demark |
121 | lists of anything. There won't be any hashes since this is really just a |
122 | mass of initializations and a list is fine to initialize a hash. |
123 | |
124 | A Stem cell configuration is comprised of a list of attribute/value |
125 | pairs. You can also have a list of cell configurations in one file, |
126 | but each configuration is handled independently. Each configuration |
127 | entry has only a few options in the usual format of key/value pairs. The |
128 | first is 'class', which is required and it names the Stem class which |
129 | will be configured. The next one is 'name' and it is optional but almost |
130 | always used. It is the name that this cell will be registered as and |
131 | that is the address that will be used to send messages to this cell. The |
132 | last major option is 'args' and its value is a list of attribute/value |
133 | pairs used to initialize the cell. Which set of configuration options |
134 | is what controls how a cell is created and/or registered. |
135 | |
136 | The 'class' option is first processed and if it is not loaded, Stem will |
137 | load it for you. This can be done remotely which allows for a servlet |
138 | style of behavior, i.e. a request can come in and be a configuration or |
139 | converted to one and the required Stem class will be loaded and a cell |
140 | created. That cell can then be passed a message and respond to it. All |
141 | of that can occur at runtime on demand. |
142 | |
143 | If the 'args' option is set, then a constructor of the class is called |
144 | and it is passed the attribute/value pairs from the list value of |
145 | 'args'. The constructor method is defaulted to 'new' but that can be |
146 | overridden with the 'method' option. The constructor processes its |
147 | arguments (most likely using the Stem::Class::parse_args routine |
148 | described above) and has 3 possible return values. If it returns undef, |
149 | nothing more is done by the Stem::Conf module for this configuration. If |
150 | a string is returned, that is assumed to be an error message and it is |
151 | either printed or returned to the originator of this configuration. Any |
152 | other configurations in this file (or passed in remote configuration) |
153 | are skipped. If the retun value is a ref, then it is assumed |
154 | to be an object and it can be registered with the address in the 'name' |
155 | option. |
156 | |
157 | If the 'name' option is set, that will be used to register the cell or |
158 | class itself. In most of the configuration cases, an object is created |
159 | by the class constructor with the 'args' option and it is then |
160 | registered as a cell with that name for its address. If no 'args' |
161 | option is set, then the class itself is registered under the 'name' |
162 | value and it is a class level cell. There can only be one class level |
163 | cell for any class although it could be registered under multiple names |
164 | (aliases). In addition, the value of the 'name' option is passed along |
165 | with the 'args' values to the constructor as the attribute 'reg_name'. |
166 | |
167 | Here are some example classes which are configured in several of those |
168 | ways: |
169 | |
170 | The Stem::Hub class needs to be initialized with a registration name |
171 | but has no other attributes. So its configuration has a 'name' and an |
172 | 'args' option whose value is an empty list (that forces the constructor |
173 | to be called). |
174 | |
175 | [ |
176 | class => 'Stem::Hub', |
177 | name => 'server', |
178 | args => [], |
179 | ] |
180 | |
181 | The Stem::TTY::Msg class configuration doesn't use the 'name' option and |
182 | it used an empty 'args' value. So its constructor is called and it |
183 | returns its single class level object, and that is registered under its |
184 | class name. |
185 | |
186 | [ |
187 | class => 'Stem::TTY::Msg', |
188 | args => [], |
189 | ] |
190 | |
191 | |
192 | The Stem::Demo::Cmd class is a class level cell that just has a 'name' |
193 | option in its configuration and that is used to register the class |
194 | itself. |
195 | |
196 | [ |
197 | class => 'Stem::Demo::Cmd', |
198 | name => 'cmd', |
199 | ] |
200 | |
201 | |
202 | The Stem::Sock::Msg is configured in the most common way, with 'name' and |
203 | 'args' options and values for the arguments. |
204 | |
205 | [ |
206 | class => 'Stem::Sock::Msg', |
207 | name => 'C', |
208 | args => [ |
209 | port => 6668, |
210 | server => 1, |
211 | data_msg => [ |
212 | to_cell => 'sw', |
213 | to_target => 'c', |
214 | ], |
215 | ], |
216 | ] |
217 | |
218 | |
219 | Normally a single configuration file is loaded by the run_stem program |
220 | at start up time. The Stem::Conf module also supports loading a |
221 | configuration file via a command message or another configuration (which |
222 | is similar to include files or Perl modules). A configuration which |
223 | loads a configuration file can evaluate in the current Hub or send it to |
224 | any Hub in the Stem system. This allows for centralized management of |
225 | Stem configurations. As an aid to this, the stem_msg program can be used |
226 | to send a 'load' command message to a Hub. |
227 | |