Passing more values into hspec tests cases with Yesod - unit-testing

I'm trying to enhance my current test fixtures with passing more than just foundation (of type App) into hspec test cases. In the example below I'm passing an additional Text value inside of a tuple (as IO (App, Text) ) as opposed to directly returning IO App
beforeOp :: IO (App, Text)
beforeOp = do
settings <- loadAppSettings
["config/test-settings.yml", "config/settings.yml"]
[]
ignoreEnv
foundation <- makeFoundation settings
wipeDB foundation
setUpFixtures foundation
return (foundation, "foo")
innerSpec :: SpecWith (App, Text)
innerSpec = do
describe "stuff" $ do
it "should work" $ do
post MyRouteR
statusIs 403
spec :: Spec
spec = before beforeOp innerSpec
I can't figure out how to correctly structure innerSpec in such a way that I can do my normal Yesod testing with functions such as post, statusIs etc, but also have the Text value be available to those specs for reading.
Without Yesod I can do something along the lines of the following:
innerSpec :: SpecWith (Int, Int)
innerSpec = do
describe "stuff" $ do
it "should work" $ \(x, y) -> do
x `shouldBe` y
and it will build just fine, but I can't quite get the types right as soon as Yesod comes into the mix. Advice?

Related

How to import Shakespearean Templates in Yesod?

I was using QuasiQuotations in Yesod, and everything worked fine. BUT my file became very large and not nice to look at. Also, my TextEditor does not highlight this syntax correctly. That is why is split my files like so:
getHomeR :: Handler Html
getHomeR = do
webSockets chatApp
defaultLayout $ do
$(luciusFile "templates/chat.lucius")
$(juliusFile "templates/chat.julius")
$(hamletFile "templates/chat.hamlet")
If this is wrong, please do tell. Doing runghc myFile.hs throws many errors like this:
chat_new.hs:115:9:
Couldn't match expected type ‘t0 -> Css’
with actual type ‘WidgetT App IO a0’
The lambda expression ‘\ _render_ajFK
-> (shakespeare-2.0.7:Text.Css.CssNoWhitespace . (foldr ($) ...))
...’
has one argument,
but its type ‘WidgetT App IO a0’ has none
In a stmt of a 'do' block:
\ _render_ajFK
...
And this.
chat_new.hs:116:9:
Couldn't match type ‘(url0 -> [(Text, Text)] -> Text)
-> Javascript’
with ‘WidgetT App IO a1’
Expected type: WidgetT App IO a1
Actual type: JavascriptUrl url0
Probable cause: ‘asJavascriptUrl’ is applied to too few arguments
...
And also one for the HTML (Hamlet).
Thus, one per template.
It seems that hamletFile and others treat templates as self-contained, while yours are referencing something from each other. You can play with order of *File calls, or use widgetFile* from Yesod.Default.Util module:
$(widgetFileNoReload def "chat")
The Reload variant is useful for development - it would make yesod devel to watch for file changes and reload them.

Could not deduce (blaze-markup-0.6.3.0:Text.Blaze.ToMarkup Day) arising from a use of ‘toHtml’

I'm trying to use Yesod to build a simple web site and I'm starting with the code from Max Tagher's excellent intro on Youtube, YesodScreencast. I've forked his code from GitHub, and I would like to add a date to the posting to indicate when it was published, but I'm running into the problem that I can't quite figure out given my low experience with Haskell and beginner's experience with Yesod. I've been unable to find an answer via the Googleplex.
Yesod provides a native dayField in Yesod.Form.Fields, so I thought that all I needed to do was to add the postdate Field in BlogPost the following to config/models using Day:
BlogPost
title Text
postdate Day
article Markdown
and add it to the blogPostForm in PostNew.hs:
blogPostForm :: AForm Handler BlogPost
blogPostForm = BlogPost
<$> areq textField (bfs ("Title" :: Text)) Nothing
<*> areq dayField (bfs ("Postdate" :: Day)) Nothing
<*> areq markdownField (bfs ("Article" :: Text)) Nothing
When this compiles I get the following error message:
Handler/Home.hs:16:11:
Could not deduce (blaze-markup-0.6.3.0:Text.Blaze.ToMarkup Day)
arising from a use of ‘toHtml’
from the context (PersistEntity BlogPost)
bound by a pattern with constructor
Entity :: forall record.
PersistEntity record =>
Key record -> record -> Entity record,
in a lambda abstraction
at Handler/Home.hs:16:11-34
In the first argument of ‘asWidgetT GHC.Base.. toWidget’, namely
‘toHtml (blogPostPostdate post_apZp)’
In a stmt of a 'do' block:
(asWidgetT GHC.Base.. toWidget)
(toHtml (blogPostPostdate post_apZp))
In the expression:
do { (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.6.3.0:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<h4><li><a href=\"");
(getUrlRenderParams
>>=
(\ urender_apZq
-> (asWidgetT GHC.Base.. toWidget)
(toHtml
(\ u_apZr -> urender_apZq u_apZr [] (PostDetailsR id_apZo)))));
(asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.6.3.0:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"\">");
(asWidgetT GHC.Base.. toWidget)
(toHtml (blogPostPostdate post_apZp));
.... }
If I change Day to Text, everything works as I expect. I'm not sure why Yesod can't deal with a Day since it has a dayField in Yesod.Form.Fields that I would expect to handle this. I figure this is something simple, but I can't seem to determine what I need to do to fix this error.
It appears there is no instance for ToMarkup for the Date datatype.
You could supply an instance yourself:
instance ToMarkup Date where
toMarkup = toMarkup . show
Which turns your date into a string and then converts it to Markup. If the default show instance doesn't fit your needs you could supply a formatter yourself and put it in the place of show.

Yesod Mform and hamlet

Hi I am new to yesod and following the documentation to make a form. In the documentation the form template was created in .hs file itself. But I have a separate hamlet where I want to customize.
I want to access "fields" in my hamlet file. The expected type of 'generateFormPost' is (xml, Enctype) . Can anybody tell me what I should be returning from 'tableMform extra' . I think it should be in xml format. But I think I should not be using toWidget as in below example of documentation.
tableMform extra = do
fields <- forM lis (\(w,h) -> mopt intField "this is not used" (Just h) )
return (fields) ---I know this line has the type error. Can anybody suggest how to deal with it
{-
--I am referring this code from yesod website to make my form. In this it was using runFormGet, but I want use generateFormPost and moreover it was creating a widget which is used in displaying the website. I don't want to create the widget here but in my hamlet file where the 'fields' is accessed via interpolation.
personForm :: Html -> MForm Handler (FormResult Person, Widget)
personForm extra = do
(nameRes, nameView) <- mreq textField "this is not used" Nothing
(ageRes, ageView) <- mreq intField "neither is this" Nothing
let personRes = Person <$> nameRes <*> ageRes
let widget = do
toWidget
[lucius|
##{fvId ageView} {
width: 3em;
}
|]
[whamlet|
#{extra}
<p>
Hello, my name is #
^{fvInput nameView}
\ and I am #
^{fvInput ageView}
\ years old. #
<input type=submit value="Introduce myself">
|]
return (personRes, widget)
-}
getHomeR :: Handler Html
getHomeR = defaultLayout $ do
-- Generate the form to be displayed
(fields, enctype) <- generateFormPost tableMform
let (fires,fiview) = unzip fields
$(widgetFile "layout")
|]
Please let me know if there is any misunderstanding. I have idea of how to get the form from the way done in the documentation, but I want to use a separate hamlet file, as I want to customize the look of the form.
Thanks
Sai
EDIT:
Sorry, I wasn't clear. I was trying to make a Mform where instead of creating the layout of the form in the ".hs" file , I wanted to give the layout in hamlet file. I have done it through http://pastebin.com/fwpZsKXy . But after doing that I could split it in to two files as I wanted. I have solved those errors. Thanks anyways
I got it. I was not clear of what "tableMform extra" has to return. I know that it has to return something of type [(FormResult a, xml)][1] . But then I was not sure what the type of "forM lis ((w,h) -> mopt intField (fromString w) (Just h) )" -- Line 2 was , So I followed what was done in documentation did it in the way it was done there.(without use of external widget file) .
After doing that I tried to do in the way I wanted to do i.e using a separate hamlet, julius and lucius files. http://pastebin.com/FgGph2CU . It worked !!
In summary I wasn't clear of the 'type' of "forM lis ((w,h) -> mopt intField (fromString w) (Just h) )" . Once I figured that out, it was easy.

Generating template with some logic via HStringTemplate

Here is some invalid HStringTemplate syntax:
option_a = $options.a$
option_b = $options.b$
$if options.option_c_is_needed$
option_c = $option.c$
$end$
In other words, part of template file should be created only if specific predicate is true. How can it be achieved via HStringTemplate? If there is no way to do that in it, what libraries could be helpful here?
May be there is some analog of erubis mechanism with ability to use haskell code inside template files?
Hammar's comment is correct. See below:
*Main Text.StringTemplate> render $ setAttribute "optSet" False $ (newSTMP "OptSet: $if(optSet)$Option Is Set$else$Option Isn't Set$endif$" :: StringTemplate String)
"OptSet: Option Isn't Set"
*Main Text.StringTemplate> render $ setAttribute "optSet" True $ (newSTMP "OptSet: $if(optSet)$Option Is Set$else$Option Isn't Set$endif$" :: StringTemplate String)
"OptSet: Option Is Set"

Writing Haskell interpreter in C++ (using ghc or hugs as library)

I'm writing a C++ application that needs to interpret and evaluate haskell code. This code isn't known at compile time but given by the user.
Is there a way to use a haskell compiler/interpreter (like GHCi or hugs) as a library?
I found FFI but this seems only to work for haskell code that is known at compile time.
I found the GHC API and hint, but they seem only to work when I want to interpret haskell code from out of haskell.
Instead of using the GHC api I would suggest binding to Hint for this particular approach, which is just a simplified wrapper around the GHC api. The reason I would recommend this is because the GHC api has a bit of a steep learning curve.
But anyway, Like I said In my comment, depending on how deep you want this to go it would require surprisingly few FFI calls. Below I give an example on how to run expressions from a loaded file and return the results (only if there's a show instance). This is just the basics, returning the results as a structure should be possible too.
module FFIInterpreter where
import Language.Haskell.Interpreter
import Data.IORef
import Foreign.StablePtr
type Session = Interpreter ()
type Context = StablePtr (IORef Session)
-- ## Export
-- | Create a new empty Context to be used when calling any functions inside
-- this class.
-- .
-- String: The path to the module to load or the module name
createContext :: ModuleName -> IO Context
createContext name
= do let session = newModule name
_ <- runInterpreter session
liftIO $ newStablePtr =<< newIORef session
newModule :: ModuleName -> Session
newModule name = loadModules [name] >> setTopLevelModules [name]
-- ## Export
-- | free a context up
freeContext :: Context -> IO ()
freeContext = freeStablePtr
-- ## Export = evalExpression
runExpr :: Context -> String -> IO String
runExpr env input
= do env_value <- deRefStablePtr env
tcs_value <- readIORef env_value
result <- runInterpreter (tcs_value >> eval input)
return $ either show id result
Since we have to exit haskell land we have to have some way to refer to the Context, We can do this with a StablePtr and I just wrap it in an IORef to make it mutable in case you want to change things in the future. Note that the GHC API does not support type checking an in-memory buffer, so you have to save the code you want to interpret to a temporary file before loading it.
The -- ## Annotations are for my tool Hs2lib, don't mind them if you don't use it.
My test file is
module Test where
import Control.Monad
import Control.Monad.Instances
-- | This function calculates the value \x->x*x
bar :: Int -> Int
bar = join (*)
and we can test this using a simple test
*FFIInterpreter> session <- createContext "Test"
*FFIInterpreter> runExpr session "bar 5"
"25"
So yeah, it works in Haskell, now to make it work outside of haskell.
Just add to the top of the file a few instructions for Hs2lib on how to marshal ModuleName because that type is defined in a file which it doesn't have the source to.
{- ## INSTANCE ModuleName 0 ## -}
{- ## HS2HS ModuleName CWString ## -}
{- ## IMPORT "Data.IORef" ## -}
{- ## IMPORT "Language.Haskell.Interpreter" ## -}
{- ## HS2C ModuleName "wchar_t*#4" ## -}
or
{- ## HS2C ModuleName "wchar_t*#8" ## -}
if on a 64bit architecture,
and Just invoke Hs2lib
PS Haskell\FFIInterpreter> hs2lib .\FFIInterpreter.hs -n "HsInterpreter"
Linking main.exe ...
Done.
And you'll end up with among others, an Include file with
#ifdef __cplusplus
extern "C" {
#endif
// Runtime control methods
// HsStart :: IO ()
extern CALLTYPE(void) HsStart ( void );
// HsEnd :: IO ()
extern CALLTYPE(void) HsEnd ( void );
// createContext :: ModuleName -> IO (StablePtr (IORef (Interpreter ())))
//
// Create a new empty Context to be used when calling any functionsinside this class.
// String: The path to the module to load or themodule name
//
extern CALLTYPE(void*) createContext (wchar_t* arg1);
// freeContext :: StablePtr (IORef (Interpreter ())) -> IO ()
//
// free a context up
//
extern CALLTYPE(void) freeContext (void* arg1);
// evalExpression :: StablePtr (IORef (Interpreter ())) -> String -> IO String
extern CALLTYPE(wchar_t*) evalExpression (void* arg1, wchar_t* arg2);
#ifdef __cplusplus
}
#endif
I haven't tested the C++ side, but there's no reason it shouldn't work.
This is a very barebones example, if you compile it to a dynamic lib you probably want to redirect stdout, stderr and stdin.
Since GHC is written in Haskell, its API is exclusively available from Haskell. Writing the interfaces you need in Haskell and binding them to C with the FFI, as Daniel Wagner suggested, is going to be the simplest route. This is probably easier than using a direct binding of the GHC API to C would be; you get to use Haskell's strengths to build the interfaces you need, and only interface with them in C++ at the top layer.
Note that Haskell's FFI will only export to C; if you want a C++-ish wrapper around it, you'll have to write it as another layer.
(BTW, Hugs is ancient and unmaintained.)