如何制作nicEditor snaplet? (几个问题)

时间:2013-03-17 16:06:02

标签: haskell haskell-snap-framework

下面的示例定义了一个snaplet,用于将nicEditor绑定到textarea。以下问题不仅与下面的例子有关,而且可能与其他类似案例有关。

  1. 新手可以按照以下说明(如何澄清)吗?
  2. 如何使示例使用更少的步骤或更简单? (可能与下面的内容大致相同吗?)
  3. 这使用了解释的拼接。如果可能的话,snaplet是否应该提供编译的拼接?
  4. snaplet可能会为典型情况提供默认处理程序或几个处理程序。处理程序可以在下面的“SnapNic.hs”中定义。对用户有一些回调机制呢?
  5. -

    {-# LANGUAGE TemplateHaskell   #-}
    {-# LANGUAGE OverloadedStrings #-}
    
     ------------------------------------------------------------------------------
    -- | This module defines nicEditor snaplet, just a short example to show,
    -- how snaplets can be defined together with splices.
    -- License: BSD3. 
    -- Here are hopefully easy instructions, how to use or try:
    -- 
    -- 1. Make a directory, we'll use "netry" below, go there.
    --  Initialize a project, e.g. "snap init default".
    -- 2. Copy this file to netry/src-directory as SnapNic.hs.
    -- 3. Add "import SnapNic" to Site.hs and to Application.hs
    -- 4. Add ", _niced :: Snaplet Nicsnap" to data App in Application.hs 
    -- 
    -- 5. Add "n <- nestSnaplet "niced" niced nicsnapInit" to 
    --    app :: SnapletInit App App in Site.hs. 
    -- 6. Add "addNicEditSplices n" to the same function as in step 5.
    -- 7. Change the return-line of the same function as in step 5: 
    --      "return $ App h s a n" 
    --    that is, add "n" into the end. We need this because of step 4.
    -- 
    -- 8. Make route, e.g. ", ("/netext",   with auth handleNEtext)" to
    --    routes-function in Site.hs
    -- 
    -- 9. And then add handler into Site.hs:
    --    handleNEtext :: Handler App v ()
    --    handleNEtext = method GET handleForm <|> method POST handleFormSubmit
    --      where
    --       handleForm = render "textedit"
    --       handleFormSubmit = do 
    --        p <- getParam "ots"
    --        writeText "Submitting text from textarea...\n"
    --        writeText (T.pack (show p))
    -- 
    -- 10. Last, add the following 2 templates to "netry/snaplets/heist/templates".
    --    (This could be made simpler, but this works as an example of apply-tag.)
    --    textedit.tpl:
    --      <apply template="base">
    --         <apply template="_textedit" />
    --      </apply>
    --    _textedit.tpl:
    --       <h2>Your nic editor</h2>
    --         <form method="post" action="netext">
    --           <neTA/>
    --           <button name="ne" value="ne" type="Submit">Send text</button>
    --         </form>
    --         <neScript/>
    -- 
    -- 11. Compile everything "cabal install -fdevelopment". After that, 
    --     if everything compiled, "netry -p 8000", start your browser and go
    --     to "localhost:8000/netext".
    -- 
    -- TODO! This could use the config-files at least for some parameters, and more
    -- tags,please. Tags could use some attributes (for example, size parameters
    -- could be given as attributes of tags)...
    -- 
    module SnapNic 
      ( Nicsnap (..)
      , nicsnapInit
      , addNicEditSplices
      ) where
    
    ------------------------------------------------------------------------------
    import           Control.Lens   (makeLenses, view, (^.))
    import qualified Data.Text as T (Text, append, pack)
    import           Data.Maybe     (fromJust, fromMaybe)
    import           Snap.Core      (MonadSnap)
    import           Snap.Snaplet   (Snaplet
                                    , makeSnaplet
                                    , snapletValue
                                    , SnapletInit
                                    , Initializer
                                    )
    import           Snap.Snaplet.Heist     (HasHeist, addSplices)
    import qualified Text.XmlHtml as X      (Node (Element, TextNode))
    import qualified Heist.Interpreted as I (Splice)
    
    ------------------------------------------------------------------------------
    -- | Nicsnap has fields that can be used to set some basic properties.
    -- The editor can have a title and its size can be set. Javascript can be
    -- local or remote.
    data Nicsnap = Nicsnap
      { _nicsnap  :: T.Text       -- title
      , _areaSize :: (Int,Int)    -- rows, cols
      , _areaRef  :: T.Text       -- how to apply nicEditors? 
      -- (This may not be sufficient in order to refer in some other way, TODO!)
      , _localR   :: Maybe T.Text -- local route to nicEdit.js
      , _webR     :: T.Text       -- route to nicEdit's javascript source. 
      }
    
    makeLenses ''Nicsnap          -- makes webR and other lenses
    
    
    ------------------------------------------------------------------------------
    -- | Configurations are given here. This could use config-files...
    -- What other things to configure?
    -- If you want to make a local copy of the nicEdit, then add a static route
    -- to the "routes"-function. 
    nicsnapInit :: SnapletInit b Nicsnap
    nicsnapInit = makeSnaplet "nicsnap" "NicEditor snaplet " Nothing $ do
       let m  = "Nic editor title"
           aS = (20,80)::(Int,Int) -- rows, cols
           aR = "nicEditors.allTextAreas" -- TODO! We need to be able to tell,
           -- which textareas have editors in a page.
           lR = Nothing 
           -- lR = Just "/nicEdit.js" 
           -- If localR is nothing, then webR is used with the following addr.
           wR = "http://js.nicedit.com/nicEdit-latest.js"
       return $ Nicsnap m aS aR lR wR
    
    ------------------------------------------------------------------------------
    
    -- | Internal, this makes the script-tag.
    -- Input could be e.g. txt = "/nicEdit.js"
    srcElem :: T.Text -> X.Node
    srcElem txt = X.Element "script" 
       [("src",txt),("type","text/javascript")] []
    
    -- | Internal, this makes the script-tag. At the moment this changes all
    -- textareas to niceditors, if the example input below is used. TODO!...
    -- Input could be e.g.  txt = "nicEditors.allTextAreas"
    srcOnLoad :: T.Text -> X.Node
    srcOnLoad txt = X.Element "script" [("type","text/javascript")] 
       [X.TextNode (T.append (T.append "bkLib.onDomLoaded(" txt) ");")] 
    
    
    -- | Internal, used to define "divs", where we give a label and size to 
    -- textarea. Also ids and names.
    -- TODO! ids and names could be parameters.
    divLabelTX :: T.Text -> T.Text -> T.Text -> X.Node
    divLabelTX title r c =  X.Element "div" [("class", "required")]
       [ X.Element "label" [("for","ots")] 
            [X.TextNode title]
       , X.Element "textarea" 
            [("id","ots"), ("name","ots"), ("cols",c), ("rows",r)] 
            [X.TextNode " "]
       ]
    
    -- | Internal, this can be used in splice-definition.
    -- TODO! ids and names could be parameters, too.
    nicTextAreaAdd :: MonadSnap m => T.Text -> (Int,Int) -> I.Splice m
    nicTextAreaAdd title (r,c) = return [divLabelTX 
      title
      (T.pack . show $ r) 
      (T.pack . show $ c)]
    
    -- | Add script-tags to web page with splice that tell, what javascript
    -- library to use... 
    nicEditAdd :: MonadSnap m => T.Text -> T.Text -> I.Splice m
    nicEditAdd src edElems = return (srcElem src : [srcOnLoad edElems])
    
    ------------------------------------------------------------------------------
    
    -- | Get the route to the javascript library that is applied (either local
    -- library or construct a link to a web address).
    nicRoute :: Nicsnap -> T.Text
    nicRoute ns = let mlR = ns ^. localR in fromMaybe (ns ^. webR) mlR
    
    ------------------------------------------------------------------------------
    
    -- | neTextAreaTag and neScripTag are used in addSplices to define the tags
    -- to be used in templates.
    -- What other tags could be useful? Maybe a way to add a nicEditor directly
    -- with one or more button bind to it ("send", "clear", etc). TODO!
    neTextAreaTag = "neTA"     :: T.Text
    neScriptTag   = "neScript" :: T.Text
    
    -- | Make the tags to be used in templates. At the moment, only the above
    -- tags are defined. 
    addNicEditSplices :: HasHeist b => Snaplet Nicsnap -> Initializer b v ()
    addNicEditSplices n = let m = view snapletValue n in addSplices
      [(neTextAreaTag, nicTextAreaAdd (m ^. nicsnap) (m ^. areaSize))
      ,(neScriptTag,   nicEditAdd (nicRoute m) (m ^. areaRef) )
      ]
    ------------------------------------------------------------------------------
    

1 个答案:

答案 0 :(得分:1)

我不是新手,所以我无法回答你的第一个问题,但我对其他一些问题有一些想法和答案。首先,如果你真的希望这是一个严肃的snaplet(无论是出于教学目的还是实际用途),你应该把它变成一个cabal项目。这将消除您的指示中的第2步。接下来,snaplet可以定义自己的路由。您可以通过调用addRoutes函数在初始值设定项中执行此操作。这将消除步骤8. Snaplet还可以提供自己的文件系统资源,这些资源将被复制到使用它的任何项目中。您可以使用此功能来消除步骤10,以及提供默认配置文件。有关如何执行此操作的详细信息,请查看snaplet教程末尾的filesystem data and automatic installation部分。

目前,snaplet-postgresql-simple可能是关于如何使用snaplet必须提供的大多数功能的最佳示例。如果你想让它成为一个非常强大的通用snaplet供其他人使用,那么你绝对应该包括解释和编译的拼接。我最近在snap包中添加了一些new functions,这使得编写通用snaplet更容易,这些snaplet可以在编译或解释的拼接模式下自动工作。那段代码还没有用,但我很快就会发布它。

我一直致力于another snaplet,它可以更全面地使用snaplet API的大部分功能。与snaplet-postgresql-simple不同,此snaplet定义了模板和拼接。它仍处于开发阶段,但已经展示了大部分功能。剩下的工作大部分只是润色和坚固。