Haskell framework for the web

Speaking in Tongues

Yesod Ecosystem

The listings that follow are excerpts from the project folder, which Stack generates as soon as you call:

stack new <My project>

The original project essentially comprises the pages shown in a login and link to Yesod documents (Figure 1), the possibility to upload files (Figure 4), and built-in support (Figure 5). Also, the home page includes a brief introduction to the framework.

Figure 5: In the last part of the homepage, users either leave a comment or learn more about testing software under Yesod.

The login overrides the sample project with the option to register with Google and Yahoo (Figure 3) and sets the maximum number of uploadable files to one (Figure 4). For a quick start in Yesod, you still need to know which information belongs where. Table 1 provides an overview of the most important files.

Table 1

<My project> Folder

Folder/File Name Content
Application.hs Initialize application, register handler
Foundation.hs Configure plugins
<Project name>.cabal Register extensions, packages, and handler
stack.yaml Packages, GHC
config/settings.yml Global variables
config/favicon.ico Icon
config/models Database schema
config/routes Routes
Handler/handler.hs Handler
static/ CSS, JavaScript, images, fonts
templates/ Templates in Hamlet, Julius, Cassius, or Lucius
Figure 3: This login window appears when you click Login on the homepage of the project.
Figure 4: A variable now indicates the maximum number of uploadable files in the form of the slightly altered website.

Now you can edit the files and define an individual icon for the URL bar; you can replace the config/favicon.ico file with an original composition of the same name. You can modify the database schema in the config/model file, as necessary. Changes affect the current system once you save them – you do not need to restart the web application – and applies to changes to other files in the project.

In the config/routes file, you determine the routes (i.e., the URLs) that users use to reach the individual websites. To do so, you can store POST, GET, and other methods for each route in the same file.

Yesod also requires one handler per route. The handler determines what will happen as soon as you call a route. It is best to create a new handler in the handler folder, where you should make sure to use the typical Haskell .hs file suffix. Last but not least, you need to define the name of the domain in which the Yesod framework runs in the config/settings.yml file below the AppRoot keyword.

The static subfolder includes JavaScript files, style sheets, fonts, and images. It is worth mentioning in this context that the project includes the widespread Bootstrap CSS framework [15]. The static folder only contains pure CSS or JavaScript files here, because Yesod has its own template language, and the files created with it are usually in the templates directory.

The <My project>/test subfolder contains sample code that you use to put the web application through its paces. Additionally, several interesting files reside in the main folder of the project. Yesod initializes global variables or data types in the Application.hs (Listing 5) file, and the handler uses the getYesod function to access their values (Listing 6).

Listing 5

<My project>/Application.hs

01 [...]
02 import Model.MySettings
03 [...]
04 makeFoundation :: AppSettings -> IO App
05 makeFoundation appSettings = do
06     [...]
07 - maximum number of uploadable files
08     let maxFiles = 5
09     mysettings' <- newSettings maxFiles
10 -- make "myMax" version available globally
11         let mkFoundation appConnPool = App mysettings'
12 [...]

Listing 6

<My project>/handler/Home.hs (Extract)

01 [...]
02 module Handler.Home where
03 [...]
04 -- handler that created the www.domainname page
05 getHomeR :: Handler Html
06 getHomeR = do
07 [...]
08 - access to global variables with getYesod
09         (App{mySettings=s}) <- getYesod
10      let (M.MySettings {M.maxFiles=m }) = s
11 [...]

For example, if a website user can upload a maximum of five images per ticket, you would import a separate file (Listing 7), in which you have set a data type and a function that initializes the variable.

Listing 7

<My project>/Model/MySettings.hs

01 module Model.MySettings
02     (
03           MySettings(..)
04         , newSettings
05     )where
06 import [...]
07 -- type synonym
08 type Max = Int
09 --Datentyp MySettings
10 data MySettings = MySettings
11     { myMax         :: Max }
12 --this function determines the maximum number of uploadable files
13 newSettings num = do
14   return MySettings { myMax = num }

You then declare a local variable in the makeFoundation function in Application.hs (Listing 5): The variable contains the maximum number of uploadable files and passes this variable in to the imported function, which initializes the variable. Additionally, you need to define which data types belong to the web application named App (Listing 8).

Listing 8

<My project>/Foundation.hs (Extract)

01 [...]
02 import Model.MySettings
03 [...]
04 --Name the of the web application = App
05 data App = App
06 - In the app, only the variables of the data type MySettings appear.
07 - Under Java developers could compare MySettings with a class,
08     --which has the myApp variable. This is from MySettings type.
09     {mySettings    :: MySettings }
10 [...]

To Be or Not To Be

Shakespeare provides the name of the template language that Yesod uses to generate HTML files from a template: Hamlet. If you know HTML, you will have no trouble familiarizing yourself with Hamlet, which supports if-then queries, case distinctions (using case and Maybe), and loops.

If necessary, you can overwrite the settings in the global style sheet with the use of two style sheet languages: Cassius and Lucius. Whereas Lucius, like CSS, requires brackets when defining individual selectors and a semicolon at the end of a statement, Cassius gets by without this overhead. However, with Cassius, you do need to indent the definition of a selector.

Unlike CSS, Lucius and Cassius can define variables, saving lines of code. Using Julius, you can create JavaScript files that access the existing variables in the project, such as route names. You can statically integrate existing CSS style sheets or JavaScript files without having to convert them to another format. In Listing 9, Yesod loads the static/css/styling.css style sheet when the user calls a route of their choice.

Listing 9

<My project>/Foundation.hs (Extract)

01 [...]
02 - defines the default layout, which applies to each page of a given domain
03 pc <- widgetToPageContent $ do
04       addStylesheet $ StaticR css_styling_css
05 - the standard layout tempate is in the "default-layout.hamlet" file
06       $(widgetFile "default-layout")
07     withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
08 [...]

However, it makes sense to use the template languages for new files. For example, if you want the handler that implements the homepage to integrate both HTML code in a Hamlet file and an appropriate style sheet and Julius file on top, you should ensure that the names of these files are all the same (e.g., homepage.hamlet, homepage.julius, and homepage.lucius). In this way, you only need to include a file in the handler once and then reference homepage (Listing 10) in the handler. Yesod then automatically loads the templates.

Listing 10

<My project>/handler/Home.hs (Extract)

01 [...]
02 module Handler.Home where
03 [...]
04 - handler that generates the home page of a domain
05 - In the homepage.hamlet file you will find the template for the home page
06 getHomeR :: Handler Html
07 getHomeR = defaultLayout $(widgetFile "homepage")
08 [...]

Guaranteeing Security

If a user contacts the webmaster via a contact form, you can generate the necessary forms under Yesod without having to write multiple lines of code. Two different methods give you the same results. If you just want to get started, type A forms are suitable. You don't need an additional Hamlet file for them; you just need to specify which HTML fields the form will comprise, as in the contactAForm function (Listing 11).

Listing 11

contact-v2.hs

01 [...]
02 data ContactForm = ContactForm {
03      contactName ::  Text
04     ,contactEmail :: Text
05     ,contactText :: Textarea
06 } deriving Show
07
08 - generates a type A form
09 contactAForm :: AForm Handler ContactForm
10 contactAForm = ContactForm
11     <$> areq textField "Name"  Nothing
12     <*> areq textField  "Email"   (Just "info@example.com")
13     <*> areq textareaField "Text"  Nothing
14
15 contactForm :: Html -> MForm Handler (FormResult ContactForm, Widget)
16 contactForm = renderBootstrap2 contactAForm
17
18 getContactR = do
19    (widget, enctype) <- generateFormPost $  contactForm
20    page enctype widget
21 [...]

You can define the appearance of monadic M forms, mentioned at the top of the article, yourself (Figure 2), but this does mean you add the previously defined HTML input elements to the Hamlet section. You also add classes or IDs to the Hamlet files that you previously defined in Lucius or Cassius (Listing 4).

Yesod guarantees security with the use of security tokens, which are stored in a hidden HTML input field. These tokens prevent cross-site request forgery attacks. You only need to enter the #{extra} variable in the Hamlet section for M forms. Yesod automatically adds the extra variable for A forms.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Lua for Apache

    Lua is a small, lean, and fast scripting language – ideal for working with web servers. Version 2.4 of the Apache web server is the first to offer a matching module that has a few quirks – and pitfalls, if you dig more deeply.

  • How to Hide a Malicious File

    The best way to stop an attack is to think like an attacker. We’ll show you how to use the Metasploit framework to create a malicious payload that escapes antivirus detection.

  • A DIY HTML Engine
    Jekyll is a lightweight, fast, HTML engine that renders websites with ease, with the added benefits of low cost, high speed, security, and free hosting with GitHub Pages.
  • Slipping your pen test past antivirus protection with Veil-Evasion
    The Veil pen-testing platform provides some powerful tools that will hide your attack from antivirus scanners – and Veil even supports Metasploit payloads.
  • Introduction to HDF5

    HDF5 is a data model, library, and file format used primarily for scientific computing to store and manage data.

comments powered by Disqus