aboutsummaryrefslogtreecommitdiffstats
path: root/docs/source/pluginwriter
diff options
context:
space:
mode:
Diffstat (limited to 'docs/source/pluginwriter')
-rw-r--r--docs/source/pluginwriter/api.rst197
-rw-r--r--docs/source/pluginwriter/database.rst9
-rw-r--r--docs/source/pluginwriter/media_type_hooks.rst38
-rw-r--r--docs/source/pluginwriter/quickstart.rst6
-rw-r--r--docs/source/pluginwriter/tests.rst64
5 files changed, 308 insertions, 6 deletions
diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst
index 5e0568fd..29adb691 100644
--- a/docs/source/pluginwriter/api.rst
+++ b/docs/source/pluginwriter/api.rst
@@ -11,6 +11,7 @@
Dedication along with this software. If not, see
<http://creativecommons.org/publicdomain/zero/1.0/>.
+.. _plugin-api-chapter:
==========
Plugin API
@@ -23,7 +24,27 @@ Authors are encouraged to develop plugins and work with the
MediaGoblin community to keep them up to date, but this API will be a
moving target for a few releases.
-Please check the release notes for updates!
+Please check the :ref:`release-notes` for updates!
+
+
+How are hooks added? Where do I find them?
+-------------------------------------------
+
+Much of this document talks about hooks, both as in terms of regular
+hooks and template hooks. But where do they come from, and how can
+you find a list of them?
+
+For the moment, the best way to find available hooks is to check the
+source code itself. (Yes, we should start a more official hook
+listing with descriptions soon.) But many hooks you may need do not
+exist yet: what to do then?
+
+The plan at present is that we are adding hooks as people need them,
+with community discussion. If you find that you need a hook and
+MediaGoblin at present doesn't provide it at present, please
+`http://mediagoblin.org/pages/join.html <talk to us>`_! We'll
+evaluate what to do from there.
+
:mod:`pluginapi` Module
-----------------------
@@ -48,6 +69,32 @@ example might look like::
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
-------------
@@ -125,3 +172,151 @@ context... it is added to the actual context of each individual
template render right before it is run! Because of this you also can
do some powerful and crazy things, such as checking the request object
or other parts of the context before passing them on.
+
+
+Adding static resources
+-----------------------
+
+It's possible to add static resources for your plugin. Say your
+plugin needs some special javascript and images... how to provide
+them? Then how to access them? MediaGoblin has a way!
+
+
+Attaching to the hook
++++++++++++++++++++++
+
+First, you need to register your plugin's resources with the hook.
+This is pretty easy actually: you just need to provide a function that
+passes back a PluginStatic object.
+
+.. autoclass:: mediagoblin.tools.staticdirect.PluginStatic
+
+
+Running plugin assetlink
+++++++++++++++++++++++++
+
+In order for your plugin assets to be properly served by MediaGoblin,
+your plugin's asset directory needs to be symlinked into the directory
+that plugin assets are served from. To set this up, run::
+
+ ./bin/gmg assetlink
+
+
+Using staticdirect
+++++++++++++++++++
+
+Once you have this, you will want to be able to of course link to your
+assets! MediaGoblin has a "staticdirect" tool; you want to use this
+like so in your templates::
+
+ staticdirect("css/monkeys.css", "mystaticname")
+
+Replace "mystaticname" with the name you passed to PluginStatic. The
+staticdirect method is, for convenience, attached to the request
+object, so you can access this in your templates like:
+
+.. code-block:: html
+
+ <img alt="A funny bunny"
+ src="{{ request.staticdirect('images/funnybunny.png', 'mystaticname') }}" />
+
+
+Additional hook tips
+--------------------
+
+This section aims to explain some tips in regards to adding hooks to
+the MediaGoblin repository.
+
+WTForms hooks
++++++++++++++
+
+We haven't totally settled on a way to tranform wtforms form objects,
+but here's one way. In your view::
+
+ from mediagoblin.foo.forms import SomeForm
+
+ def some_view(request)
+ form_class = hook_transform('some_form_transform', SomeForm)
+ form = form_class(request.form)
+
+Then to hook into this form, do something in your plugin like::
+
+ import wtforms
+
+ class SomeFormAdditions(wtforms.Form):
+ new_datefield = wtforms.DateField()
+
+ def transform_some_form(orig_form):
+ class ModifiedForm(orig_form, SomeFormAdditions)
+ return ModifiedForm
+
+ 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}
diff --git a/docs/source/pluginwriter/database.rst b/docs/source/pluginwriter/database.rst
index 58edf3a0..603a19eb 100644
--- a/docs/source/pluginwriter/database.rst
+++ b/docs/source/pluginwriter/database.rst
@@ -12,9 +12,12 @@
<http://creativecommons.org/publicdomain/zero/1.0/>.
-========
-Database
-========
+.. _plugin-database-chapter:
+
+
+===========================
+Database models for plugins
+===========================
Accessing Existing Data
diff --git a/docs/source/pluginwriter/media_type_hooks.rst b/docs/source/pluginwriter/media_type_hooks.rst
new file mode 100644
index 00000000..498b0b54
--- /dev/null
+++ b/docs/source/pluginwriter/media_type_hooks.rst
@@ -0,0 +1,38 @@
+==================
+ Media Type hooks
+==================
+
+This documents the hooks that are currently available for ``media_type`` plugins.
+
+What hooks are available?
+=========================
+
+'sniff_handler'
+---------------
+
+This hook is used by ``sniff_media`` in ``mediagoblin.media_types.__init__``.
+Your media type should return its ``sniff_media`` method when this hook is
+called.
+
+.. Note::
+ Your ``sniff_media`` method should return either the ``media_type`` or
+ ``None``.
+
+'get_media_type_and_manager'
+----------------------------
+
+This hook is used by ``get_media_type_and_manager`` in
+``mediagoblin.media_types.__init__``. When this hook is called, your media type
+plugin should check if it can handle the given extension. If so, your media
+type plugin should return the media type and media manager.
+
+('media_manager', MEDIA_TYPE)
+-----------------------------
+
+If you already know the string representing the media type of a type
+of media, you can pull down the manager specifically. Note that this
+hook is not a string but a tuple of two strings, the latter being the
+name of the media type.
+
+This is used by media entries to pull down their media managers, and
+so on.
diff --git a/docs/source/pluginwriter/quickstart.rst b/docs/source/pluginwriter/quickstart.rst
index b5a63f79..6d45ea36 100644
--- a/docs/source/pluginwriter/quickstart.rst
+++ b/docs/source/pluginwriter/quickstart.rst
@@ -178,8 +178,10 @@ That's it for the quick start!
Where to go from here
=====================
-See the documentation on the plugin API for code samples and other
-things you can use when building your plugin.
+See the documentation on the :ref:`plugin-api-chapter` for code
+samples and other things you can use when building your plugin. If
+your plugin needs its own database models, see
+:ref:`plugin-database-chapter`.
See `Hitchhiker's Guide to Packaging
<http://guide.python-distribute.org/>`_ for more information on
diff --git a/docs/source/pluginwriter/tests.rst b/docs/source/pluginwriter/tests.rst
new file mode 100644
index 00000000..fe99688f
--- /dev/null
+++ b/docs/source/pluginwriter/tests.rst
@@ -0,0 +1,64 @@
+.. MediaGoblin Documentation
+
+ Written in 2013 by MediaGoblin contributors
+
+ To the extent possible under law, the author(s) have dedicated all
+ copyright and related and neighboring rights to this software to
+ the public domain worldwide. This software is distributed without
+ any warranty.
+
+ You should have received a copy of the CC0 Public Domain
+ Dedication along with this software. If not, see
+ <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+==============================
+Writing unit tests for plugins
+==============================
+
+Here's a brief guide to writing unit tests for plugins. However, it
+isn't really ideal. It also hasn't been well tested... yes, there's
+some irony there :)
+
+Some notes: we're using py.test and webtest for unit testing stuff.
+Keep that in mind.
+
+My suggestion is to mime the behavior of `mediagoblin/tests/` and put
+that in your own plugin, like `myplugin/tests/`. Copy over
+`conftest.py` and `pytest.ini` to your tests directory, but possibly
+change the `test_app` fixture to match your own tests' config needs.
+For example::
+
+ import pkg_resources
+ # [...]
+
+ @pytest.fixture()
+ def test_app(request):
+ return get_app(
+ request,
+ mgoblin_config=pkg_resources.resource_filename(
+ 'myplugin.tests', 'myplugin_mediagoblin.ini'))
+
+In any test module in your tests directory you can then do::
+
+ def test_somethingorother(test_app):
+ # real code goes here
+ pass
+
+And you'll get a mediagoblin application wrapped in webtest passed in
+to your environment.
+
+If your plugin needs to define multiple configuration setups, you can
+actually set up multiple fixtures very easily for this. You can just
+set up multiple fixtures with different names that point to different
+configs and pass them in as that named argument.
+
+To run the tests, from mediagoblin's directory (make sure that your
+plugin has been added to your mediagoblin checkout's virtualenv!) do::
+
+ ./runtests.sh /path/to/myplugin/tests/
+
+replacing `/path/to/myplugin/` with the actual path to your plugin.
+
+NOTE: again, the above is untested, but it should probably work. If
+you run into trouble, `contact us
+<http://mediagoblin.org/pages/join.html>`_, preferably on IRC!