trainingwheels/trainingwheels — pull request #54 by marktheunissen (2013-02-13 05:25:45+00:00)
Title: Implement the plugins system

Monday night started with me crushing some code. I started getting into it, and the more code I crushed, the more I saw code that *needed* *crushing*. I now attend Brogrammer's Anonymous meetings and am on step 2 of 12.

Anyway sorry for the large PR. It's a good problem to have though, right? Like drowning in chocolate milk. Even though it's a large PR, it's refactoring, so lots of it is just shuffling things around, and implementations of the same pattern in different places. I'll try make up for it by explaining in detail what's happening, I think I prefer to do this here instead of commenting on the diff as I did last time - what do you think?

This PR implements a complete basic plugin system, moving all the resource definitions and environment commands into the plugins. There is far less inheritance now, which makes it much cleaner, IMHO. The backend is really starting to approach a decent structure.

We no longer have to specify a course type - this is now a dynamic thing that is determined by the plugins

Jared and I hashed this out and the 'classroom' term is confusing. I originally started using it as we may in future at some unspecified time have a feature of training wheels where it can do other things besides run the student environments on a Linux server, for example Pantheon, but this seems so far off that it's not worth considering. So I've tried to eliminate the use of 'classroom' for now, speaking rather in concrete terms like 'server' and 'provision'.

##### Items

* Namespace the config as 'tw.config' in the config.yml file, this allows us to inject all the config into our objects instead of having it scattered on $app
* Implement Observer pattern on the course, so that the course can `fireEvent()` on itself and all plugins can react to these events.
* assertValidStrings() is replaced with checking in __call function in the Environment, a much cleaner way to do it.
* The CourseFactory is much simpler now that there is less hardcoding of object creation.
* There is no 'NodejsCourse' and 'DrupalCourse' anymore. These are handled in plugins.
* There are no more 'LinuxEnv', 'CentosEnv' and 'UbuntuEnv' classes anymore. The plugins provide commands to an Environment object which is no longer subclassed.
* The Environment->__call() is where some magic happens. It's kinda handy to check parameters there.
* Check the plugins for how they provide anonymous functions as mixins for the environment. I think the result looks a lot cleaner than the previous implementation.
* Instead of plugins providing /ansible directories, they now provide /provision directories, for consistency and the option maybe of one day using a different provisioner.
* The Drupal plugin registers some anonymous observers, see that for an example of how it works.
* There is no more LAMPUser, the User class is no longer abstract and will not be subclassed. The functionality in LAMPUser is now handled by observers.
* The way that data is stored in mongo is simpler now. Originally I thought we would be reusing plugin instances, however this turned out to be a stupid feature that adds complexity. Also, I didn't realize how easily you can make and store nested JSON in mongo - it rocks. So now we simply keep two objects on the course:
* The plugins which are the plugins that the course uses. Any properties on the plugin objects are Ansible variable overrides, for example mysql_root_password specifies the root password for any MySQL server that is provisioned with this course.
* The resources configuration. Each object on the ->resources property is one resource. We leave the option of having 2 of the same kind of resource open.