I am using Yesod on my first site and I have a list of news items:
NewsItem
date UTCTime default=CURRENT_TIME
title String
content String
author String
which are retrieved in my handler:
newsitems <- runDB $ selectList [] [Desc NewsItemDate]
and ultimately used in my template:
$if null newsitems
<p>No news.
$else
$forall Entity id entry <- newsitems
<article>
<h4>#{newsItemDate entry}
<p>#{newsItemContent entry}
But I get an error about datatypes:
Handler/Home.hs:20:11:
No instance for (Text.Blaze.ToMarkup
time-1.4:Data.Time.Clock.UTC.UTCTime)
arising from a use of `toHtml'
Possible fix:
add an instance declaration for
(Text.Blaze.ToMarkup time-1.4:Data.Time.Clock.UTC.UTCTime)
In the first argument of `toWidget', namely
`toHtml (newsItemDate entry_a6ev)'
In a stmt of a 'do' block:
toWidget (toHtml (newsItemDate entry_a6ev))
In the expression:
do { toWidget
((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
"<article><h4>");
toWidget (toHtml (newsItemDate entry_a6ev));
toWidget
((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
"</h4>\
\<p>");
toWidget (toHtml (newsItemContent entry_a6ev));
.... }
So I figure I would go ahead and add to my Import.hs:
import Data.Time (UTCTime)
import Data.Time.Format (formatTime)
import Text.Blaze (ToMarkup, toMarkup)
import Text.Blaze.Internal (string)
import System.Locale (defaultTimeLocale)
-- format date as 26 July 2012
instance ToMarkup UTCTime where
toMarkup a = string (formatTime defaultTimeLocale "%e %B %Y" a)
Which does compile, but gives me an error at runtime in the browser:
Internal Server Error
PersistMarshalError "Expected UTCTime, received PersistText \"2012-08-30\""
So I am not sure how to solve this, any ideas?
EDIT: Source code to the site in case it is needed or curious: https://github.com/iaefai/socrsite
Without investigating the actual error, I think your approach is not great. You will very likely eventually want several ways of formatting a UTCTime, after all, the type is there to store times, not just dates. By giving a ToMarkup UTCTime instance, you fix this globally.
I would recommend to write functions renderAsDate :: UTCDate -> HTML, renderAsTime :: UTCDate -> HTML etc. and use them in your template, e.g. #{renderAsDate (newsItemDate entry)}.
But this won’t solve the runtime error, which comes from the serialization layer and is likely independent of your templates.
I'm pretty sure you could just use show in the hamlet? That's atleast what I've done...
#{show $ newsItemDate entry}
I've run into this instance thing before, and as this guy describes here it's something like this:
As part of this philosophy of frugality of expression Haskell doesn’t
require type signatures — although an experienced Haskeller provides
them for clarity — so type errors in this strongly typed language are
often cryptic for the uninitiated. For instance, if you define a
function f that adds two numbers and then call it with two strings,
the compiler will not complain about bad arguments, it will complain
about string not supporting operator plus. And it will formulate this
complaint in a very non-obvious way.
[1] Under "1. Haskell is Terse"...
[1] http://fpcomplete.com/ten-things-you-should-know-about-haskell-syntax/
Related
Is there a way in Haskell to parse a date from a string of format "YYYY-MM-DD"? That is, something like parseDateFromString 2016-10-20 that would then return a date in some commonly used Haskell date format.
Data.Time provides parseTimeM, which is a bit unwieldy. I might be overlooking a simpler alternative.
> import Data.Time
> parseTimeM False defaultTimeLocale "%Y-%m-%d" "2016-10-20" :: Maybe UniversalTime
Just 2016-10-20 00:00:00
The return type is fairly open. You can return the value to any monad; Maybe seemed like a good example to reflect an answer or a failure to parse the string. The type wrapped by Maybe is any instance of the ParseTime typeclass; UniversalTime is defined in the library.
The function getUrlRender is used for converting Route (HandlerSite m) to Text, through the use of Monads. For instance:
routeToText :: MonadHandler m => Route (HandlerSite m) -> m Text
routeToText url = do
r <- getUrlRender
return $ r url
getTestR :: Text -> Handler Text
getTestR something = routeToText (TestR something)
But what if I want to pass in a query parameter? Could I do it without dealing directly with Texts? Another question, could I simplify the type declaration of the function routeToText?
I will use these urls to accomplish with a JSON API specification regarding pagination, I've created all the abstraction to work with it but I don't know how to implement a way to put a link like http://example.com/articles?page=2 in a concise way. If there is a even better way to put the link with the query parameter integrating it beautifully with the Aeson library without converting it to Text first, please, let me know.
In a yesod application, I want to create URL attributes for a graph that will be rendered by graphviz , and I want to use interpolation. Ideally,
graphToDot nonClusteredParams { fmtNode = \ (n,l) ->
[ URL [whamlet| #{MyRoute ...} |]
} g
Of course, the types don't match:
attribute of URL is pure Text, but whamlet is monadic (widget)
when I replace by shamlet, type is fine, but it cannot interpolate: URL interpolation used, but no URL renderer provided
Is there an easy way to solve this?
This works: get the render function (in the monad), and apply (in pure code)
render <- getUrlRender
let d = graphToDot ...
[ URL $ render $ MyRoute ... ]
I found this here, where a similar problem is solved: https://github.com/yesodweb/yesod/wiki/Using-type-safe-urls-from-inside-javascript
I have been following the yesod tutorial and I am stuck on how to build a unit test involving parameters in a view that also hit a database. Backtracking a little, I followed the Echo.hs example:
getEchoR :: Text -> Handler Html
getEchoR theText = do
defaultLayout $ do
$(widgetFile "echo")
The corresponding test, note I have to cast the parameter into Text using Data.Text.pack
yit "Echo some text" $ do
get $ EchoR $ pack "Hello"
statusIs 200
Now I have the model defined like so:
Tag
name Text
type Text
With a handler that can render that that obviously take a TagId as the parameter
getTagR :: TagId -> Handler Html
getTagR tagId = do
tag <- runDB $ get404 tagId
defaultLayout $ do
setTitle $ toHtml $ tagName tag
$(widgetFile "tag")
This is where the test fails.
yit "Get a tag" $ do
-- tagId is undefined
get $ TagR tagId
statusIs 200
I am not sure how to define the tagId. It wouldn't work with a String or Text or Num, and I can't seem to figure out how to generate one as I can't find any example code in various Data.Persist tutorials. Or better yet, some other way to call the get method.
You want to use the Key data constructor to construct an ID value, which takes a PersistValue as a parameter. A simple example of creating one is:
Key $ PersistInt64 5
Another option is to call get with a textual URL, e.g. get ("/tag/5" :: Text).
Since times have changed, I'll leave this note here to say that these days one would use something like:
fromBackendKey 5
See the docs for fromBackendKey.
In Yesod, I've got a form that populates the type
data Field = Field Text Text text
deriving Show
When I write the hamlet html to display it, I'm running into the problem that Field is wrapped up in Maybe Maybe Field. So in hamlet I'm trying to do the following as shown here
(Snippet in postHomeR function)
let fieldData = case result of
FormSuccess res -> Just res
_ -> Nothing
(In the hamlet file)
<ul>
$maybe (Field one two three) <- fieldData
<li>#{show one}
However, when compiling there is Not in scope: one error. Why is the variable one not created/populated correctly?
You need to indent the <li> so that it's inside the $maybe block. As it sits now, it's a sibling to the $maybe, and thus the variables bound by $maybe are not in scope.