diff --git a/CLI/skel/App.hs b/CLI/skel/App.hs new file mode 100644 index 00000000..9b1a37d9 --- /dev/null +++ b/CLI/skel/App.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE QuasiQuotes #-} +import Yesod +import Yesod.Helpers.Static +import qualified Data.Object.Yaml +import qualified Safe.Failure + +data $Datatype$ = $Datatype$ + { settings :: Settings + , templateGroup :: TemplateGroup + } + +data Settings = Settings + { sApproot :: String + , staticRoot :: String + , staticDir :: String + , templateDir :: String + , portNumber :: Int + } + +settingsFile :: FilePath +settingsFile = "settings.yaml" + +loadSettings :: IO Settings +loadSettings = do + m <- Data.Object.Yaml.decodeFile settingsFile >>= fromMapping + ar <- lookupScalar "approot" m + sr <- lookupScalar "static-root" m + sd <- lookupScalar "static-dir" m + td <- lookupScalar "template-dir" m + pn <- lookupScalar "port" m >>= Safe.Failure.read + return \$ Settings ar sr sd td pn + +load$Datatype$ :: IO $Datatype$ +load$Datatype$ = do + s <- loadSettings + tg <- loadTemplateGroup \$ templateDir s + return \$ $Datatype$ s tg + +main :: IO () +main = do + datatype <- load$Datatype$ + app <- toWaiApp datatype + basicHandler (portNumber \$ settings datatype) app + +instance Yesod $Datatype$ where + resources = [\$mkResources| +/: + GET: homepageH +/static/*: serveStatic' +|] + applyLayout = defaultApplyLayout + +instance YesodApproot $Datatype$ where + approot = sApproot . settings + +instance YesodTemplate $Datatype$ where + getTemplateGroup = templateGroup + defaultTemplateAttribs y _ = return + . setHtmlAttrib "approot" (approot y) + . setHtmlAttrib "staticroot" (staticRoot \$ settings y) + +homepageH :: Handler $Datatype$ RepHtml +homepageH = templateHtml "homepage" return + +serveStatic' :: Method -> [String] + -> Handler $Datatype$ [(ContentType, Content)] +serveStatic' method pieces = do + y <- getYesod + let sd = staticDir \$ settings y + serveStatic (fileLookupDir sd) method pieces diff --git a/CLI/skel/LICENSE b/CLI/skel/LICENSE new file mode 100644 index 00000000..29ed9276 --- /dev/null +++ b/CLI/skel/LICENSE @@ -0,0 +1,25 @@ +The following license covers this documentation, and the source code, except +where otherwise indicated. + +Copyright $year$, $author$. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/CLI/skel/settings.yaml b/CLI/skel/settings.yaml new file mode 100644 index 00000000..e98384d4 --- /dev/null +++ b/CLI/skel/settings.yaml @@ -0,0 +1,5 @@ +approot: http://localhost:3000/ +static-root: http://localhost:3000/static/ +static-dir: static +template-dir: templates +port: 3000 diff --git a/CLI/skel/static/style.css b/CLI/skel/static/style.css new file mode 100644 index 00000000..d5de60d6 --- /dev/null +++ b/CLI/skel/static/style.css @@ -0,0 +1,10 @@ +html { + background: #ccc; +} +body { + width: 760px; + margin: 10px auto; + padding: 10px; + border: 1px solid #333; + background: #fff; +} diff --git a/CLI/skel/templates/homepage.st b/CLI/skel/templates/homepage.st new file mode 100644 index 00000000..fffa55d9 --- /dev/null +++ b/CLI/skel/templates/homepage.st @@ -0,0 +1,7 @@ +\$layout( + title={Homepage}; + content={ +

Homepage

+

You probably want to put your own content here.

+ } +)\$ diff --git a/CLI/skel/templates/layout.st b/CLI/skel/templates/layout.st new file mode 100644 index 00000000..fadca393 --- /dev/null +++ b/CLI/skel/templates/layout.st @@ -0,0 +1,11 @@ + + + + \$title\$ + + \$extrahead\$ + + + \$content\$ + + diff --git a/CLI/skel/webapp.cabal b/CLI/skel/webapp.cabal new file mode 100644 index 00000000..0fe22111 --- /dev/null +++ b/CLI/skel/webapp.cabal @@ -0,0 +1,21 @@ +name: $project$ +version: 0.0.0 +license: BSD3 +license-file: LICENSE +author: $author$ $email$ +maintainer: $author$ $email$ +synopsis: A web application based on Yesod. +description: The default web application. You might want to change this. +category: Web +stability: Stable +cabal-version: >= 1.2 +build-type: Simple +homepage: $homepage$ + +executable $project$ + build-depends: base >= 4 && < 5, + yesod >= 0.0.0 && < 0.1, + safe-failure >= 0.4.0 && < 0.5, + data-object-yaml >= 0.2.0.1 && < 0.3 + main-is: $Datatype$.hs + ghc-options: -Wall diff --git a/CLI/yesod.hs b/CLI/yesod.hs new file mode 100644 index 00000000..3f6606f9 --- /dev/null +++ b/CLI/yesod.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE TemplateHaskell #-} +import Data.FileEmbed +import Text.StringTemplate +import Data.ByteString.Char8 (ByteString, unpack) +import System.Directory +import System.Environment +import System.IO +import Data.Char + +skel :: [(FilePath, ByteString)] +skel = $(embedDir "CLI/skel") + +yesodInit :: FilePath -> [(String, String)] -> IO () +yesodInit topDir a = do + mapM_ (\x -> createDirectoryIfMissing True $ topDir ++ x) + ["static", "templates"] + mapM_ go skel + where + go (fp, bs) = do + let temp = newSTMP $ unpack bs + writeFile (topDir ++ fp) $ toString $ setManyAttrib a temp + +main :: IO () +main = do + args <- getArgs + case args of + ["init"] -> yesodInit' + _ -> usage + +usage :: IO () +usage = putStrLn "Currently, the only support operation is \"init\"." + +prompt :: String -> (String -> Bool) -> IO String +prompt s t = do + putStr s + hFlush stdout + x <- getLine + if t x + then return x + else do + putStrLn "That was not valid input." + prompt s t + +yesodInit' :: IO () +yesodInit' = do + putStrLn "Let's get started created a Yesod web application." + dest <- + prompt + "In which directory would you like to put the application? " + (not . null) + dt <- + prompt + "Give a data type name (first letter capital): " + (\x -> not (null x) && isUpper (head x)) + pr <- prompt + "Name of project (cabal file): " + (not . null) + au <- prompt + "Author (cabal file): " + (not . null) + em <- prompt + "Author email (cabal file): " + (not . null) + ho <- prompt + "Homepage (cabal file): " + (not . null) + yesodInit (dest ++ "/") + [ ("Datatype", dt) + , ("project", pr) + , ("author", au) + , ("email", em) + , ("homepage", ho) + ] + renameFile (dest ++ "/webapp.cabal") (dest ++ "/" ++ pr ++ ".cabal") + renameFile (dest ++ "/App.hs") (dest ++ "/" ++ dt ++ ".hs") + putStrLn "Your project has been initialized." diff --git a/Yesod.hs b/Yesod.hs index af6f8dbe..59fec055 100644 --- a/Yesod.hs +++ b/Yesod.hs @@ -25,6 +25,7 @@ module Yesod , module Yesod.Template , module Web.Mime , Application + , Method (..) ) where #if TEST @@ -45,5 +46,5 @@ import Yesod.Form import Yesod.Yesod import Yesod.Definitions import Yesod.Handler -import Network.Wai (Application) +import Network.Wai (Application, Method (..)) import Yesod.Template diff --git a/Yesod/Helpers/Static.hs b/Yesod/Helpers/Static.hs index c48d9514..ecb7d457 100644 --- a/Yesod/Helpers/Static.hs +++ b/Yesod/Helpers/Static.hs @@ -27,7 +27,6 @@ import Control.Monad import Yesod import Data.List (intercalate) -import Network.Wai (Method (GET)) type FileLookup = FilePath -> IO (Maybe (Either FilePath Content)) diff --git a/Yesod/Template.hs b/Yesod/Template.hs index 03c073a3..b5b7a4f5 100644 --- a/Yesod/Template.hs +++ b/Yesod/Template.hs @@ -6,6 +6,7 @@ module Yesod.Template , Template , TemplateGroup , loadTemplateGroup + , defaultApplyLayout -- * HTML templates , HtmlTemplate (..) , templateHtml @@ -43,6 +44,23 @@ instance Exception NoSuchTemplate loadTemplateGroup :: FilePath -> IO TemplateGroup loadTemplateGroup = directoryGroupRecursiveLazy +defaultApplyLayout :: YesodTemplate y + => y + -> Request + -> String -- ^ title + -> Html -- ^ body + -> Content +defaultApplyLayout y req t b = + case getStringTemplate "layout" $ getTemplateGroup y of + Nothing -> cs (cs (Tag "title" [] $ cs t, b) :: HtmlDoc) + Just temp -> + ioTextToContent + $ fmap (render . unHtmlTemplate) + $ defaultTemplateAttribs y req + $ setHtmlAttrib "title" t + $ setHtmlAttrib "content" b + $ HtmlTemplate temp + type TemplateName = String newtype HtmlTemplate = HtmlTemplate { unHtmlTemplate :: Template } diff --git a/Yesod/Yesod.hs b/Yesod/Yesod.hs index a50556ef..44112e58 100644 --- a/Yesod/Yesod.hs +++ b/Yesod/Yesod.hs @@ -6,6 +6,7 @@ module Yesod.Yesod , applyLayoutJson , getApproot , toWaiApp + , basicHandler ) where import Data.Object.Html @@ -26,6 +27,10 @@ import Network.Wai.Middleware.ClientSession import Network.Wai.Middleware.Jsonp import Network.Wai.Middleware.MethodOverride +import qualified Network.Wai.Handler.SimpleServer as SS +import qualified Network.Wai.Handler.CGI as CGI +import System.Environment (getEnvironment) + class Yesod a where -- | Please use the Quasi-Quoter, you\'ll be happier. For more information, -- see the examples/fact.lhs sample. @@ -139,3 +144,15 @@ httpAccept = map contentTypeFromBS . fromMaybe B.empty . lookup W.Accept . W.requestHeaders + +-- | Runs an application with CGI if CGI variables are present (namely +-- PATH_INFO); otherwise uses SimpleServer. +basicHandler :: Int -- ^ port number + -> W.Application -> IO () +basicHandler port app = do + vars <- getEnvironment + case lookup "PATH_INFO" vars of + Nothing -> do + putStrLn $ "http://localhost:" ++ show port ++ "/" + SS.run port app + Just _ -> CGI.run app diff --git a/yesod.cabal b/yesod.cabal index 34edd6b8..388ace3d 100644 --- a/yesod.cabal +++ b/yesod.cabal @@ -70,6 +70,11 @@ library Web.Mime ghc-options: -Wall -Werror +executable yesod + ghc-options: -Wall + build-depends: file-embed >= 0.0.3 && < 0.1 + main-is: CLI/yesod.hs + executable runtests if flag(buildtests) Buildable: True