Yesod book example chat and scaffolding - haskell

I'm trying to make the chat example from the Yesod book working in the scaffolding site.
I think I've corrected almost all I had to correct, but all of that is completely new to me (it's my first "real" Haskell project) and I'm not very confident in all my modifications; moreover, I'm really stuck at the point 7. Could you comment all the following points if necessary, and helping me for the 7. (in bold some questions/remarks)?
Copy/paste the Chat.hs and Chat/Data.hs in the root of my site,
add import Chat as Import and import Chat.Data as Import to Import.NoFoundation,
add import of IO, Bool, return, Maybe(Nothing), ($) in Data.hs, since the extension NoImplicitPrelude is on Seems very clumsy... Do we have to import all the standard operators on each new file?
in Fundation.hs, add getChat in the App record (after appHttpManager and appLogger)
in Fundation.hs, add YesodChat instance for App: I had to modify the getUserName on the Just uid case (in the original example of Chat, it was just Just uid -> return uid):
Just uid -> do
muser <- runDB $ get uid
case muser of
Nothing -> error "uid not in the DB"
Just user -> return $ userIdent user
This seems very long and nested... Can we do better?
In Fundation.hs, add chatWidget ChatR after the line pc <- widgetToPageContent $ do in the defaultLayout definition.
Now, I have the following warning :
Application.hs:60:36: Warning:
Fields of ‘App’ not initialised: getChat
In the expression: App {..}
I think I have to write something like getChat <- newChan >>=Chat after the appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger and appStatic <- ... in the makeFundation definition, but the type doesn't match. I'm a totally lost here, I don't really understand how this function makeFundation works.

You actually got almost the entire way there. I think you just need to change the line to:
getChat <- fmap Chat newChan
Alternatively, if you're not familiar with he fmap function yet, you can use do notation and get:
chan <- newChan
let getChat = Chat chan

Related

Do i need a scheduler to create daily log files with the hslogger library of Haskell?

First of all, this is my first experience ever with logs; and i don't really know what a scheduler is, only heard about them.
Now, as a first approach, i want to make a file with only important logs (WARNING or above), and daily files (like 2017-7-20.log) for all possible logged messages that day.
This is my code atm:
module Utilities.Loggers where
import System.Log.Logger
-- import System.Log.Handler.Syslog (TODO: remote logs)
import System.Log.Handler.Simple
import System.Log.Handler (LogHandler, setFormatter)
import System.Log.Formatter
import Data.Time
import GHC.IO.Handle (Handle)
--------------------------------------------
importantLogsPath :: FilePath
importantLogsPath = "app/logs/prioritary.log"
logsFolder :: FilePath
logsFolder = "app/logs/allLogs/"
defaultFormat :: LogFormatter a
defaultFormat = simpleLogFormatter "[$utcTime : $tid : $loggername : $prio] $msg"
applyFormat :: LogHandler a => a -> a
applyFormat h = setFormatter h defaultFormat
--------------------------------------------
prioritaryHandler :: IO (GenericHandler Handle)
prioritaryHandler = applyFormat <$> prioritaryHandler'
where
prioritaryHandler' = fileHandler importantLogsPath WARNING
todaysHandler :: UTCTime -> IO (GenericHandler Handle)
todaysHandler = (applyFormat <$>) . todaysHandler'
where
todaysHandler' time = fileHandler (mkPath time) DEBUG
mkPath time = logsFolder ++ show (utctDay time) ++ ".log"
--------------------------------------------
initLogger :: IO ()
initLogger = do
h1 <- prioritaryHandler
timeNow <- getCurrentTime
h2 <- todaysHandler timeNow
updateGlobalLogger "MyApp" (setHandlers [h2,h1])
If i understood correctly, each handle "listens" for possible messages that are above its priority level (daily handles have the DEBUG priority, the lowest possible, so they should catch every message)
So i need something that everyday at 00:00 utctime removes the handle for the passing day and adds the handle for the new day, right?
Also i'm a bit puzzled with the naming issues for loggers/messages, but that's just me (althout any insight will be welcome)
Edit: it's a web server

Snap framework - Restrict access to the whole website including its subsnaplets

I have a simple snaplet, which has its own routing and pretty much independent of the whole application behavior. However, as for most of the application, I want to hide the new simple snaplet under the restricted area, where only logged in users can enter.
For the root snaplet the problem solved by using simple function restricted which accepts a handler and checks if the user logged in, proceeding further with the given handler or redirecting to the login screen.
Here is the whole configuration:
appInit :: SnapletInit App App
appInit = makeSnaplet "myapp" "My Example Application" Nothing $ do
fs <- nestSnaplet "foo" foo fooInit
ss <- nestSnaplet "sess" sess $
initCookieSessionManager "site_key.txt" "sess" (Just 3600)
as <- nestSnaplet "auth" auth $
initJsonFileAuthManager defAuthSettings sess "users.json"
addRoutes [("content", restricted $ render "content"),
("login", login)]
return $ App ss as fs
restricted :: Handler App App () -> Handler App App ()
restricted = requireUser auth (redirect "/login")
fooInit :: SnapletInit b Foo
fooInit = makeSnaplet "foo" "A nested snaplet" Nothing $ do
addRoutes [("info", writeText "Only registered users can have acess to it")]
return Foo
If I enter http://mywebsite/foo/info, I will be able to see the content of the subsnaplet without logging it. It seems to me, that I cannot protect all of the handlers implemented inside of my new Foo without changing that snaplet and modifying its routing. Or am I wrong?
P.S.: There is an option to use weapSite and check the request URL, but since it implies verification based on URL, not on the recourse, (handler in this case) it doesn't seem right to me.
The answer here is to use the wrapSite function. It takes an argument (Handler b v () -> Handler b v ()), which is exactly the type signature of your restricted function.

Scotty and POST params

I'm having an issue with the Scotty web server right now - rescue isn't working for unfound parameters - I'm still getting a 404 with the following code:
post "/newsletter/create" ( do
(param "subscriber[email]") `rescue` (\msg -> text msg)
formContent <- param "subscriber[email]"
text $ "found! " ++ show formContent )
I can see that when I just use params instead, my data is there, and indexed with "subscriber[email]". Is there something going on with [ escaping? Any help with this would be tremendous.
With some cleanup I got it to work:
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import qualified Data.Text.Lazy as TL
main = scotty 3000 $ do
post "/newsletter/create" $ do
formContent <- (param "subscriber[email]") `rescue` (\msg -> return msg)
text $ "found! " `TL.append` formContent
I made a bunch of modifications, but the key point was that rescue is used as a wrapper around param, not to change any internal state, hence you shouldn't call it twice. The square brackets didn't cause me any trouble.

Snap: compiled splice dependent on runtime decision and URL variable

I have a situation where I have to construct compiled splices and feed data into them which depends on the URL variable. I struggle to solve the problem.
So there is simple file name list that needs to be rendered in a table. Simple.
Files belong to a group or category so you can list all files or related to a particular category. I pull data using this function:
getFilesList :: Maybe ByteString -> AppHandler [Document]
getFilesList cat = do
let selection = maybe [] (\c -> ["category" =: T.decodeUtf8 c]) cat
r <- eitherWithDB $ rest =<< find (select selection "files") {project = ["blob" =: 0]}
return $ either (const []) id r
If it gets Nothing it pulls the whole list if it gets Just category it pulls files that belongs to that category. Easy so far.
I call the above function from within a handler so that I can feed an argument into it.
listFiles :: AppHandler [Document]
listFiles = do
cat <- getParam "cat"
let r = maybe Nothing (\c -> if c == "all" then Nothing else Just c) cat
render "files/list-files"
getFilesList r
If I get "all" or Nothing on the URL - I get the full list. Anything other then that - I get a category filtered list.
The URL root looks like this
("/files/:cat", method GET listFiles)
But now I have a problem because the "method" function will only accept Handler App App () signature. My handler returns data to be fed into the splices.
I construct my splices like so:
listFilesS :: Splices (Splice (Handler App App))
listFilesS = "files" ## files
where
files = manyWithSplices runChildren file $ lift listFiles -- Feed data here
file = do
"file-name" ## (pureSplice . textSplice $ at "name")
"file-oid" ## (pureSplice . textSplice $ id)
"file-date" ## (pureSplice . textSplice $ dateFromDoc)
"file-size" ## (pureSplice . textSplice $ fsize)
"file-type" ## (pureSplice . textSplice $ at "type")
"file-auth" ## (pureSplice . textSplice $ const "admin")
"file-link" ## (pureSplice . textSplice $ flink)
"file-category" ## (pureSplice . textSplice $ at "category")
where id = T.pack . show . valueAt "_id"
fsize = T.pack . show . round . (flip (/) 1024) . (at "size")
flink = T.append "/files/" . id
I cannot find a way around it. Probably just missing something stupid.
Any ideas what I am doing wrong?
In any case, my handler function looks incorrect since I render the template first and then pull the data. If I fix the handler then I cant feed the data based on the URL parameter.
Confused.
First of all, if listFiles is just returning [Document], then you don't want to call render "files/list-files". So the first order of business is to eliminate that line entirely. You might wonder why. That brings us to the second point. Your route should look like this:
("/files/:cat", method GET $ render "files/list-files")
Your route is the result of rendering a template. That's pretty much always the case with Heist routes. Sometimes you might want to explicitly call render. Other times you might just use the routes automatically given to you by heistServe.
I can't really comment on listFilesS without seeing more of the code for the Document API, but it looks reasonable. Assuming it works properly, you just have to bind that splice for your application with something like this:
addConfig heist $ mempty { hcCompiledSplices = listFilesS }
Then just use the files tag in your "files/list-files" template.

Haskell ZeroMQ binding not working for REQ socket

So here i was, barely able to install the libzmq on a windows desktop and then zeromq-haskell with cabal. I wanted to test the api by binding a python program with a haskell program in a hello-world type application.
So the most basic pattern i see is the request-reply pattern . First i tried to make the server in haskell (REP) and the client in python (REQ), witch failed miserably no matter what i did. The generated exception message was Exception: receive: failed (No error).
So i look inside the System.ZMQ and System.ZMQ.Base source code and i see that receive throws an error on calling c_zmq_recv , witch in turn maps directly to a ffi (?) call to the C api. So i think perhaps i didn't do the installation properly , but then i try to make the client in Haskell and the server in python and i notice it works without any problem, so perhaps the recv interface isn't the problem here.
Here is the haskell code below , with both client and server functions
import System.ZMQ
import Control.Monad (forM_,forever)
import Data.ByteString.Char8 (pack,unpack)
import Control.Concurrent (threadDelay)
clientMain :: IO ()
clientMain = withContext 1 (\context->do
putStrLn "Connecting to server"
withSocket context Req $ (\socket-> do
connect socket "tcp://127.0.0.1:5554"
putStrLn $ unwords ["Sending request"]
send socket (pack "Hello...") []
threadDelay (1*1000*1000)
reply<-receive socket []
putStrLn $ unwords ["Received response : ",unpack reply]))
serverMain :: IO ()
serverMain = withContext 1 (\context-> do
putStrLn "Listening at 5554"
withSocket context Rep $ (\socket-> do
connect socket "tcp://127.0.0.1:5554"
forever $ do
message<-receive socket [] -- this throws an IO Exception
putStrLn $ unwords ["Received request : ",unpack message]
threadDelay (1*1000*1000)
send socket (pack "World") [] ))
main :: IO ()
main = serverMain -- replace with clientMain and it works
Now i really didn't get around to testing all other modes of communication (push/pull, subscribe/publish, pair etc.) and for what i need the python server/haskell client is probably better but i am curious about weather i'm doing something wrong or if any part of my code is broken in any way.
Thanks in advance
You need to make one of the sockets (usually the server) bind, you seem to have them both connecting.
Try changing connect socket "tcp://127.0.0.1:5554" to bind socket "tcp://127.0.0.1:5554" in the serverMain function.

Resources