-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | dynamic plugin system for web applications
--   
--   dynamic plugin system for web applications
@package web-plugins
@version 0.2.3


-- | <tt>web-plugins</tt> is a very general purpose plugin system for web
--   applications.
--   
--   It provides facilities for loading multiple plugins and a single
--   theme. In the future, the <tt>web-plugins-dynamic</tt> library will
--   allow plugins and themes to be loaded and unloaded at runtime.
--   
--   A key aspect of <tt>web-plugins</tt> is that all plugins for a
--   particular system have the same type signature. This is what makes it
--   possible to load new plugins at runtime.
--   
--   This plugin system is not tied to any particular web server framework
--   or template engine.
--   
--   There are four steps to using <tt>web-plugins</tt>:
--   
--   <ol>
--   <li>initialize the plugins system</li>
--   <li>initialize the individual plugins</li>
--   <li>set the theme</li>
--   <li>route incoming requests to the correct plugin</li>
--   </ol>
--   
--   To use <tt>web-plugins</tt>, you first initialize a <a>Plugins</a>
--   handle.
--   
--   The <a>Plugins</a> handle is heavily parameterized:
--   
--   <pre>
--   newtype Plugins theme m hook config st = ...
--   </pre>
--   
--   <ul>
--   <li><i><tt>theme</tt></i> is (not suprisingly) the type for you
--   theme.</li>
--   <li><i><tt>m</tt></i> is the monad that your plugin handlers will run
--   in. (e.g., <tt>ServerPart</tt>)</li>
--   <li><i><tt>hook</tt></i> is additional actions that should be called
--   after the plugins have been initialized</li>
--   <li><i><tt>config</tt></i> provides read-only configuration
--   information</li>
--   <li><i><tt>st</tt></i> provides mutable state that is shared between
--   all plugins. (There is a separate mechanism for plugin-local
--   state.)</li>
--   </ul>
--   
--   The plugin system is typically started by using <a>withPlugins</a>.
--   Though, if needed, you can call <a>initPlugins</a> and
--   <a>destroyPlugins</a> instead.
--   
--   The <a>Plugin</a> record is used to create a plugin:
--   
--   <pre>
--   data Plugin url theme n hook config st = Plugin
--       { pluginName         :: PluginName
--       , pluginInit         :: Plugins theme n hook config st -&gt; IO (Maybe Text)
--       , pluginDepends      :: [PluginName]   -- ^ plugins which much be initialized before this one can be
--       , pluginToPathInfo   :: url -&gt; Text
--       , pluginPostHook     :: hook
--       }
--   </pre>
--   
--   You will note that it has the same type parameters as <a>Plugins</a>
--   plus an additional <tt>url</tt> parameter.
--   
--   <ul>
--   <li><i><tt>pluginName</tt></i> is a simple <a>Text</a> value which
--   should uniquely identify the plugin.</li>
--   <li><i><tt>pluginInit</tt></i> will be called automatically when the
--   plugin is loaded.</li>
--   <li><i><tt>pluginDepends</tt></i> is a list of plugins which must be
--   loaded before this plugin can be initialized.</li>
--   <li><i><tt>pluginToPathInfo</tt></i> is the function that is used to
--   convert the <tt>url</tt> type to an actual URL.</li>
--   <li><i><tt>pluginPostHook</tt></i> is the hook that you want called
--   after the system has been initialized.</li>
--   </ul>
--   
--   A <a>Plugin</a> is initialized using the <a>initPlugin</a> function
--   (which calls the <a>pluginInit</a> field among other things).
--   
--   <pre>
--   -- | initialize a plugin
--   initPlugin :: (Typeable url) =&gt;
--                 Plugins theme n hook config st    -- ^ <a>Plugins</a> handle
--              -&gt; Text                              -- ^ base URI to prepend to generated URLs
--              -&gt; Plugin url theme n hook config st -- ^ <a>Plugin</a> to initialize
--              -&gt; IO (Maybe Text)                   -- ^ possible error message
--   </pre>
--   
--   A lot of the magic happens in the <a>pluginInit</a> function in the
--   <a>Plugin</a> record. Let's look at a simple example. We will use the
--   following type aliases to parameterize the <a>Plugins</a> and
--   <a>Plugin</a> type:
--   
--   <pre>
--   type ExamplePlugins    = Plugins    Theme (ServerPart Response) (IO ()) () ()
--   type ExamplePlugin url = Plugin url Theme (ServerPart Response) (IO ()) () ()
--   </pre>
--   
--   Here is the initialization function for <tt>myPlugin</tt>:
--   
--   <pre>
--   myInit :: ExamplePlugins -&gt; IO (Maybe Text)
--   myInit plugins =
--       do (Just clckShowFn) &lt;- getPluginRouteFn plugins (pluginName clckPlugin)
--          (Just myShowFn)   &lt;- getPluginRouteFn plugins (pluginName myPlugin)
--          acid &lt;- liftIO $ openLocalState MyState
--          addCleanup plugins OnNormal  (putStrLn <a>myPlugin: normal shutdown</a>  &gt;&gt; createCheckpointAndClose acid)
--          addCleanup plugins OnFailure (putStrLn <a>myPlugin: failure shutdown</a> &gt;&gt; closeAcidState acid)
--          addHandler plugins (pluginName myPlugin) (myPluginHandler acid clckShowFn myShowFn)
--          putStrLn <a>myInit completed.</a>
--          return Nothing
--   </pre>
--   
--   There are a few things to note here:
--   
--   <a>getPluginRouteFn</a> is used to retrieve the the URL route showing
--   function for various plugins. In this case, the plugin needs to
--   generate routes for itself and also routes in the <tt>clckPlugin</tt>.
--   
--   Next it opens up an <tt>AcidState</tt>. It then registers two
--   different cleanup functions. The <a>OnNormal</a> cleanup will only be
--   called if the system is shutdown normally. The <a>OnFailure</a> will
--   be called if the system is shutdown due to some error condition. If we
--   wanted to perform the same shutdown procedure regardless of
--   termination cause, we could use the <a>Always</a> condition instead.
--   
--   the <a>addHandler</a> then registers the function which route requests
--   for this plugin:
--   
--   <pre>
--   addHandler :: MonadIO m =&gt;
--                 Plugins theme n hook config st
--               -&gt; PluginName -- plugin name / prefix
--               -&gt; (Plugins theme n hook config st -&gt; [Text] -&gt; n)
--               -&gt; m ()
--   </pre>
--   
--   Each plugin should be registered using a unique prefix. When the
--   handler is called it will be passed the <a>Plugins</a> handle and a
--   list of <a>Text</a> values. In practice, the list <a>Text</a> values
--   is typically the unconsumed path segments from the URL.
--   
--   Setting the theme is done by calling the <a>setTheme</a> function:
--   
--   <pre>
--   -- | set the current <tt>theme</tt>
--   setTheme :: (MonadIO m) =&gt;
--               Plugins theme n hook config st
--            -&gt; Maybe theme
--            -&gt; m ()
--   </pre>
--   
--   Setting the theme to <a>Nothing</a> will unload the theme but not load
--   a new one.
--   
--   Incoming requests are routed to the various plugins via the
--   <a>serve</a> function:
--   
--   <pre>
--   -- | serve requests using the <a>Plugins</a> handle
--   serve :: Plugins theme n hook config st -- ^ <a>Plugins</a> handle
--         -&gt; PluginName -- ^ name of the plugin to handle this request
--         -&gt; [Text]     -- ^ unconsume path segments to pass to handler
--         -&gt; IO (Either String n)
--   </pre>
--   
--   The expected usage is that you are going to have request with a url
--   such as:
--   
--   <pre>
--   /my/extra/path/segments
--   </pre>
--   
--   The code will treat the first path segment as the plugin to be called
--   and pass in the remaining segments as the <tt>[Text]</tt> arguments:
--   
--   <pre>
--   serve plugins "my" ["extra","path","segments"]
--   </pre>
--   
--   the <a>serve</a> function itself knows nothing about the web -- it is
--   framework agnostic. Here is a simple <tt>main</tt> function that shows
--   how to tie everything together:
--   
--   <pre>
--   main :: IO ()
--   main =
--     withPlugins () () $ \plugins -&gt;
--       do initPlugin plugins "" clckPlugin
--          initPlugin plugins "" myPlugin
--          setTheme plugins (Just theme)
--          hooks &lt;- getPostHooks plugins
--          sequence_ hooks
--          simpleHTTP nullConf $ path $ \p -&gt; do
--            ps &lt;- fmap rqPaths askRq
--            r &lt;- liftIO $ serve plugins p (map Text.pack ps)
--            case r of
--              (Left e) -&gt; internalServerError $ toResponse e
--              (Right sp) -&gt; sp
--   </pre>
--   
--   In this example, we do not use the <tt>config</tt> or <tt>st</tt>
--   parameters so we just set them to <tt>()</tt>.
--   
--   Note that we are responsible for calling the hooks after we have
--   initialized all the plugins.
module Web.Plugins.Core

-- | <a>When</a> indicates when a clean up action should be run
data When

-- | always run this action when <a>destroyPlugins</a> is called
Always :: When

-- | only run this action if <a>destroyPlugins</a> is called with a failure
--   present
OnFailure :: When

-- | only run this action when <a>destroyPlugins</a> is called with a
--   normal shutdown
OnNormal :: When

-- | A <a>Cleanup</a> is an <a>IO</a> action to run when the server shuts
--   down. The server can either shutdown normally or due to a failure. The
--   <a>When</a> parameter indicates when an action should run.
data Cleanup
Cleanup :: When -> (IO ()) -> Cleanup

-- | The <a>PluginName</a> should uniquely identify a plugin -- though we
--   currently have no way to enforce that.
type PluginName = Text

-- | The <a>PluginsState</a> record holds all the record keeping
--   information needed for loading, unloading, and invoking plugins. In
--   theory you should not be modifying or inspecting this structure
--   directly -- only calling the helper functions that modify or read it.
data PluginsState theme n hook config st
PluginsState :: Map PluginName (Plugins theme n hook config st -> [Text] -> n) -> [Cleanup] -> Map PluginName Dynamic -> Map PluginName (TVar Dynamic) -> Maybe theme -> [hook] -> config -> st -> PluginsState theme n hook config st
pluginsHandler :: PluginsState theme n hook config st -> Map PluginName (Plugins theme n hook config st -> [Text] -> n)
pluginsOnShutdown :: PluginsState theme n hook config st -> [Cleanup]
pluginsRouteFn :: PluginsState theme n hook config st -> Map PluginName Dynamic

-- | per-plugin state
pluginsPluginState :: PluginsState theme n hook config st -> Map PluginName (TVar Dynamic)
pluginsTheme :: PluginsState theme n hook config st -> Maybe theme
pluginsPostHooks :: PluginsState theme n hook config st -> [hook]
pluginsConfig :: PluginsState theme n hook config st -> config
pluginsState :: PluginsState theme n hook config st -> st

-- | The <a>Plugins</a> type is the handle to the plugins system. Generally
--   you will have exactly one <a>Plugins</a> value in your app.
--   
--   see also <a>withPlugins</a>
newtype Plugins theme m hook config st
Plugins :: TVar (PluginsState theme m hook config st) -> Plugins theme m hook config st
ptv :: Plugins theme m hook config st -> TVar (PluginsState theme m hook config st)

-- | initialize the plugins system
--   
--   see also <a>withPlugins</a>
initPlugins :: config -> st -> IO (Plugins theme n hook config st)

-- | shutdown the plugins system
--   
--   see also <a>withPlugins</a>
destroyPlugins :: When -> Plugins theme m hook config st -> IO ()

-- | a bracketed combination of <a>initPlugins</a> and
--   <a>destroyPlugins</a>. Takes care of passing the correct termination
--   condition.
withPlugins :: config -> st -> (Plugins theme m hook config st -> IO a) -> IO a

-- | get the current <tt>st</tt> value from <a>Plugins</a>
getPluginsSt :: MonadIO m => Plugins theme n hook config st -> m st

-- | put the current st value from <a>Plugins</a>
putPluginsSt :: MonadIO m => Plugins theme n hook config st -> st -> m ()

-- | add a new plugin-local state
addPluginState :: (MonadIO m, Typeable state) => Plugins theme n hook config st -> PluginName -> state -> m ()

-- | Get the state for a particular plugin
--   
--   per-plugin state is optional. This will return <a>Nothing</a> if the
--   plugin did not register any local state.
getPluginState :: (MonadIO m, Typeable state) => Plugins theme n hook config st -> Text -> m (Maybe state)

-- | modify the current st value from <a>Plugins</a>
modifyPluginsSt :: MonadIO m => Plugins theme n hook config st -> (st -> st) -> m ()

-- | add a new route handler
addHandler :: MonadIO m => Plugins theme n hook config st -> PluginName -> (Plugins theme n hook config st -> [Text] -> n) -> m ()

-- | add a new cleanup action to the top of the stack
addCleanup :: MonadIO m => Plugins theme n hook config st -> When -> IO () -> m ()

-- | add a new post initialization hook
addPostHook :: MonadIO m => Plugins theme n hook config st -> hook -> m ()

-- | get all the post initialization hooks
getPostHooks :: MonadIO m => Plugins theme n hook config st -> m [hook]

-- | add the routing function for a plugin
--   
--   see also: <a>getPluginRouteFn</a>
addPluginRouteFn :: (MonadIO m, Typeable url) => Plugins theme n hook config st -> PluginName -> (url -> [(Text, Maybe Text)] -> Text) -> m ()

-- | get the plugin routing function for the named plugin
--   
--   see also: <a>addPluginRouteFn</a>
getPluginRouteFn :: (MonadIO m, Typeable url) => Plugins theme n hook config st -> PluginName -> m (Maybe (url -> [(Text, Maybe Text)] -> Text))

-- | set the current <tt>theme</tt>
setTheme :: MonadIO m => Plugins theme n hook config st -> Maybe theme -> m ()

-- | get the current <tt>theme</tt>
getTheme :: MonadIO m => Plugins theme n hook config st -> m (Maybe theme)

-- | get the <tt>config</tt> value from the <a>Plugins</a> type
getConfig :: MonadIO m => Plugins theme n hook config st -> m config

-- | NOTE: it is possible to set the URL type incorrectly here and not get
--   a type error. How can we fix that ?
data Plugin url theme n hook config st
Plugin :: PluginName -> (Plugins theme n hook config st -> IO (Maybe Text)) -> [PluginName] -> (url -> Text) -> hook -> Plugin url theme n hook config st
pluginName :: Plugin url theme n hook config st -> PluginName
pluginInit :: Plugin url theme n hook config st -> Plugins theme n hook config st -> IO (Maybe Text)

-- | plugins which much be initialized before this one can be
pluginDepends :: Plugin url theme n hook config st -> [PluginName]
pluginToPathInfo :: Plugin url theme n hook config st -> url -> Text
pluginPostHook :: Plugin url theme n hook config st -> hook

-- | initialize a plugin
initPlugin :: Typeable url => Plugins theme n hook config st -> Text -> Plugin url theme n hook config st -> IO (Maybe Text)

-- | serve requests using the <a>Plugins</a> handle
serve :: Plugins theme n hook config st -> PluginName -> [Text] -> IO (Either String n)
instance Eq When
instance Ord When
instance Show When
