120 | | The `TodoList` component notifies the observers inside the `add()` method by iterating over `self.observers` and calling the `todo_added()` method for each. This works because the `observers` attribute is a [http://users.rcn.com/python/download/Descriptor.htm descriptor]: When it is accessed, it finds all ''enabled'' components that declare to extend the extension point. For each of those components, it gets the instance from the component manager, potentially activating it if it is getting accessed for the first time. |
121 | | |
122 | | ''Note:'' Only component classes that are enabled (in the `[components]` section of [wiki:TracIni trac.ini]) will be available when iterating over an extension point (like in the code above). Thus it's ''not enough'' to only instantiate a component (e.g. by calling the component constructor as `ComponentName(self.compmgr)`) but one needs to enable it as well. |
| 120 | The `TodoList` component notifies the observers inside the `add()` method by iterating over `self.observers` and calling the `todo_added()` method for each. This works because the `observers` attribute is a [http://users.rcn.com/python/download/Descriptor.htm descriptor]: When it is accessed, it finds all ''enabled'' components (see [#component_lifecycle below]) that declare to extend the extension point. For each of those components, it gets the instance from the component manager, potentially activating it if it is getting accessed for the first time. |
| 168 | == Component Lifecycle and State == #component_lifecycle |
| 169 | This section will shed some light on how components come to be. It describes some inner workings and terminology that you may come across when trying to understand components, and provides information about where the specific parts are implemented. |
| 170 | |
| 171 | First of all, let's repeat the basic rules for components: |
| 172 | |
| 173 | '''Components are singletons. They should be stateless.''' |
| 174 | |
| 175 | This means each component only exists once (within a single process). It's designed to be reused for multiple - possibly concurrent - web request. So it can't (read: should not) store information from one request in its class members and reuse this information in the next request. |
| 176 | |
| 177 | Now, the following list describes the stages a component goes through until it's ready to be used: |
| 178 | |
| 179 | 1. ''Registration:'' The first step is to register a component so that Trac knows about it. This happens automatically when the Python file contain the component gets imported for the first time. This is done either with a `import` statement in a file that has already been imported, or - like in most cases - by listing the file in `entry_points` section of a plugin (see [wiki:TracDev/PluginDevelopment#Packagingplugins Packaging Plugins]).[[BR]] |
| 180 | The registration is handled by `trac.core.ComponentMeta` using [http://www.ibm.com/developerworks/linux/library/l-pymeta.html metaclass programming]. |
| 181 | 1. ''Activation:'' A component gets activated when it's first used. So, "activation" is basically just another word for "instantiation", though since a component is a singleton it's only activate/instantiated once. When a component gets activated, Trac's component manager adds some useful fields to the component (specifically: `env`, `log`, and `config`).[[BR]] |
| 182 | A component can be activated by one of the following ways: |
| 183 | * when its manually constructed for the first time (using `Component(compmngr)`) |
| 184 | * when one of the extension points the component implements is used for the first time. Note that the component must be enabled for this way to work (see below). |
| 185 | |
| 186 | The activation and making sure that only one instance of a component exists is handled by `trac.core.Component.__new__()`. This method then calls `trac.core.ComponentManager.component_activated()`. In Trac the component manager is usually `trac.env.Environment`. |
| 187 | |
| 188 | As stated above components can be either "enabled" or "disabled". The main (and only) difference between these states is: |
| 189 | |
| 190 | '''Extension points will ''only'' use enabled components.''' |
| 191 | |
| 192 | This means that the extension point methods (like `todo_added` in the example above) of a ''disabled'' component (that implements a certain extension point interface) ''won't be called''. Note also that even disabled components can be activated (instantiated), but only by constructing them manually (as mentioned before). |
| 193 | |
| 194 | Enabling a component is done in the `[components]` section of [wiki:TracIni trac.ini]. This is implemented in `trac.env.Environment.is_component_enabled()`. Whether a component is enabled or disabled is check ''only once'' when an extension point that component implements is first used. |
| 195 | |