I18N and exception catching
This commit is contained in:
parent
ec355d5811
commit
1b643b93e4
@ -41,6 +41,7 @@ import Control.Applicative
|
|||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import qualified Data.ByteString.Lazy as B
|
import qualified Data.ByteString.Lazy as B
|
||||||
import qualified Hack
|
import qualified Hack
|
||||||
|
import qualified Control.OldException
|
||||||
|
|
||||||
------ Handler monad
|
------ Handler monad
|
||||||
newtype HandlerT m a =
|
newtype HandlerT m a =
|
||||||
@ -55,7 +56,9 @@ runHandler :: (ErrorResult -> Reps)
|
|||||||
-> RawRequest
|
-> RawRequest
|
||||||
-> IO Hack.Response
|
-> IO Hack.Response
|
||||||
runHandler eh wrapper ctypesAll (HandlerT inside) rr = do
|
runHandler eh wrapper ctypesAll (HandlerT inside) rr = do
|
||||||
(x, headers') <- inside rr
|
(x, headers') <- Control.OldException.catch
|
||||||
|
(inside rr)
|
||||||
|
(\e -> return (Left $ InternalError $ show e, []))
|
||||||
let extraHeaders =
|
let extraHeaders =
|
||||||
case x of
|
case x of
|
||||||
Left r -> getHeaders r
|
Left r -> getHeaders r
|
||||||
@ -67,14 +70,15 @@ runHandler eh wrapper ctypesAll (HandlerT inside) rr = do
|
|||||||
Left r -> getStatus r
|
Left r -> getStatus r
|
||||||
Right _ -> 200
|
Right _ -> 200
|
||||||
(ctype, selectedRep) <- chooseRep outReps ctypesAll
|
(ctype, selectedRep) <- chooseRep outReps ctypesAll
|
||||||
finalRep <- wrapper ctype selectedRep
|
let languages = [] -- FIXME
|
||||||
|
finalRep <- wrapper ctype $ selectedRep languages
|
||||||
let headers'' = ("Content-Type", ctype) : headers
|
let headers'' = ("Content-Type", ctype) : headers
|
||||||
return $! Hack.Response statusCode headers'' finalRep
|
return $! Hack.Response statusCode headers'' finalRep
|
||||||
|
|
||||||
chooseRep :: Monad m
|
chooseRep :: Monad m
|
||||||
=> [(ContentType, B.ByteString)]
|
=> Reps
|
||||||
-> [ContentType]
|
-> [ContentType]
|
||||||
-> m (ContentType, B.ByteString)
|
-> m Rep
|
||||||
chooseRep rs cs
|
chooseRep rs cs
|
||||||
| null rs = fail "All reps must have at least one representation"
|
| null rs = fail "All reps must have at least one representation"
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
|
|||||||
58
Web/Restful/I18N.hs
Normal file
58
Web/Restful/I18N.hs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{-# LANGUAGE TypeSynonymInstances #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
{-# LANGUAGE OverlappingInstances #-}
|
||||||
|
---------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- Module : Web.Restful.I18N
|
||||||
|
-- Copyright : Michael Snoyman
|
||||||
|
-- License : BSD3
|
||||||
|
--
|
||||||
|
-- Maintainer : Michael Snoyman <michael@snoyman.com>
|
||||||
|
-- Stability : Stable
|
||||||
|
-- Portability : portable
|
||||||
|
--
|
||||||
|
-- Simple method for internationalization.
|
||||||
|
--
|
||||||
|
---------------------------------------------------------
|
||||||
|
module Web.Restful.I18N
|
||||||
|
( Language
|
||||||
|
, Translator
|
||||||
|
, I18N (..)
|
||||||
|
, toTranslator
|
||||||
|
) where
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as B
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import Data.ByteString.Class
|
||||||
|
|
||||||
|
type Language = String
|
||||||
|
type Translator = [Language] -> B.ByteString
|
||||||
|
|
||||||
|
class I18N a where
|
||||||
|
translate :: a -> Translator
|
||||||
|
|
||||||
|
instance I18NString a => I18N a where
|
||||||
|
translate a langs = toLazyByteString $ helper langs where
|
||||||
|
helper [] = defTrans a
|
||||||
|
helper (l:ls) =
|
||||||
|
case tryTranslate a l of
|
||||||
|
Nothing -> helper ls
|
||||||
|
Just s -> s
|
||||||
|
|
||||||
|
class I18NString a where
|
||||||
|
defTrans :: a -> String
|
||||||
|
tryTranslate :: a -> Language -> Maybe String
|
||||||
|
|
||||||
|
toTranslator :: LazyByteString lbs => lbs -> Translator
|
||||||
|
toTranslator = translate . toLazyByteString
|
||||||
|
|
||||||
|
instance I18N B.ByteString where
|
||||||
|
translate = const
|
||||||
|
|
||||||
|
instance I18N BS.ByteString where
|
||||||
|
translate bs _ = toLazyByteString bs
|
||||||
|
|
||||||
|
instance I18NString String where
|
||||||
|
defTrans = id
|
||||||
|
tryTranslate = const . Just
|
||||||
@ -15,7 +15,8 @@
|
|||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
module Web.Restful.Response
|
module Web.Restful.Response
|
||||||
( -- * Representations
|
( -- * Representations
|
||||||
Reps
|
Rep
|
||||||
|
, Reps
|
||||||
, HasReps (..)
|
, HasReps (..)
|
||||||
, ContentType
|
, ContentType
|
||||||
-- * Abnormal responses
|
-- * Abnormal responses
|
||||||
@ -32,21 +33,24 @@ module Web.Restful.Response
|
|||||||
, objectResponse
|
, objectResponse
|
||||||
-- * Tests
|
-- * Tests
|
||||||
, testSuite
|
, testSuite
|
||||||
|
-- * Re-export
|
||||||
|
, module Web.Restful.I18N
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.ByteString.Class
|
import Data.ByteString.Class
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Object hiding (testSuite)
|
import Data.Object hiding (testSuite)
|
||||||
import qualified Data.ByteString.Lazy as B
|
|
||||||
import Data.Object.Instances
|
import Data.Object.Instances
|
||||||
|
|
||||||
import Web.Encodings (formatW3)
|
import Web.Encodings (formatW3)
|
||||||
|
import Web.Restful.I18N
|
||||||
|
|
||||||
import Test.Framework (testGroup, Test)
|
import Test.Framework (testGroup, Test)
|
||||||
|
|
||||||
type ContentType = String
|
type ContentType = String
|
||||||
|
|
||||||
type Reps = [(ContentType, B.ByteString)]
|
type Rep = (ContentType, Translator)
|
||||||
|
type Reps = [Rep]
|
||||||
|
|
||||||
-- | Something which can be represented as multiple content types.
|
-- | Something which can be represented as multiple content types.
|
||||||
-- Each content type is called a representation of the data.
|
-- Each content type is called a representation of the data.
|
||||||
@ -105,7 +109,7 @@ genResponse :: (Monad m, LazyByteString lbs)
|
|||||||
=> ContentType
|
=> ContentType
|
||||||
-> lbs
|
-> lbs
|
||||||
-> m Reps
|
-> m Reps
|
||||||
genResponse ct lbs = return [(ct, toLazyByteString lbs)]
|
genResponse ct lbs = return [(ct, toTranslator lbs)]
|
||||||
|
|
||||||
-- | Return a response with a text/html content type.
|
-- | Return a response with a text/html content type.
|
||||||
htmlResponse :: (Monad m, LazyByteString lbs) => lbs -> m Reps
|
htmlResponse :: (Monad m, LazyByteString lbs) => lbs -> m Reps
|
||||||
@ -117,15 +121,15 @@ objectResponse = return . reps . toRawObject
|
|||||||
|
|
||||||
-- HasReps instances
|
-- HasReps instances
|
||||||
instance HasReps () where
|
instance HasReps () where
|
||||||
reps _ = [("text/plain", toLazyByteString "")]
|
reps _ = [("text/plain", translate "")]
|
||||||
instance HasReps RawObject where
|
instance HasReps RawObject where
|
||||||
reps o =
|
reps o =
|
||||||
[ ("text/html", unHtml $ safeFromObject o)
|
[ ("text/html", translate $ unHtml $ safeFromObject o)
|
||||||
, ("application/json", unJson $ safeFromObject o)
|
, ("application/json", translate $ unJson $ safeFromObject o)
|
||||||
, ("text/yaml", unYaml $ safeFromObject o)
|
, ("text/yaml", translate $ unYaml $ safeFromObject o)
|
||||||
]
|
]
|
||||||
|
|
||||||
instance HasReps [(ContentType, B.ByteString)] where
|
instance HasReps Reps where
|
||||||
reps = id
|
reps = id
|
||||||
|
|
||||||
----- Testing
|
----- Testing
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import Web.Restful.Response
|
|||||||
|
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Web.Encodings
|
import Web.Encodings
|
||||||
import Data.ByteString.Class
|
|
||||||
|
|
||||||
data AtomFeed = AtomFeed
|
data AtomFeed = AtomFeed
|
||||||
{ atomTitle :: String
|
{ atomTitle :: String
|
||||||
@ -32,7 +31,7 @@ data AtomFeed = AtomFeed
|
|||||||
}
|
}
|
||||||
instance HasReps AtomFeed where
|
instance HasReps AtomFeed where
|
||||||
reps e =
|
reps e =
|
||||||
[ ("application/atom+xml", toLazyByteString $ show e)
|
[ ("application/atom+xml", translate $ show e)
|
||||||
]
|
]
|
||||||
|
|
||||||
data AtomFeedEntry = AtomFeedEntry
|
data AtomFeedEntry = AtomFeedEntry
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import Web.Restful.Response
|
|||||||
import Web.Encodings
|
import Web.Encodings
|
||||||
import qualified Hack
|
import qualified Hack
|
||||||
import Web.Restful.Request
|
import Web.Restful.Request
|
||||||
import Data.ByteString.Class
|
|
||||||
import Data.Time (UTCTime)
|
import Data.Time (UTCTime)
|
||||||
|
|
||||||
data SitemapLoc = AbsLoc String | RelLoc String
|
data SitemapLoc = AbsLoc String | RelLoc String
|
||||||
@ -79,7 +78,7 @@ instance Show SitemapResponse where
|
|||||||
|
|
||||||
instance HasReps SitemapResponse where
|
instance HasReps SitemapResponse where
|
||||||
reps res =
|
reps res =
|
||||||
[ ("text/xml", toLazyByteString $ show res)
|
[ ("text/xml", translate $ show res)
|
||||||
]
|
]
|
||||||
|
|
||||||
sitemap :: IO [SitemapUrl] -> Handler
|
sitemap :: IO [SitemapUrl] -> Handler
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: restful
|
name: restful
|
||||||
version: 0.1.5
|
version: 0.1.6
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -47,6 +47,7 @@ library
|
|||||||
Web.Restful.Handler,
|
Web.Restful.Handler,
|
||||||
Web.Restful.Application,
|
Web.Restful.Application,
|
||||||
Web.Restful.Resource,
|
Web.Restful.Resource,
|
||||||
|
Web.Restful.I18N,
|
||||||
Data.Object.Instances,
|
Data.Object.Instances,
|
||||||
Hack.Middleware.MethodOverride,
|
Hack.Middleware.MethodOverride,
|
||||||
Web.Restful.Helpers.Auth,
|
Web.Restful.Helpers.Auth,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user