Added rudimentary JSON support

This commit is contained in:
Michael Snoyman 2010-04-20 16:36:16 -07:00
parent e280e284f8
commit e8812472c0
6 changed files with 148 additions and 14 deletions

View File

@ -23,6 +23,7 @@ module Yesod
, module Yesod.Form , module Yesod.Form
, module Web.Mime , module Web.Mime
, module Yesod.Hamlet , module Yesod.Hamlet
, module Yesod.Json
, Application , Application
, Method (..) , Method (..)
, cs , cs
@ -32,10 +33,12 @@ module Yesod
import Yesod.Response hiding (testSuite) import Yesod.Response hiding (testSuite)
import Yesod.Request hiding (testSuite) import Yesod.Request hiding (testSuite)
import Web.Mime hiding (testSuite) import Web.Mime hiding (testSuite)
import Yesod.Json hiding (testSuite)
#else #else
import Yesod.Response import Yesod.Response
import Yesod.Request import Yesod.Request
import Web.Mime import Web.Mime
import Yesod.Json
#endif #endif
import Yesod.Dispatch import Yesod.Dispatch

View File

@ -6,7 +6,6 @@
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types #-} {-# LANGUAGE Rank2Types #-}
{-# LANGUAGE NoMonomorphismRestriction #-} -- FIXME remove
--------------------------------------------------------- ---------------------------------------------------------
-- --
-- Module : Yesod.Helpers.Auth -- Module : Yesod.Helpers.Auth
@ -165,19 +164,25 @@ getDisplayName (Rpxnow.Identifier ident extra) = helper choices where
Nothing -> helper xs Nothing -> helper xs
Just y -> y Just y -> y
getCheck :: Yesod master => GHandler Auth master RepHtml getCheck :: Yesod master => GHandler Auth master RepHtmlJson
getCheck = do getCheck = do
ident <- maybeIdentifier ident <- maybeIdentifier
dn <- displayName dn <- displayName
-- FIXME applyLayoutJson let arg = (cs $ fromMaybe "" ident, cs $ fromMaybe "" dn)
simpleApplyLayout "Authentication Status" $ [$hamlet| applyLayoutJson "Authentication Status" arg html json
where
html = [$hamlet|
%h1 Authentication Status %h1 Authentication Status
%dl %dl
%dt identifier %dt identifier
%dd $fst$ %dd $fst$
%dt displayName %dt displayName
%dd $snd$ %dd $snd$
|] (cs $ fromMaybe "" ident, cs $ fromMaybe "" dn) |]
json (ident, dn) =
jsonMap [ (jsonScalar $ cs "ident", jsonScalar ident)
, (jsonScalar $ cs "displayName", jsonScalar dn)
]
getLogout :: GHandler Auth master () getLogout :: GHandler Auth master ()
getLogout = do getLogout = do
@ -215,7 +220,7 @@ redirectLogin = do
{- FIXME {- FIXME
-- | Determinge the path requested by the user (ie, the path info). This -- | Determinge the path requested by the user (ie, the path info). This
-- includes the query string. -- includes the query string.
requestPath :: (Functor m, Monad m, RequestReader m) => m String --FIXME unused requestPath :: (Functor m, Monad m, RequestReader m) => m String
requestPath = do requestPath = do
env <- waiRequest env <- waiRequest
let q = case B8.unpack $ W.queryString env of let q = case B8.unpack $ W.queryString env of

107
Yesod/Json.hs Normal file
View File

@ -0,0 +1,107 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE CPP #-}
module Yesod.Json
( Json
, jsonToContent
-- * Generate Json output
, jsonScalar
, jsonList
, jsonList'
, jsonMap
, jsonMap'
#if TEST
, testSuite
#endif
)
where
import Text.Hamlet.Monad
import Control.Applicative
import Data.Text (Text)
import Web.Encodings
import Yesod.Hamlet
import Control.Monad (when)
#if TEST
import Yesod.Response hiding (testSuite)
import Data.Text.Lazy (unpack)
import qualified Data.Text as T
#else
import Yesod.Response
#endif
import Yesod.Handler
#if TEST
import Test.Framework (testGroup, Test)
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.HUnit hiding (Test)
import Test.QuickCheck
import Control.Monad (when)
#endif
newtype Json url m a = Json { unJson :: Hamlet url m a }
deriving (Functor, Applicative, Monad)
jsonToContent :: Json (Routes sub) IO () -> GHandler sub master Content
jsonToContent = hamletToContent . unJson
htmlContentToText :: HtmlContent -> Text
htmlContentToText (Encoded t) = t
htmlContentToText (Unencoded t) = encodeHtml t
jsonScalar :: Monad m => HtmlContent -> Json url m ()
jsonScalar s = Json $ do
outputString "\""
output $ encodeJson $ htmlContentToText s
outputString "\""
jsonList :: Monad m => [Json url m ()] -> Json url m ()
jsonList = jsonList' . fromList
jsonList' :: Monad m => Enumerator (Json url m ()) (Json url m) -> Json url m () -- FIXME simplify type
jsonList' (Enumerator enum) = do
Json $ outputString "["
_ <- enum go False
Json $ outputString "]"
where
go putComma j = do
when putComma $ Json $ outputString ","
() <- j
return $ Right True
jsonMap :: Monad m => [(Json url m (), Json url m ())] -> Json url m ()
jsonMap = jsonMap' . fromList
jsonMap' :: Monad m => Enumerator (Json url m (), Json url m ()) (Json url m) -> Json url m () -- FIXME simplify type
jsonMap' (Enumerator enum) = do
Json $ outputString "{"
_ <- enum go False
Json $ outputString "}"
where
go putComma (k, v) = do
when putComma $ Json $ outputString ","
() <- k
Json $ outputString ":"
() <- v
return $ Right True
#if TEST
testSuite :: Test
testSuite = testGroup "Yesod.Json"
[ testCase "simple output" caseSimpleOutput
]
caseSimpleOutput :: Assertion
caseSimpleOutput = do
let j = do
jsonMap
[ (jsonScalar $ T.pack "foo" , jsonList
[ jsonScalar $ T.pack "bar"
, jsonScalar $ T.pack "baz"
])
]
t <- hamletToText id $ unJson j
"{\"foo\":[\"bar\",\"baz\"]}" @=? unpack t
#endif

View File

@ -4,6 +4,7 @@ module Yesod.Yesod
( Yesod (..) ( Yesod (..)
, YesodSite (..) , YesodSite (..)
, simpleApplyLayout , simpleApplyLayout
, applyLayoutJson
, getApproot , getApproot
) where ) where
@ -16,6 +17,7 @@ import Control.Arrow ((***))
import Network.Wai.Middleware.ClientSession import Network.Wai.Middleware.ClientSession
import qualified Network.Wai as W import qualified Network.Wai as W
import Yesod.Definitions import Yesod.Definitions
import Yesod.Json
import Web.Routes.Quasi (QuasiSite (..)) import Web.Routes.Quasi (QuasiSite (..))
@ -59,6 +61,24 @@ class YesodSite a => Yesod a where
-- trailing slash. -- trailing slash.
approot :: a -> Approot approot :: a -> Approot
applyLayoutJson :: Yesod master
=> String -- ^ title
-> x
-> (x -> Hamlet (Routes sub) IO ())
-> (x -> Json (Routes sub) IO ())
-> GHandler sub master RepHtmlJson
applyLayoutJson t x toH toJ = do
let pc = PageContent
{ pageTitle = cs t
, pageHead = return () -- FIXME allow user to supply?
, pageBody = toH x
}
y <- getYesodMaster
rr <- getRequest
html <- hamletToContent $ applyLayout y pc rr
json <- jsonToContent $ toJ x
return $ RepHtmlJson html json
-- | A convenience wrapper around 'simpleApplyLayout for HTML-only data. -- | A convenience wrapper around 'simpleApplyLayout for HTML-only data.
simpleApplyLayout :: Yesod master simpleApplyLayout :: Yesod master
=> String -- ^ title => String -- ^ title

View File

@ -1,20 +1,18 @@
import Test.Framework (defaultMain) import Test.Framework (defaultMain)
import qualified Yesod.Response import qualified Yesod.Response
import qualified Yesod.Resource
import qualified Yesod.Request import qualified Yesod.Request
import qualified Data.Object.Html -- FIXME import qualified Test.Errors
import qualified Test.Errors -- FIXME import qualified Test.QuasiResource
import qualified Test.QuasiResource
import qualified Web.Mime import qualified Web.Mime
import qualified Yesod.Json
main :: IO () main :: IO ()
main = defaultMain main = defaultMain
[ Yesod.Response.testSuite [ Yesod.Response.testSuite
, Yesod.Resource.testSuite
, Yesod.Request.testSuite , Yesod.Request.testSuite
, Data.Object.Html.testSuite -- FIXME , Test.Errors.testSuite
, Test.Errors.testSuite -- FIXME, Test.QuasiResource.testSuite
, Test.QuasiResource.testSuite
, Web.Mime.testSuite , Web.Mime.testSuite
, Yesod.Json.testSuite
] ]

View File

@ -67,6 +67,7 @@ library
Yesod.Definitions Yesod.Definitions
Yesod.Form Yesod.Form
Yesod.Hamlet Yesod.Hamlet
Yesod.Json
Yesod.Handler Yesod.Handler
Yesod.Dispatch Yesod.Dispatch
Yesod.Yesod Yesod.Yesod