This means that when people enable your plugin in their config you'll
be able to provide defaults as well as type validation.
+You can access this via the app_config variables in mg_globals, or you
+can use a shortcut to get your plugin's config section::
+
+ >>> from mediagoblin.tools import pluginapi
+ # Replace with the path to your plugin.
+ # (If an external package, it won't be part of mediagoblin.plugins)
+ >>> floobie_config = pluginapi.get_config('mediagoblin.plugins.floobifier')
+ >>> floobie_dir = floobie_config['floobie_dir']
+ # This is the same as the above
+ >>> from mediagoblin import mg_globals
+ >>> config = mg_globals.global_config['plugins']['mediagoblin.plugins.floobifier']
+ >>> floobie_dir = floobie_config['floobie_dir']
+
+A tip: you have access to the `%(here)s` variable in your config,
+which is the directory that the user's mediagoblin config is running
+out of. So for example, your plugin may need a "floobie" directory to
+store floobs in. You could give them a reasonable default that makes
+use of the default `user_dev` location, but allow users to override
+it, like so::
+
+ [plugin_spec]
+ floobie_dir = string(default="%(here)s/user_dev/floobs/")
+
+Note, this is relative to the user's mediagoblin config directory,
+*not* your plugin directory!
+
Context Hooks
-------------
hooks = {
'some_form_transform': transform_some_form}
+
+
+Interfaces
+++++++++++
+
+If you want to add a pseudo-interface, it's not difficult to do so.
+Just write the interface like so::
+
+ class FrobInterface(object):
+ """
+ Interface for Frobbing.
+
+ Classes implementing this interface should provide defrob and frob.
+ They may also implement double_frob, but it is not required; if
+ not provided, we will use a general technique.
+ """
+
+ def defrob(self, frobbed_obj):
+ """
+ Take a frobbed_obj and defrob it. Returns the defrobbed object.
+ """
+ raise NotImplementedError()
+
+ def frob(self, normal_obj):
+ """
+ Take a normal object and frob it. Returns the frobbed object.
+ """
+ raise NotImplementedError()
+
+ def double_frob(self, normal_obj):
+ """
+ Frob this object and return it multiplied by two.
+ """
+ return self.frob(normal_obj) * 2
+
+
+ def some_frob_using_method():
+ # something something something
+ frobber = hook_handle(FrobInterface)
+ frobber.frob(blah)
+
+ # alternately you could have a default
+ frobber = hook_handle(FrobInterface) or DefaultFrobber
+ frobber.defrob(foo)
+
+
+It's fine to use your interface as the key instead of a string if you
+like. (Usually this is messy, but since interfaces are public and
+since you need to import them into your plugin anyway, interfaces
+might as well be keys.)
+
+Then a plugin providing your interface can be like::
+
+ from mediagoblin.foo.frobfrogs import FrobInterface
+ from frogfrobber import utils
+
+ class FrogFrobber(FrobInterface):
+ """
+ Takes a frogputer science approach to frobbing.
+ """
+ def defrob(self, frobbed_obj):
+ return utils.frog_defrob(frobbed_obj)
+
+ def frob(self, normal_obj):
+ return utils.frog_frob(normal_obj)
+
+ hooks = {
+ FrobInterface: lambda: return FrogFrobber}