How to import Shakespearean Templates in Yesod? - templates

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.

Related

Yesod route interpolation in Julius via widgetFile

I think #{SomeRouteR} should work in .julius files, as described in:
https://www.yesodweb.com/book/shakespearean-templates#shakespearean-templates_julius_javascript
Everything was working when I first had this in my Handler:
toWidget [julius|
$("#examplespan").click(function(){
$.ajax({
url: #{ExampleR},
type: "POST",
...
|]
But now that I've changed my Handler to work like this:
$(widgetFile "example")
I added this code in example.julius:
$("#examplespan").click(function(){
$.ajax({
url: #{ExampleR},
type: "POST",
...
I now get the error:
[Error#yesod-core] expected EUrl but got Nothing for: DerefIdent (Ident "ExampleR")
CallStack (from HasCallStack):
error, called at ./Text/Shakespeare.hs:441:27 in shakespeare-2.0.20-3iMfZ8A5DXH3Twhu6IoWNc:Text.Shakespeare #(yesod-core-1.6.9-7g4SwkDmJJ261rdNBKrLzX:Yesod.Core.Class.Yesod ./Yesod/Core/Class/Yesod.hs:662:5)
What am I doing wrong?
There might not be anything wrong here. I will copy in a troubleshooting note I have written previously for my company.
DerefBranch Error from a Template File
I made a change in a template ({*.hamlet,*.cassius,*.lucius,*.julius}) and I get an error like this:
\"rawJS\")) (DerefBranch (DerefIdent (Ident \"tshow\")) (DerefIdent (Ident
\"errors\")))\nCallStack (from HasCallStack):\n error
This is an issue with [I believe] template Haskell and the way that we're compiling. Maybe it's some GHC flags, maybe it's GHCi itself, I'm not sure.
You can resolve this by touching the file that references the template, e.g., if your template file is templates/foo.julius, and you reference it with $(widgetFile "foo") in the Handler.Foo module, then touch (or save from your editor) the src/Handler/Foo.hs file and reload GHCi.

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.

Passing more values into hspec tests cases with Yesod

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?

Error: Camlp4: Uncaught exception: Not_found

I am working on an Ocsigen example (http://ocsigen.org/tuto/manual/macaque).
I get an error when trying to compile the program, as follows.
File "testDB.ml", line 15, characters 14-81 (end at line 18, character 4):
While finding quotation "table" in a position of "expr":
Available quotation expanders are:
svglist (in a position of expr)
svg (in a position of expr)
html5list (in a position of expr)
html5 (in a position of expr)
xhtmllist (in a position of expr)
xhtml (in a position of expr)
Camlp4: Uncaught exception: Not_found
My code is:
module Lwt_thread = struct
include Lwt
include Lwt_chan
end
module Lwt_PGOCaml = PGOCaml_generic.Make(Lwt_thread)
module Lwt_Query = Query.Make_with_Db(Lwt_thread)(Lwt_PGOCaml)
let get_db : unit -> unit Lwt_PGOCaml.t Lwt.t =
let db_handler = ref None in
fun () ->
match !db_handler with
| Some h -> Lwt.return h
| None -> Lwt_PGOCaml.connect ~database:"testbase" ()
let table = <:table< users (
login text NOT NULL,
password text NOT NULL
) >>
..........
I used eliom-destillery to generate the basic files.
I used "make" to compile the program.
I've tried many different things and done a google search but I can't figure out the problem. Any hints are greatly appreciated.
Generally speaking, the error message indicates that CamlP4 does not know the quotation you used, here table, which is used in your code as <:table< ... >>. The quotations can be added by CamlP4 extensions pa_xxx.cmo (or pa_xxx.cma) modules. Unless you made a typo of the quotation name, you failed to load an extension which provides it to CamlP4.
According to http://ocsigen.org/tuto/manual/macaque , Macaque (or its underlying libraries? I am not sure since I have never used it) provides the quotation table. So you have to instruct CamlP4 to load the corresponding extension. I believe the vanilla eliom-destillery is minimum for the basic eliom programming and does not cover for the extensions for Macaque.
Actually the document http://ocsigen.org/tuto/manual/macaque points out it:
We need to reference macaque in the Makefile :
SERVER_PACKAGE := macaque.syntax
This should be the CamlP4 syntax extension name required for table.

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"