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

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.

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.

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.

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.

Can't launch ocsigen server due to failure : ("That function cannot be called here because it needs information about the request or the site.")

I want to create a service who generates its HTML according to the parameter given and a map. Given the parameter, the service search in the map for the html, and a function to launch on client side.
type sample =
(string (* little text *)*
Html5_types.html Eliom_content.Html5.elt (* html page *) *
(unit -> unit)(* Demonstration function *))
Given that the function is to be launched on client side, I insert it in the map as a client value :
{client{
let demo_function = ignore (Ojquery.add_html
(Ojquery.jQ "li") "<p id='test1'>new paragraph</p>") }}
let get_samples () =
let samples_map = Samples.empty in
let samples_map = Samples.add "add_html"
("text",
(Eliom_tools.F.html
(** html stuff **)
),
{unit->unit{demo_function}}) samples_map in
samples_map
And then I register the service like this :
let sample_service =
Eliom_service.service
~path:["examples"]
~get_params:Eliom_parameter.(string "entry")
()
let () =
Examples_app.register
~service:sample_service
(fun (entry) () ->
try
(let entry = Samples.find entry samples_map in
let html = ((function (name, html, func) -> html) entry) in
let func = ((function (name, html, func) -> func) entry) in
ignore {unit{%func ()}};
Lwt.return (html))
with Not_found -> Lwt.return (not_found)
)
The rest of the code is pretty much only the result of a classic eliom-distillery, with the inclusion of the ojquery package for the client function used.
The compilation phase goes smoothly, but when I try to launch the server, I get the following error message :
ocsigenserver: main: Fatal - Error in configuration file: Error while parsing configuration file: Eliom: while loading local/lib/examples/examples.cma: Failure("That function cannot be called here because it needs information about the request or the site.")
My first guess was that it is due to the fact that I store client values outside of a service, but is there any way to store this kind of values on the server?
I tried to wrap them in regular functions :
let demo_serv_func () = {unit{demo_client_func ()}}
But the problem remained...
I found the issue. The problem was not because I stored client functions, but because I used Eliom_tools.F.html outside of a service.
It happens that Eliom_tools needs the context of the service to function, and since I was storing it outside of the service, it could not work.
I solved the issue by using Eliom_tools inside the service, and storing the body of the HTML page in the map.