-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | dynamic plugin system for web applications -- -- This provides a simple framework for defining plugins for a web -- application. It is designed with the requirement that plugins can be -- loaded into a running web application with out having to know about -- the plugin in advance. @package web-plugins @version 0.4.1 -- | web-plugins 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 web-plugins-dynamic library will -- allow plugins and themes to be loaded and unloaded at runtime. -- -- A key aspect of web-plugins 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 web-plugins: -- --
    --
  1. initialize the plugins system
  2. --
  3. initialize the individual plugins
  4. --
  5. set the theme
  6. --
  7. route incoming requests to the correct plugin
  8. --
-- -- To use web-plugins, you first initialize a Plugins -- handle. -- -- The Plugins handle is heavily parameterized: -- --
--   newtype Plugins theme m hook config st = ...
--   
-- -- -- -- The plugin system is typically started by using withPlugins. -- Though, if needed, you can call initPlugins and -- destroyPlugins instead. -- -- The Plugin record is used to create a plugin: -- --
--   data Plugin url theme n hook config st = Plugin
--       { pluginName           :: PluginName
--       , pluginInit           :: Plugins theme n hook config st -> IO (Maybe Text)
--       , pluginDepends        :: [PluginName]   -- ^ plugins which much be initialized before this one can be
--       , pluginToPathSegments :: url -> Text
--       , pluginPostHook       :: hook
--       }
--   
-- -- You will note that it has the same type parameters as Plugins -- plus an additional url parameter. -- -- -- -- A Plugin is initialized using the initPlugin function -- (which calls the pluginInit field among other things). -- --
--   -- | initialize a plugin
--   initPlugin :: (Typeable url) =>
--                 Plugins theme n hook config st    -- ^ Plugins handle
--              -> Text                              -- ^ base URI to prepend to generated URLs
--              -> Plugin url theme n hook config st -- ^ Plugin to initialize
--              -> IO (Maybe Text)                   -- ^ possible error message
--   
-- -- A lot of the magic happens in the pluginInit function in the -- Plugin record. Let's look at a simple example. We will use the -- following type aliases to parameterize the Plugins and -- Plugin type: -- --
--   type ExamplePlugins    = Plugins    Theme (ServerPart Response) (IO ()) () ()
--   type ExamplePlugin url = Plugin url Theme (ServerPart Response) (IO ()) () ()
--   
-- -- Here is the initialization function for myPlugin: -- --
--   myInit :: ExamplePlugins -> IO (Maybe Text)
--   myInit plugins =
--       do (Just clckShowFn) <- getPluginRouteFn plugins (pluginName clckPlugin)
--          (Just myShowFn)   <- getPluginRouteFn plugins (pluginName myPlugin)
--          acid <- liftIO $ openLocalState MyState
--          addCleanup plugins OnNormal  (putStrLn "myPlugin: normal shutdown"  >> createCheckpointAndClose acid)
--          addCleanup plugins OnFailure (putStrLn "myPlugin: failure shutdown" >> closeAcidState acid)
--          addHandler plugins (pluginName myPlugin) (myPluginHandler acid clckShowFn myShowFn)
--          putStrLn "myInit completed."
--          return Nothing
--   
-- -- There are a few things to note here: -- -- getPluginRouteFn 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 clckPlugin. -- -- Next it opens up an AcidState. It then registers two -- different cleanup functions. The OnNormal cleanup will only be -- called if the system is shutdown normally. The OnFailure 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 Always condition instead. -- -- the addHandler then registers the function which route requests -- for this plugin: -- --
--   addHandler :: MonadIO m =>
--                 Plugins theme n hook config st
--               -> PluginName -- plugin name / prefix
--               -> (Plugins theme n hook config st -> [Text] -> n)
--               -> m ()
--   
-- -- Each plugin should be registered using a unique prefix. When the -- handler is called it will be passed the Plugins handle and a -- list of Text values. In practice, the list Text values -- is typically the unconsumed path segments from the URL. -- -- Setting the theme is done by calling the setTheme function: -- --
--   -- | set the current theme
--   setTheme :: (MonadIO m) =>
--               Plugins theme n hook config st
--            -> Maybe theme
--            -> m ()
--   
-- -- Setting the theme to Nothing will unload the theme but not load -- a new one. -- -- Incoming requests are routed to the various plugins via the -- serve function: -- --
--   -- | serve requests using the Plugins handle
--   serve :: Plugins theme n hook config st -- ^ Plugins handle
--         -> PluginName -- ^ name of the plugin to handle this request
--         -> [Text]     -- ^ unconsume path segments to pass to handler
--         -> IO (Either String n)
--   
-- -- The expected usage is that you are going to have request with a url -- such as: -- --
--   /my/extra/path/segments
--   
-- -- The code will treat the first path segment as the plugin to be called -- and pass in the remaining segments as the [Text] arguments: -- --
--   serve plugins "my" ["extra","path","segments"]
--   
-- -- the serve function itself knows nothing about the web -- it is -- framework agnostic. Here is a simple main function that shows -- how to tie everything together: -- --
--   main :: IO ()
--   main =
--     withPlugins () () $ \plugins ->
--       do initPlugin plugins "" clckPlugin
--          initPlugin plugins "" myPlugin
--          setTheme plugins (Just theme)
--          hooks <- getPostHooks plugins
--          sequence_ hooks
--          simpleHTTP nullConf $ path $ \p -> do
--            ps <- fmap rqPaths askRq
--            r <- liftIO $ serve plugins p (map Text.pack ps)
--            case r of
--              (Left e) -> internalServerError $ toResponse e
--              (Right sp) -> sp
--   
-- -- In this example, we do not use the config or st -- parameters so we just set them to (). -- -- Note that we are responsible for calling the hooks after we have -- initialized all the plugins. module Web.Plugins.Core -- | When indicates when a clean up action should be run data When -- | always run this action when destroyPlugins is called Always :: When -- | only run this action if destroyPlugins is called with a failure -- present OnFailure :: When -- | only run this action when destroyPlugins is called with a -- normal shutdown OnNormal :: When -- | A Cleanup is an IO action to run when the server shuts -- down. The server can either shutdown normally or due to a failure. The -- When parameter indicates when an action should run. data Cleanup Cleanup :: When -> IO () -> Cleanup -- | The PluginName should uniquely identify a plugin -- though we -- currently have no way to enforce that. type PluginName = Text -- | The PluginsState 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 (Text, Dynamic) -> Map PluginName (TVar Dynamic) -> Maybe theme -> [hook] -> config -> st -> Maybe (RewriteIncoming, RewriteOutgoing) -> 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] -- | baseURI, url -> [Text] [pluginsRouteFn] :: PluginsState theme n hook config st -> Map PluginName (Text, 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 -- | functions rewrite the incoming and outgoing URLs [pluginsRewrite] :: PluginsState theme n hook config st -> Maybe (RewriteIncoming, RewriteOutgoing) -- | The Plugins type is the handle to the plugins system. Generally -- you will have exactly one Plugins value in your app. -- -- see also withPlugins 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) -- | Rewrite or Redirect data Rewrite -- | rewrite the URL internally -- does not affect the URL displayed to the -- user Rewrite :: Rewrite -- | perform a 303 redirect to a different URL Redirect :: Maybe Text -> Rewrite -- | rewrite the URL from a Request before routing it type RewriteIncoming = IO ([Text] -> [(Text, Maybe Text)] -> Maybe (Rewrite, [Text], [(Text, Maybe Text)])) -- | rewrite a URL that is going to end up in a HTML document or other -- output type RewriteOutgoing = IO ([Text] -> [(Text, Maybe Text)] -> Maybe ([Text], [(Text, Maybe Text)])) -- | initialize the plugins system -- -- see also withPlugins initPlugins :: config -> st -> IO (Plugins theme n hook config st) -- | shutdown the plugins system -- -- see also withPlugins destroyPlugins :: When -> Plugins theme m hook config st -> IO () -- | a bracketed combination of initPlugins and -- destroyPlugins. Takes care of passing the correct termination -- condition. withPlugins :: config -> st -> (Plugins theme m hook config st -> IO a) -> IO a -- | get the current st value from Plugins getPluginsSt :: MonadIO m => Plugins theme n hook config st -> m st -- | put the current st value from Plugins 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 Nothing 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 plugin state -- -- If the plugin did not register any state, then this is a noop modifyPluginState' :: (MonadIO m, Typeable state) => Plugins theme n hook config st -> Text -> (state -> state) -> m () -- | modify the current st value from Plugins 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: getPluginRouteFn addPluginRouteFn :: (MonadIO m, Typeable url) => Plugins theme n hook config st -> PluginName -> Text -> (url -> [Text]) -> m () -- | get the plugin routing function for the named plugin -- -- see also: addPluginRouteFn getPluginRouteFn :: (MonadIO m, Typeable url) => Plugins theme n hook config st -> PluginName -> m (Maybe (url -> [(Text, Maybe Text)] -> Text)) getRewriteFn :: MonadIO m => Plugins theme n hook config st -> m (Maybe (RewriteIncoming, RewriteOutgoing)) setRewriteFn :: MonadIO m => Plugins theme n hook config st -> Maybe (RewriteIncoming, RewriteOutgoing) -> m () -- | set the current theme setTheme :: MonadIO m => Plugins theme n hook config st -> Maybe theme -> m () -- | get the current theme getTheme :: MonadIO m => Plugins theme n hook config st -> m (Maybe theme) -- | get the config value from the Plugins 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] -- | convert url to URL path segments [pluginToPathSegments] :: 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 Plugins handle -- -- NOTE: serve :: Plugins theme n hook config st -> PluginName -> [Text] -> IO (Either String n) instance GHC.Show.Show Web.Plugins.Core.When instance GHC.Classes.Ord Web.Plugins.Core.When instance GHC.Classes.Eq Web.Plugins.Core.When instance Data.Data.Data Web.Plugins.Core.Rewrite instance GHC.Show.Show Web.Plugins.Core.Rewrite instance GHC.Read.Read Web.Plugins.Core.Rewrite instance GHC.Classes.Ord Web.Plugins.Core.Rewrite instance GHC.Classes.Eq Web.Plugins.Core.Rewrite