1
1
mirror of https://github.com/qfpl/applied-fp-course.git synced 2024-11-22 02:55:57 +03:00

Full Course Layout Change with a shuffle. (#54)

* Restructure entire course into a single library with multiple executable definitions. Makes working through the course a bit easier with respect to sandboxes, nix shells, and stack caching. Should be a side benefit of giving editor tooling a bit of an easier time. Lowers switch time between course levels.

* Readme updates to suit new structure.

Add comment to cabal file.

* Syntax error in Travis yml file.

Not sure where yet.

* Applied some linting suggestions to travis file

* Fixed missing comments in travis yml, removed duplicate executable entry in the cabal file

* Added changelog file, vcs entry to cabal file

* Fixing travis file, WIP.

* Still trying

* Added cabal.project file

* Still trying (Travis)

* Moved var declaration to higher level in travis.yml file.

* Forgo the complexity of hvrs script and go low-tech.

* Remove Haddocks from stack build.

Add extra-deps because what was a working stack build is now failing for some
reason, despite having the same version bounds and using the same LTS as before.

* Added more deps to extra-deps to appease the stackbeast.

* Updated base README

* Readme tweaks, level 03 test updates

* Moved Level07 to Level05, bumped other levels accordingly.

* Moved 'Main' to 'Core'.

Updated READMEs, first pass.

Updated Cabal file.

Updated Tests to handle change of Main to Core.

* Proof read #3616

Lots of small fixes for file paths, wording, some additional exercises. Fixed up
the tests to be more consistent as they've been reordered a few times and mostly
left to rot.

Readmes updated where needed.

New exercise added to level07 for using the general `ExceptT` type.

* Fix up some tests and avoid any more dynamic linking errors, I think

* Fix up cabal config for doctests, missing packages and some exposed-modules

* Removed use of Semigroup in tests to avoid CPP in cabal file for including semigroups package. Added bounds to base dependency for doctests test-suite
This commit is contained in:
Sean Chalmers 2018-05-08 09:02:51 +10:00 committed by GitHub
parent 10612052be
commit dd907523ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
157 changed files with 1500 additions and 3410 deletions

View File

@ -1,61 +1,56 @@
# This is the complex Travis configuration, which is intended for use
# on open source libraries which need compatibility across multiple GHC
# versions, must work with cabal-install, and should be
# cross-platform. For more information and other options, see:
# This Travis job script has been generated by a script via
#
# https://docs.haskellstack.org/en/stable/travis_ci/
# runghc make_travis_yml_2.hs 'applied-fp-course.cabal'
#
# Copy these contents into the root directory of your Github project in a file
# named .travis.yml
# Use new container infrastructure to enable caching
# For more information, see https://github.com/hvr/multi-ghc-travis
#
language: c
sudo: false
# Do not choose a language; we provide our own build tools.
language: generic
git:
submodules: false # whether to recursively clone submodules
# Caching so the next build will be fast too.
cache:
directories:
- $HOME/.ghc
- $HOME/.cabal
- $HOME/.stack
- $HOME/.cabal/packages
- $HOME/.cabal/store
- $HOME/.ghc
- $HOME/.stack
before_cache:
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
# remove files that are regenerated by 'cabal update'
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
- rm -rfv $HOME/.cabal/packages/head.hackage
# The different configurations we want to test. We have BUILD=cabal which uses
# cabal-install, and BUILD=stack which uses Stack. More documentation on each
# of those below.
#
# We set the compiler values here to tell Travis to use a different
# cache file per set of arguments.
#
# If you need to have different apt packages for each combination in the
# matrix, you can use a line such as:
# addons: {apt: {packages: [libfcgi-dev,libgmp-dev]}}
matrix:
include:
# We grab the appropriate GHC and cabal-install versions from hvr's PPA. See:
# https://github.com/hvr/multi-ghc-travis
- env: BUILD=cabal GHCVER=7.10.3 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 7.10.3"
addons: {apt: {packages: [cabal-install-2.0,ghc-7.10.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
- env: BUILD=cabal GHCVER=8.0.2 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 8.0.2"
addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
- env: BUILD=cabal GHCVER=8.2.2 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 8.2.2"
addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
- env: BUILD=cabal GHCVER=8.4.1 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 8.4.1"
addons: {apt: {packages: [cabal-install-2.0,ghc-8.4.1,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
- env: BUILD=cabal
compiler: "ghc-7.10.3"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.10.3], sources: [hvr-ghc]}}
# Build with the newest GHC and cabal-install. This is an accepted failure,
# see below.
- env: BUILD=cabal GHCVER=head CABALVER=head HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC HEAD"
addons: {apt: {packages: [cabal-install-head,ghc-head,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
- env: BUILD=cabal
compiler: "ghc-8.0.2"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
# The Stack builds. We can pass in arbitrary Stack arguments via the ARGS
# variable, such as using --stack-yaml to point to a different file.
- env: BUILD=cabal
compiler: "ghc-8.2.2"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
- env: BUILD=cabal
compiler: "ghc-8.4.1"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}
# The Stack builds. We can pass in arbitrary Stack arguments via the ARGS
# variable, such as using --stack-yaml to point to a different file.
- env: BUILD=stack ARGS=""
compiler: ": #stack default"
addons: {apt: {packages: [libgmp-dev]}}
@ -100,102 +95,63 @@ matrix:
os: osx
allow_failures:
- env: BUILD=cabal GHCVER=head CABALVER=head HAPPYVER=1.19.5 ALEXVER=3.1.7
#- env: BUILD=stack ARGS="--resolver lts-6"
- env: BUILD=stack ARGS="--resolver nightly"
before_install:
# Using compiler above sets CC to an invalid value, so unset it
- unset CC
# Store the list of levels
- LEVELS=$(ls -1d level*)
# We want to always allow newer versions of packages when building on GHC HEAD
- CABALARGS=""
- if [ "x$GHCVER" = "xhead" ]; then CABALARGS=--allow-newer; fi
# Download and unpack the stack executable
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.local/bin:/opt/alex/$ALEXVER/bin:/opt/happy/$HAPPYVER/bin:$HOME/.cabal/bin:$PATH
- mkdir -p ~/.local/bin
- |
if [ `uname` = "Darwin" ]
then
travis_retry curl --insecure -L https://www.stackage.org/stack/osx-x86_64 | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin
else
travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
fi
# Use the more reliable S3 mirror of Hackage
mkdir -p $HOME/.cabal
echo 'remote-repo: hackage.haskell.org:http://hackage.fpcomplete.com/' > $HOME/.cabal/config
echo 'remote-repo-cache: $HOME/.cabal/packages' >> $HOME/.cabal/config
if [ "$CABALVER" != "1.16" ]
then
echo 'jobs: $ncpus' >> $HOME/.cabal/config
fi
- HC=${CC}
- HCPKG=${HC/ghc/ghc-pkg}
- unset CC
- ROOTDIR=$(pwd)
- mkdir -p $HOME/.local/bin
- "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
- HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
- echo $HCNUMVER
# Download and unpack the stack executable
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.local/bin:/opt/alex/$ALEXVER/bin:/opt/happy/$HAPPYVER/bin:$HOME/.cabal/bin:$PATH
- mkdir -p ~/.local/bin
- |
if [ `uname` = "Darwin" ]
then
travis_retry curl --insecure -L https://www.stackage.org/stack/osx-x86_64 | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin
else
travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
fi
install:
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
- if [ -f configure.ac ]; then autoreconf -i; fi
- |
set -x
case "$BUILD" in
stack)
# Add in extra-deps for older snapshots, as necessary
stack --no-terminal --install-ghc $ARGS build --bench --dry-run || ( \
stack --no-terminal $ARGS build cabal-install && \
stack --no-terminal $ARGS solver --update-config)
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
- BENCH=${BENCH---enable-benchmarks}
- TEST=${TEST---enable-tests}
- HADDOCK=${HADDOCK-true}
- INSTALLED=${INSTALLED-true}
- GHCHEAD=${GHCHEAD-false}
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
- |
case "$BUILD" in
stack)
# Add in extra-deps for older snapshots, as necessary
stack --no-terminal --install-ghc $ARGS build --bench --dry-run || ( \
stack --no-terminal $ARGS build cabal-install && \
stack --no-terminal $ARGS solver --update-config)
# Build the dependencies
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies
;;
cabal)
cabal --version
travis_retry cabal update
# Get the list of packages from the stack.yaml file. Note that
# this will also implicitly run hpack as necessary to generate
# the .cabal files needed by cabal-install.
PACKAGES=$(stack --install-ghc query locals | grep '^ *path' | sed 's@^ *path:@@')
;;
esac
set +ex
# Build the dependencies
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies
;;
cabal)
cabal --version
travis_retry cabal update -v
;;
esac
# Here starts the actual work to be performed for the package under test;
# any command which exits with a non-zero exit code causes the build to fail.
script:
- |
set -ex
case "$BUILD" in
stack)
stack --no-terminal $ARGS build --no-run-benchmarks --haddock --no-haddock-deps
;;
cabal)
# Build with stack
- if [[ "$BUILD" == "stack" ]]; then stack --no-terminal $ARGS build --no-run-benchmarks; fi
# Build with cabal, using individual commands so we can see which part failed.
- if [[ "$BUILD" == "cabal" ]]; then cabal new-configure -w ${HC} --enable-tests --ghc-options -O0; fi
- if [[ "$BUILD" == "cabal" ]]; then cabal new-build -w ${HC} all; fi
- if [[ "$BUILD" == "cabal" ]]; then rm -rf ./dist-newstyle; fi
ORIGDIR=$(pwd)
if [ $CABALVER != "2.0" ]
then
cabal sandbox init
for dir in $PACKAGES
do
cd $dir
echo "Processing $dir"
travis_retry cabal update
PKGVER=$(cabal info . | awk '{print $2;exit}')
cabal install --only-dependencies --enable-tests
cabal configure --enable-tests --ghc-options -O0
cabal build
cd $ORIGDIR
done
else
cabal new-configure --project-file="cabal.project" --enable-tests --ghc-options -O0
cabal new-build --project-file="cabal.project" all
fi
;;
esac
set +ex
# REGENDATA ["applied-fp-course.cabal"]
# EOF

View File

@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/qfpl/applied-fp-course.svg?branch=master)](https://travis-ci.org/qfpl/applied-fp-course)
<img src="https://i.imgur.com/0h9dFhl.png" height="400" width="640" />
<img src="https://i.imgur.com/0h9dFhl.png" height="200" width="320" />
This is a brand new course, so there are going to be rough edges. We invite you to submit issues or
pull requests if you find errors or have suggestions on how to improve it.
@ -21,6 +21,7 @@ IRC on [Freenode](https://freenode.net/) in #qfpl or #fp-course.
* Have a few months self-study to your name.
* Want to know how to build larger applications with statically typed FP.
* Are willing to accept that a web application is a sufficient choice.
* Can write the canonical function of type: ``Applicative f => [f a] -> f [a]``
### We:
@ -39,30 +40,45 @@ IRC on [Freenode](https://freenode.net/) in #qfpl or #fp-course.
### Setup build tools:
Each level is a self-contained Haskell application, containing incomplete, or as
yet undefined, data types and functions. There is a Cabal and Nix file for each
level, so you can use either cabal sandboxes or a ``nix-shell``, depending on
your preference.
Each level is a self-contained Haskell module, containing incomplete, or as yet
undefined, data types and functions.
We recommend using either a cabal sandbox, or a ``nix-shell``, depending on your
preference.
To use a sandbox:
```bash
$ cd <levelN>
$ cd path/to/applied-fp-course
$ cabal sandbox init
$ cabal install --only-dependencies
$ cabal install --only-dependencies --enable-tests
$ cabal build
$ $EDITOR README.md
```
The normal cabal build commands should then work as expected. We do recommend
using cabal sandboxes as they provide a contained Haskell environment for a
given project. Easy to clean up, and package versions won't conflict with any
other sandboxed project you may be working on.
We do recommend using cabal sandboxes as they provide a contained Haskell
environment for a given project. Easy to clean up, and package versions won't
conflict with any other sandboxed project you may be working on.
If you're using a version of Cabal that is >=2.0 (use ``cabal --version`` to
find out), then you can use the ``new-*`` commands and you don't need a sandbox:
```bash
$ cd path/to/applied-fp-course
$ cabal new-configure --enable-tests
$ cabal new-build <levelN>-exe
$ $EDITOR src/<LevelN>/README.md
```
The normal cabal build commands should then work as expected.
To use the Nix Shell:
```bash
$ cd <levelN>
$ cd path/to/applied-fp-course
$ nix-shell
$ cabal build
$ $EDITOR README.md
$ cabal new-build <levelN>-exe
$ $EDITOR src/<LevelN>/README.md
```
Once that completes you will be in a ``nix-shell`` environment with all the
tools required to build the application for that level. Note that the
@ -70,7 +86,13 @@ levels build on each other, so you can go to the highest level and enter a
nix-shell there, you will then have all the required tools for every level.
The ``shell.nix`` is not provided, so if you have a different work-flow you can
utilise the derivation from the respective ``levelN.nix``.
utilise the derivation from the ``applied-fp-course.nix``.
##### Stack
Stack yaml configuration is provided and checked by our CI system for successful
builds. However the authors do not use stack, so we cannot promise to be able to
resolve stack related issues that may arise. Though we will do our best. :)
##### Please note...
@ -83,16 +105,16 @@ free [WebChat client](https://webchat.freenode.net).
#### Subsequent lessons may contain spoilers, don't cheat yourself out of the experience!
There is a ``README.md`` file in each Level project that will provide instructions about
what the goal is for that specific level.
There is a ``README.md`` file in each Level module folder that will provide
instructions about what the goal is for that specific level.
* Level 01 : Simple Hello World web app.
* Level 02 : Define our application spec with types!
* Level 03 : Testing & Tools (hspec & ghcid)
* Level 04 : Database layer (sqlite-simple)
* Level 05 : Add some flexible configuration
* Level 06 : ReaderT & Refactoring
* Level 07 : ExceptT & Refactoring
* Level 05 : Better Error Handling Through ExceptT
* Level 06 : Add some flexible configuration
* Level 07 : ReaderT & Refactoring
-- Coming Soon...
* Level 08 : (Bonus Round) Lenses & Refactoring
@ -101,4 +123,3 @@ what the goal is for that specific level.
* Level 09 : Add session controls (login, logout) and a protected route. So we
can have something that resembles application state. For the purposes of
modelling the state machine and implementing some property based tests.

253
applied-fp-course.cabal Normal file
View File

@ -0,0 +1,253 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: applied-fp-course
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps for educational purposes.
-- A longer description of the package.
description: Haskell course for people looking to start building larger applications.
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a README.
extra-source-files: changelog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.4.1
, GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
source-repository head
type: git
location: https://github.com/qfpl/applied-fp-course
library
-- Modules included in this executable, other than Main.
exposed-modules:
Level01.Core
, Level02.Core
, Level02.Types
, Level03.Core
, Level03.Types
, Level04.Conf
, Level04.DB
, Level04.DB.Types
, Level04.Core
, Level04.Types
, Level04.Types.CommentText
, Level04.Types.Error
, Level04.Types.Topic
, Level05.AppM
, Level05.Conf
, Level05.DB
, Level05.DB.Types
, Level05.Core
, Level05.Types
, Level05.Types.CommentText
, Level05.Types.Error
, Level05.Types.Topic
, Level06.AppM
, Level06.Conf
, Level06.Conf.CommandLine
, Level06.Conf.File
, Level06.DB
, Level06.DB.Types
, Level06.Core
, Level06.Types
, Level06.Types.CommentText
, Level06.Types.Error
, Level06.Types.Topic
, Level07.AppM
, Level07.Conf
, Level07.Conf.CommandLine
, Level07.Conf.File
, Level07.DB
, Level07.DB.Types
, Level07.Core
, Level07.Responses
, Level07.Types
, Level07.Types.CommentText
, Level07.Types.Error
, Level07.Types.Topic
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
, optparse-applicative >= 0.13 && < 0.15
, aeson == 1.*
, mtl == 2.2.*
, time >= 1.4 && < 1.10
, sqlite-simple == 0.4.*
, sqlite-simple-errors == 0.6.*
, semigroups == 0.18.*
, transformers >= 0.4 && < 0.6
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
test-suite app-fp-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
other-modules: Level03Tests
, Level04Tests
, Level05Tests
, Level06Tests
, Level07Tests
build-depends: base >= 4.8 && <4.12
, applied-fp-course
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*
, text == 1.2.*
, mtl == 2.2.*
test-suite doctests
-- Base language which the package is written in.
default-language: Haskell2010
type: exitcode-stdio-1.0
other-modules: Level04Tests
, Level05Tests
, Level06Tests
, Level07Tests
ghc-options: -threaded
main-is: doctests.hs
hs-source-dirs: tests
build-depends: base >= 4.8 && <4.12
, applied-fp-course
, mtl == 2.2.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, doctest >= 0.11 && < 0.16
-- Level Executables
executable level01-exe
-- .hs or .lhs file containing the Main module.
main-is: Level01.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level02-exe
-- .hs or .lhs file containing the Main module.
main-is: Level02.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level03-exe
-- .hs or .lhs file containing the Main module.
main-is: Level03.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level04-exe
-- .hs or .lhs file containing the Main module.
main-is: Level04.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level05-exe
-- .hs or .lhs file containing the Main module.
main-is: Level05.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level06-exe
-- .hs or .lhs file containing the Main module.
main-is: Level06.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010
executable level07-exe
-- .hs or .lhs file containing the Main module.
main-is: Level07.hs
-- Directories containing source files.
hs-source-dirs: exe
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, applied-fp-course
-- Base language which the package is written in.
default-language: Haskell2010

23
applied-fp-course.nix Normal file
View File

@ -0,0 +1,23 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, mtl, optparse-applicative, semigroups, sqlite-simple
, sqlite-simple-errors, stdenv, text, time, transformers, wai
, wai-extra, warp
}:
mkDerivation {
pname = "applied-fp-course";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types mtl optparse-applicative
semigroups sqlite-simple sqlite-simple-errors text time
transformers wai warp
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai mtl text wai wai-extra
];
description = "Simplest of web apps for educational purposes";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,132 +0,0 @@
# This Travis job script has been generated by a script via
#
# runghc make_travis_yml_2.hs 'cabal.project'
#
# For more information, see https://github.com/hvr/multi-ghc-travis
#
language: c
sudo: false
git:
submodules: false # whether to recursively clone submodules
cache:
directories:
- $HOME/.cabal/packages
- $HOME/.cabal/store
before_cache:
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
# remove files that are regenerated by 'cabal update'
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
- rm -rfv $HOME/.cabal/packages/head.hackage
matrix:
include:
- compiler: "ghc-7.10.3"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.10.3], sources: [hvr-ghc]}}
- compiler: "ghc-8.0.2"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
- compiler: "ghc-8.2.2"
# env: TEST=--disable-tests BENCH=--disable-benchmarks
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
before_install:
- HC=${CC}
- HCPKG=${HC/ghc/ghc-pkg}
- unset CC
- ROOTDIR=$(pwd)
- mkdir -p $HOME/.local/bin
- "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
- HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
- echo $HCNUMVER
install:
- cabal --version
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
- BENCH=${BENCH---enable-benchmarks}
- TEST=${TEST---enable-tests}
- HADDOCK=${HADDOCK-true}
- INSTALLED=${INSTALLED-true}
- GHCHEAD=${GHCHEAD-false}
- travis_retry cabal update -v
- "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
- rm -fv cabal.project cabal.project.local
- grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
- "printf 'packages: \"level01\" \"level02\" \"level03\" \"level04\" \"level05\" \"level06\" \"level07\"\\n' > cabal.project"
- cat cabal.project
- if [ -f "level01/configure.ac" ]; then
(cd "level01" && autoreconf -i);
fi
- if [ -f "level02/configure.ac" ]; then
(cd "level02" && autoreconf -i);
fi
- if [ -f "level03/configure.ac" ]; then
(cd "level03" && autoreconf -i);
fi
- if [ -f "level04/configure.ac" ]; then
(cd "level04" && autoreconf -i);
fi
- if [ -f "level05/configure.ac" ]; then
(cd "level05" && autoreconf -i);
fi
- if [ -f "level06/configure.ac" ]; then
(cd "level06" && autoreconf -i);
fi
- if [ -f "level07/configure.ac" ]; then
(cd "level07" && autoreconf -i);
fi
- rm -f cabal.project.freeze
- cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
- cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
- rm -rf "level01"/.ghc.environment.* "level02"/.ghc.environment.* "level03"/.ghc.environment.* "level04"/.ghc.environment.* "level05"/.ghc.environment.* "level06"/.ghc.environment.* "level07"/.ghc.environment.* "level01"/dist "level02"/dist "level03"/dist "level04"/dist "level05"/dist "level06"/dist "level07"/dist
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
# Here starts the actual work to be performed for the package under test;
# any command which exits with a non-zero exit code causes the build to fail.
script:
# test that source-distributions can be generated
# - (cd "level01" && cabal sdist)
# - (cd "level02" && cabal sdist)
# - (cd "level03" && cabal sdist)
# - (cd "level04" && cabal sdist)
# - (cd "level05" && cabal sdist)
# - (cd "level06" && cabal sdist)
# - (cd "level07" && cabal sdist)
# - mv "level01"/dist/level01-*.tar.gz "level02"/dist/level02-*.tar.gz "level03"/dist/level03-*.tar.gz "level04"/dist/level04-*.tar.gz "level05"/dist/level05-*.tar.gz "level06"/dist/level06-*.tar.gz "level07"/dist/level07-*.tar.gz ${DISTDIR}/
# - cd ${DISTDIR} || false
# - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
- "printf 'packages: level01/level01.cabal level02/level02.cabal level03/level03.cabal level04/level04.cabal level05/level05.cabal level06/level06.cabal level07/level07.cabal\\n' > cabal.project"
- cat cabal.project
# this builds all libraries and executables (without tests/benchmarks)
- cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
# Build with installed constraints for packages in global-db
- if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi
# build & run tests, build benchmarks
- cabal new-build -w ${HC} ${TEST} ${BENCH} all
# - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
# cabal check
# - (cd level01 && cabal check)
# - (cd level02 && cabal check)
# - (cd level03 && cabal check)
# - (cd level04 && cabal check)
# - (cd level05 && cabal check)
# - (cd level06 && cabal check)
# - (cd level07 && cabal check)
# haddock
- rm -rf ./dist-newstyle
- if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
# REGENDATA ["cabal.project"]
# EOF

View File

@ -1,8 +1 @@
packages:
level01/
level02/
level03/
level04/
level05/
level06/
level07/
packages: .

0
changelog.md Normal file
View File

View File

@ -1,5 +1,6 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
{ nixpkgs ? import <nixpkgs> {}
, compiler ? "default"
}:
let
inherit (nixpkgs) pkgs;
@ -7,7 +8,7 @@ let
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level04.nix {};
drv = haskellPackages.callPackage ./applied-fp-course.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

9
exe/Level01.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level01.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Main.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level02.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level02.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Main.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level03.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level03.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Main.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level04.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level04.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Level04.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level05.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level05.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Level05.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level06.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level06.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Main.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

9
exe/Level07.hs Normal file
View File

@ -0,0 +1,9 @@
module Main where
import qualified Level07.Core as Core
-- Our application will be built as a library that will be included in an
-- executable. So our ``exe/Main.hs`` is a straightforward and unremarkable
-- affair.
main :: IO ()
main = Core.runApp

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,9 +0,0 @@
module Main where
import qualified FirstApp.Main as Main
-- Our application will be built as a library that will be included in an
-- executable. So our ``bin/Main.hs`` is a straightforward and unremarkable
-- affair. We won't be updating this file.
main :: IO ()
main = Main.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level01
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level01.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,93 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level01
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
exposed-modules: FirstApp.Main
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level01-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level01
-- Directories containing source files.
hs-source-dirs: bin
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,13 +0,0 @@
{ mkDerivation, stdenv, base, wai, warp, http-types }:
mkDerivation {
pname = "level01";
src = ./.;
version = "0.1.0.0";
sha256 = "0";
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [ base wai warp http-types ];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as Main
main :: IO ()
main = Main.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level02
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level02.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,97 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level02
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
exposed-modules: FirstApp.Types
, FirstApp.Main
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-fwarn-missing-import-lists
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level02-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level02
-- Directories containing source files.
hs-source-dirs: bin
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,13 +0,0 @@
{ mkDerivation, base, wai, warp, http-types, stdenv }:
mkDerivation {
pname = "level02";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [
base wai warp http-types
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as M
main :: IO ()
main = M.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level03
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level03.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,113 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level03
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
-- Modules included in this executable, other than Main.
exposed-modules: FirstApp.Types
, FirstApp.Main
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-fwarn-missing-import-lists
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level03-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level03
-- Directories containing source files.
hs-source-dirs: bin
-- Base language which the package is written in.
default-language: Haskell2010
-- This is the declaration of a test-suite for your application. You may have
-- multiple test suites in a single application, provided they are named
-- differently.
test-suite level03-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
build-depends: base >= 4.8 && <4.12
, level03
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*

View File

@ -1,20 +0,0 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, optparse-applicative, stdenv, text, wai, wai-extra
, warp
}:
mkDerivation {
pname = "level03";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types optparse-applicative text wai warp
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai wai wai-extra
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,9 +0,0 @@
module Main where
import Test.DocTest (doctest)
main :: IO ()
main = doctest
[ "-isrc"
, "src/FirstApp/Conf.hs"
]

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as M
main :: IO ()
main = M.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level04
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,132 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level04
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
-- Modules included in this executable, other than Main.
exposed-modules: FirstApp.Conf
, FirstApp.DB
, FirstApp.DB.Types
, FirstApp.Main
, FirstApp.Types
, FirstApp.Types.CommentText
, FirstApp.Types.Error
, FirstApp.Types.Topic
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-fwarn-missing-import-lists
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
, optparse-applicative >= 0.13 && < 0.15
, aeson == 1.*
, mtl == 2.2.*
, time >= 1.4 && < 1.10
, sqlite-simple == 0.4.*
, sqlite-simple-errors == 0.6.*
, semigroups == 0.18.*
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level04-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Directories containing source files.
hs-source-dirs: bin
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level04
-- Base language which the package is written in.
default-language: Haskell2010
test-suite level04-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
build-depends: base >= 4.8 && <4.12
, level04
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*
test-suite doctests
-- Base language which the package is written in.
default-language: Haskell2010
type: exitcode-stdio-1.0
ghc-options: -threaded
main-is: doctests.hs
hs-source-dirs: tests
build-depends: base
, doctest >= 0.11 && < 0.16
, semigroups == 0.18.*

View File

@ -1,21 +0,0 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, mtl, optparse-applicative, sqlite-simple, semigroups
, sqlite-simple-errors, stdenv, text, time, wai, wai-extra, warp
}:
mkDerivation {
pname = "level04";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types mtl optparse-applicative
sqlite-simple sqlite-simple-errors text time wai warp semigroups
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai wai wai-extra
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1 +0,0 @@
{"foo":33}

View File

@ -1,11 +0,0 @@
module Main where
import Test.DocTest (doctest)
main :: IO ()
main = doctest
[ "-isrc"
, "src/FirstApp/Conf.hs"
, "src/FirstApp/DB.hs"
, "src/FirstApp/Types.hs"
]

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,33 +0,0 @@
# Level 05
In this exercise we build some configuration capabilities into our application.
This exercise will require a combination of building the right types to guide
your development, plus consulting plenty of documentation to leverage the chosen
packages. There may also be, depending on your level of interest, some external
reading for later as well.
The steps for this level:
1) ``src/FirstApp/Types.hs``
2) ``src/FirstApp/Conf/File.hs``
3) ``src/FirstApp/Conf.hs``
4) ``src/FirstApp/Main.hs``
The packages we will use for this are:
- [Aeson](http://hackage.haskell.org/package/aeson)
- [Optparse Applicative](http://hackage.haskell.org/package/optparse-applicative)
#### Aside: Tool Introduction - doctest
This level utilises the [doctest](https://hackage.haskell.org/package/doctest)
tool to help us ensure our functions comply with some quick tests that are
written as comments in the source file. This is a port of the same technology
that exists in Python.
You can see the new entry in the Cabal file as another ``test-suite``. The
``doctests.hs`` lists the files that have doctests that we want to run. The
``src/FirstApp/Conf/File.hs`` file contains some tests that you need to update
as part of the level.
For details on running and writing doctests, refer to the documentation.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,3 +0,0 @@
{
"dbFileName": "app_db.db"
}

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as Main
main :: IO ()
main = Main.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level05
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level05.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,126 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level05
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
exposed-modules: FirstApp.Conf
, FirstApp.Conf.CommandLine
, FirstApp.Conf.File
, FirstApp.DB
, FirstApp.DB.Types
, FirstApp.Main
, FirstApp.Types
, FirstApp.Types.CommentText
, FirstApp.Types.Error
, FirstApp.Types.Topic
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
, optparse-applicative >= 0.13 && < 0.15
, aeson == 1.*
, time >= 1.4 && < 1.10
, sqlite-simple == 0.4.*
, sqlite-simple-errors == 0.6.*
, semigroups == 0.18.*
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level05-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level05
-- Directories containing source files.
hs-source-dirs: bin
-- Base language which the package is written in.
default-language: Haskell2010
test-suite level05-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
build-depends: base >= 4.8 && <4.12
, level05
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*
test-suite doctests
-- Base language which the package is written in.
default-language: Haskell2010
type: exitcode-stdio-1.0
ghc-options: -threaded
main-is: doctests.hs
hs-source-dirs: tests
build-depends: base
, level05
, doctest >= 0.11 && < 0.16

View File

@ -1,21 +0,0 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, mtl, optparse-applicative, sqlite-simple, semigroups
, sqlite-simple-errors, stdenv, text, time, wai, wai-extra, warp
}:
mkDerivation {
pname = "level05";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types mtl optparse-applicative
sqlite-simple sqlite-simple-errors text time wai warp semigroups
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai wai wai-extra
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,3 +0,0 @@
{
"foo": 33
}

View File

@ -1,58 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad (join)
import Test.Hspec
import Test.Hspec.Wai
import qualified System.Exit as Exit
import qualified FirstApp.DB as DB
import qualified FirstApp.Main as Main
import qualified FirstApp.Types as Types
main :: IO ()
main = do
let dieWith m = print m >> Exit.exitFailure
reqsE <- Main.prepareAppReqs
case reqsE of
Left err -> dieWith err
Right ( cfg, db ) -> do
let app' = pure ( Main.app cfg db )
flushTopic =
-- Clean up and yell about our errors
fmap ( either dieWith pure . join ) .
-- Purge all of the comments for this topic for our tests
traverse ( DB.deleteTopic db )
-- We don't export the constructor so even for known values we have
-- to play by the rules. There is no - "Oh just this one time.", do it right.
$ Types.mkTopic "fudge"
-- Run the tests with a DB topic flush between each spec
hspec . with ( flushTopic >> app' ) $ do
-- AddRq Spec
describe "POST /topic/add" $ do
it "Should return 200 with well formed request" $ do
post "/fudge/add" "Fred" `shouldRespondWith` "Success"
it "Should 400 on empty input" $
post "/fudge/add" "" `shouldRespondWith` 400
-- ViewRq Spec
describe "GET /topic/view" $ do
it "Should return 200 with content" $ do
post "/fudge/add" "Is super tasty."
get "/fudge/view" `shouldRespondWith` 200
-- ListRq Spec
describe "GET /list" $ do
it "Should return 200 with content" $ do
post "/fudge/add" "Is super tasty."
get "/list" `shouldRespondWith` "[\"fudge\"]"

View File

@ -1,9 +0,0 @@
module Main where
import Test.DocTest (doctest)
main :: IO ()
main = doctest
[ "-isrc"
, "src/FirstApp/Conf.hs"
]

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as M
main :: IO ()
main = M.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level06
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level06.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,136 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level06
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
-- Modules included in this executable, other than Main.
exposed-modules: FirstApp.AppM
, FirstApp.Conf
, FirstApp.Conf.CommandLine
, FirstApp.Conf.File
, FirstApp.DB
, FirstApp.DB.Types
, FirstApp.Main
, FirstApp.Responses
, FirstApp.Types
, FirstApp.Types.CommentText
, FirstApp.Types.Error
, FirstApp.Types.Topic
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
, optparse-applicative >= 0.13 && < 0.15
, aeson == 1.*
, mtl == 2.2.*
, time >= 1.4 && < 1.10
, sqlite-simple == 0.4.*
, sqlite-simple-errors == 0.6.*
, semigroups == 0.18.*
, transformers >= 0.4 && < 0.6
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level06-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Directories containing source files.
hs-source-dirs: bin
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level06
-- Base language which the package is written in.
default-language: Haskell2010
test-suite level06-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
build-depends: base >= 4.8 && <4.12
, level06
, mtl == 2.2.*
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*
test-suite doctests
-- Base language which the package is written in.
default-language: Haskell2010
type: exitcode-stdio-1.0
ghc-options: -threaded
main-is: doctests.hs
hs-source-dirs: tests
build-depends: base
, doctest >= 0.11 && < 0.16

View File

@ -1,23 +0,0 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, mtl, optparse-applicative, sqlite-simple, semigroups
, sqlite-simple-errors, stdenv, text, time, wai, wai-extra, warp
, transformers
}:
mkDerivation {
pname = "level06";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types mtl optparse-applicative
sqlite-simple sqlite-simple-errors text time wai warp semigroups
transformers
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai wai wai-extra
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,57 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-missing-methods #-}
module FirstApp.Conf
( parseOptions
) where
import GHC.Word (Word16)
import Data.Bifunctor (first)
import Data.Monoid (Last (..), (<>))
import FirstApp.Types (Conf (..), ConfigError (..),
DBFilePath (DBFilePath),
PartialConf (..), Port (Port))
import FirstApp.Conf.CommandLine (commandLineParser)
import FirstApp.Conf.File (parseJSONConfigFile)
-- We have some sane defaults that we can always rely on, so define them using
-- our PartialConf.
defaultConf
:: PartialConf
defaultConf = PartialConf
(pure (Port 3000))
(pure (DBFilePath "app_db.db"))
-- We need something that will take our PartialConf and see if can finally build
-- a complete Conf record. Also we need to highlight any missing config values
-- by providing the relevant error.
makeConfig
:: PartialConf
-> Either ConfigError Conf
makeConfig pc = Conf
<$> lastToEither MissingPort pcPort
<*> lastToEither MissingDBFilePath pcDBFilePath
where
-- You don't need to provide type signatures for most functions in where/let
-- sections. Sometimes the compiler might need a bit of help, or you would
-- like to be explicit in your intentions.
lastToEither
:: ConfigError
-> (PartialConf -> Last b)
-> Either ConfigError b
lastToEither e g =
(maybe (Left e) Right . getLast . g) pc
-- This is the function we'll actually export for building our configuration.
-- Since it wraps all our efforts to read information from the command line, and
-- the file, before combining it all and returning the required information.
parseOptions
:: FilePath
-> IO (Either ConfigError Conf)
parseOptions fp =
let mkCfg cli file = makeConfig (defaultConf <> file <> cli)
in do
cli' <- commandLineParser
( >>= mkCfg cli' ) <$> parseJSONConfigFile fp

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1 +0,0 @@
{"foo":33}

View File

@ -1,89 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad.Reader (ask, reader)
import Control.Monad (join)
import Test.Hspec
import Test.Hspec.Wai
import qualified System.Exit as Exit
import FirstApp.AppM (Env)
import qualified FirstApp.AppM as AppM
import qualified FirstApp.DB as DB
import qualified FirstApp.Main as Main
import qualified FirstApp.Types as Types
main :: IO ()
main = do
let dieWith m = print m >> Exit.exitFailure
-- Keeping everything in sync with out larger application changes.
reqsE <- Main.prepareAppReqs
case reqsE of
Left err -> dieWith err
Right env -> do
let app' = pure ( Main.app env )
flushTopic :: IO ()
flushTopic = AppM.runAppM (do
r <- traverse DB.deleteTopic ( Types.mkTopic "fudge" )
either ( liftIO . dieWith ) pure $ join r
) env
-- We can't run the tests for our AppM in the same stage as our
-- application, because of the use of the 'with' function. As it expects
-- to be able to execute our tests by applying it to our 'Application'.
hspec $ appMTests env
-- Run the tests with a DB topic flush between each spec
hspec . with ( flushTopic >> app' ) $ do
-- AddRq Spec
describe "POST /topic/add" $ do
it "Should return 200 with well formed request" $
post "/fudge/add" "Fred" `shouldRespondWith` "Success"
it "Should 400 on empty input" $
post "/fudge/add" "" `shouldRespondWith` 400
-- ViewRq Spec
describe "GET /topic/view" $
it "Should return 200 with content" $ do
post "/fudge/add" "Is super tasty."
get "/fudge/view" `shouldRespondWith` 200
-- ListRq Spec
describe "GET /list" $
it "Should return 200 with content" $ do
post "/fudge/add" "Is super tasty."
get "/list" `shouldRespondWith` "[\"fudge\"]"
-- These tests ensure that our AppM will do we want it to, with respect to the
-- behaviour of 'ask', 'reader', and use in a Monad.
appMTests :: Env -> Spec
appMTests env = describe "AppM Tests" $ do
it "ask should retrieve the Env" $ do
r <- AppM.runAppM ask env
( AppM.envConfig r == AppM.envConfig env ) `shouldBe` True
it "reader should run a function on the Env" $ do
let getDBfilepath = Types.dbFilePath . AppM.envConfig
r <- AppM.runAppM ( reader getDBfilepath ) env
r `shouldBe` (getDBfilepath env)
it "should let us run IO functions" $ do
let fn = do
e <- ask
AppM.envLoggingFn e "In a test!"
r <- AppM.runAppM fn env
r `shouldBe` ()

View File

@ -1,11 +0,0 @@
module Main where
import Test.DocTest (doctest)
main :: IO ()
main = doctest
[ "-isrc"
, "src/FirstApp/Conf.hs"
, "src/FirstApp/DB.hs"
, "src/FirstApp/Types.hs"
]

View File

@ -1,31 +0,0 @@
Copyright (c) 2017, Commonwealth Scientific and Industrial Research Organisation
(CSIRO) ABN 41 687 119 230.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of QFPL nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,8 +0,0 @@
# Level 07
Handling those `Either` values everywhere is a bit awkward, this exercise
introduces another monad transformer, ``ExceptT``. As well as the concept of a
'transformer stack' and what benefits it can provide.
Start in ``src/FirstApp/AppM.hs``.

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
{
"port": 3000,
"helloMsg": "Functional Programming is neat.",
"tableName": "comments",
"dbName": "firstapp"
}

View File

@ -1,6 +0,0 @@
module Main where
import qualified FirstApp.Main as M
main :: IO ()
main = M.runApp

View File

@ -1,5 +0,0 @@
# Revision history for level07
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View File

@ -1,13 +0,0 @@
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default" }:
let
inherit (nixpkgs) pkgs;
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
drv = haskellPackages.callPackage ./level07.nix {};
in
if pkgs.lib.inNixShell then drv.env else drv

View File

@ -1,136 +0,0 @@
-- Initial level01.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: level07
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Simplest of web apps
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD3
-- The file containing the license text.
license-file: LICENCE
-- The package author(s).
author: QFPL @ Data61
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: sean.chalmers@data61.csiro.au
-- A copyright notice.
copyright: Copyright (C) 2017 Commonwealth Scientific and Industrial Research Organisation (CSIRO)
category: Education
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
tested-with: GHC==8.2.2
, GHC==8.0.2
, GHC==7.10.3
library
-- Modules included in this executable, other than Main.
exposed-modules: FirstApp.AppM
, FirstApp.Conf
, FirstApp.Conf.CommandLine
, FirstApp.Conf.File
, FirstApp.DB
, FirstApp.DB.Types
, FirstApp.Main
, FirstApp.Responses
, FirstApp.Types
, FirstApp.Types.CommentText
, FirstApp.Types.Error
, FirstApp.Types.Topic
ghc-options: -Wall
-fno-warn-unused-binds
-fno-warn-unused-do-bind
-fno-warn-unused-imports
-fno-warn-type-defaults
-ferror-spans
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, wai == 3.2.*
, warp == 3.2.*
, http-types >= 0.9 && < 0.13
, bytestring == 0.10.*
, text == 1.2.*
, optparse-applicative >= 0.13 && < 0.15
, aeson == 1.*
, mtl == 2.2.*
, time >= 1.4 && < 1.10
, sqlite-simple == 0.4.*
, sqlite-simple-errors == 0.6.*
, semigroups == 0.18.*
, transformers >= 0.4 && < 0.6
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable level07-exe
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Directories containing source files.
hs-source-dirs: bin
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.8 && <4.12
, level07
-- Base language which the package is written in.
default-language: Haskell2010
test-suite level07-tests
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: tests
main-is: Test.hs
build-depends: base >= 4.8 && <4.12
, level07
, wai == 3.2.*
, wai-extra == 3.0.*
, hspec >= 2.2 && < 3.0
, hspec-wai >= 0.6 && < 0.10
, bytestring == 0.10.*
, text == 1.2.*
, mtl == 2.2.*
test-suite doctests
-- Base language which the package is written in.
default-language: Haskell2010
type: exitcode-stdio-1.0
ghc-options: -threaded
main-is: doctests.hs
hs-source-dirs: tests
build-depends: base
, doctest >= 0.11 && < 0.16

View File

@ -1,23 +0,0 @@
{ mkDerivation, aeson, base, bytestring, doctest, hspec, hspec-wai
, http-types, mtl, optparse-applicative, sqlite-simple, semigroups
, sqlite-simple-errors, stdenv, text, time, wai, wai-extra, warp
, transformers
}:
mkDerivation {
pname = "level07";
version = "0.1.0.0";
src = ./.;
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
aeson base bytestring http-types mtl optparse-applicative
sqlite-simple sqlite-simple-errors text time wai warp semigroups
transformers
];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base bytestring doctest hspec hspec-wai text wai wai-extra
];
description = "Simplest of web apps";
license = stdenv.lib.licenses.bsd3;
}

View File

@ -1,61 +0,0 @@
module FirstApp.Conf.CommandLine
( commandLineParser
) where
import Data.Monoid (Last (Last), (<>))
import Options.Applicative (Parser, eitherReader, execParser,
fullDesc, header, help, helper, info,
long, metavar, option, optional, progDesc,
short, strOption)
import Text.Read (readEither)
import FirstApp.Types (DBFilePath (DBFilePath),
PartialConf (PartialConf), Port (Port))
-- | Command Line Parsing
-- This is an example of using the ``optparse-applicative`` package to build our command line
-- parser. As this particular problem is fraught with silly dangers and we appreciate someone else
-- having eaten this gremlin on our behalf.
commandLineParser
:: IO PartialConf
commandLineParser =
let mods = fullDesc
<> progDesc "Manage comments for something"
<> header "Your first Haskell app!"
in
execParser $ info (helper <*> partialConfParser) mods
-- Combine the smaller parsers into our larger ``PartialConf`` type.
partialConfParser
:: Parser PartialConf
partialConfParser =
PartialConf <$> portParser <*> dbFilePathParser
-- Parse the Port value off the command line args and into a Last wrapper.
portParser
:: Parser (Last Port)
portParser =
let
mods = long "port"
<> short 'p'
<> metavar "PORT"
<> help "TCP Port to accept requests on"
-- A custom parser to turn a String into a Word16, before putting it into a Port
portReader = eitherReader (fmap Port . readEither)
in
Last <$> optional (option portReader mods)
-- Parse the DBFilePath from the input string into our type and into a Last wrapper.
dbFilePathParser
:: Parser (Last DBFilePath)
dbFilePathParser =
let
mods = long "db-filepath"
<> short 'd'
<> metavar "DBFILEPATH"
<> help "File path for our SQLite Database file."
in
Last <$> optional (DBFilePath <$> strOption mods)

View File

@ -1,39 +0,0 @@
module FirstApp.Conf.File where
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.Bifunctor (first)
import Control.Exception (try)
import qualified Data.Aeson as Aeson
import FirstApp.Types (ConfigError (..), PartialConf)
-- Doctest setup section
-- $setup
-- >>> :set -XOverloadedStrings
-- | Update these tests when you've completed this function.
--
-- | readConfFile
-- >>> readConfFile "badFileName.no"
-- Left (ConfigFileReadError badFileName.no: openBinaryFile: does not exist (No such file or directory))
-- >>> readConfFile "test.json"
-- Right "{\"foo\":33}\n"
--
readConfFile
:: FilePath
-> IO ( Either ConfigError ByteString )
readConfFile fp =
first ConfigFileReadError <$> try (LBS.readFile fp)
-- Construct the function that will take a ``FilePath``, read it in, decode it,
-- and construct our ``PartialConf``.
parseJSONConfigFile
:: FilePath
-> IO ( Either ConfigError PartialConf )
parseJSONConfigFile fp =
(first JSONDecodeError . Aeson.eitherDecode =<<) <$> readConfFile fp

View File

@ -1,176 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module FirstApp.Main
( runApp
, prepareAppReqs
, app
) where
import Control.Monad.Except (ExceptT (ExceptT),
runExceptT)
import Control.Monad.IO.Class (liftIO)
import Network.Wai (Application, Request,
Response, pathInfo,
requestMethod,
strictRequestBody)
import Network.Wai.Handler.Warp (run)
import Data.Bifunctor (first)
import Data.Either (Either (Left, Right),
either)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Encoding (decodeUtf8)
import Data.Text.IO (hPutStrLn)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Database.SQLite.SimpleErrors.Types (SQLiteResponse)
import System.IO (stderr)
import qualified FirstApp.DB as DB
import qualified FirstApp.Conf as Conf
import qualified FirstApp.Responses as Res
import FirstApp.Types (Conf (..),
ConfigError (..),
Error (DBError, EmptyCommentText, EmptyTopic, UnknownRoute),
RqType (AddRq, ListRq, ViewRq),
confPortToWai,
mkCommentText, mkTopic)
import FirstApp.AppM (AppM,
Env (Env, envConfig, envDB),
runAppM, liftEither)
-- Our start-up process is becoming more complicated and could fail in new and
-- interesting ways. But we also want to be able to capture these errors in a
-- single type so that we can deal with the entire start-up process as a whole.
data StartUpError
= ConfErr ConfigError
| DbInitErr SQLiteResponse
deriving Show
runApp
:: IO ()
runApp = do
appE <- prepareAppReqs
either print runWithDbConn appE
where
runWithDbConn env =
appWithDb env >> DB.closeDB (envDB env)
appWithDb env =
run ( confPortToWai $ envConfig env ) (app env)
-- Monad transformers can be used without needing to write the newtype. The
-- constructor for ExceptT has a type of :: m (Either e a). So if you have
-- multiple functions that match that pattern and you don't want to have to
-- thread the error handling needle yourself. You can apply the constructor to
-- the functions and work directly on the values, knowing that the error
-- handling will work as expected. Then you `runExceptT` and produce the final
-- Either value.
prepareAppReqs
:: IO (Either StartUpError Env)
prepareAppReqs =
error "Copy your completed 'prepareAppReqs' and refactor to match the new type signature"
where
logToErr :: Text -> AppM ()
logToErr = liftIO . hPutStrLn stderr
toStartUpErr :: (a -> StartUpError) -> IO (Either a c) -> ExceptT StartUpError IO c
toStartUpErr = error "toStartUpErr not reimplemented"
-- Take our possibly failing configuration/db functions with their unique
-- error types and turn them into a consistently typed ExceptT. We can then
-- use them in a `do` block as if the Either isn't there. Extracting the
-- final result before returning.
initConf :: ExceptT StartUpError IO Conf
initConf = toStartUpErr ConfErr $ Conf.parseOptions "appconfig.json"
initDB :: Conf -> ExceptT StartUpError IO DB.FirstAppDB
initDB cfg = toStartUpErr DbInitErr $ DB.initDB (dbFilePath cfg)
app
:: Env
-> Application
app env rq cb = do
e <- requestToResponse
resp <- either handleError pure e
cb resp
where
logToErr :: Text -> IO ()
logToErr = liftIO . hPutStrLn stderr
requestToResponse :: IO (Either Error Response)
requestToResponse = runAppM ( mkRequest rq >>= handleRequest ) env
handleError :: Error -> IO Response
handleError e = mkErrorResponse e <$ ( logToErr . Text.pack . show ) e
-- This function has changed quite a bit since we changed our DB functions to be
-- part of AppM. We no longer have to deal with the extra layer of the returned
-- Either and these functions share the same Monad, AppM.
handleRequest
:: RqType
-> AppM Response
handleRequest ( AddRq t c ) =
-- We've cleaned this branch up a bit more by dropping our use of `const` as
-- we can use the Functor operator that ignores the result on the right hand
-- side and returns the result of the function on the left.
Res.resp200 "Success" <$ DB.addCommentToTopic t c
handleRequest ( ViewRq t ) =
Res.resp200Json <$> DB.getComments t
handleRequest ListRq =
Res.resp200Json <$> DB.getTopics
mkRequest
:: Request
-> AppM RqType
mkRequest rq =
liftEither =<< case ( pathInfo rq, requestMethod rq ) of
-- Commenting on a given topic
( [t, "add"], "POST" ) ->
liftIO $ mkAddRequest t <$> strictRequestBody rq
-- View the comments on a given topic
( [t, "view"], "GET" ) ->
pure ( mkViewRequest t )
-- List the current topics
( ["list"], "GET" ) ->
pure mkListRequest
-- We don't care about any other requests so throw your hands in the air
_ ->
pure ( Left UnknownRoute )
mkAddRequest
:: Text
-> LBS.ByteString
-> Either Error RqType
mkAddRequest ti c = AddRq
<$> mkTopic ti
<*> (mkCommentText . decodeUtf8 . LBS.toStrict) c
mkViewRequest
:: Text
-> Either Error RqType
mkViewRequest =
fmap ViewRq . mkTopic
mkListRequest
:: Either Error RqType
mkListRequest =
Right ListRq
mkErrorResponse
:: Error
-> Response
mkErrorResponse UnknownRoute =
Res.resp404 "Unknown Route"
mkErrorResponse EmptyCommentText =
Res.resp400 "Empty Comment"
mkErrorResponse EmptyTopic =
Res.resp400 "Empty Topic"
mkErrorResponse ( DBError _ ) =
Res.resp500 "OH NOES"

View File

@ -1,53 +0,0 @@
module FirstApp.Responses where
import Network.Wai (Response, responseLBS)
import Network.HTTP.Types (Status, hContentType, status200,
status400, status404, status500)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.Aeson (ToJSON)
import qualified Data.Aeson as A
import FirstApp.Types (ContentType (JSON, PlainText),
renderContentType)
mkResponse
:: Status
-> ContentType
-> LBS.ByteString
-> Response
mkResponse sts ct msg =
responseLBS sts [(hContentType, renderContentType ct)] msg
resp200
:: LBS.ByteString
-> Response
resp200 =
mkResponse status200 PlainText
resp404
:: LBS.ByteString
-> Response
resp404 =
mkResponse status404 PlainText
resp400
:: LBS.ByteString
-> Response
resp400 =
mkResponse status400 PlainText
-- Some new helpers for different statuses and content types
resp500
:: LBS.ByteString
-> Response
resp500 =
mkResponse status500 PlainText
resp200Json
:: ToJSON a
=> a
-> Response
resp200Json =
mkResponse status200 JSON . A.encode

View File

@ -1,248 +0,0 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module FirstApp.Types
( Error (..)
, ConfigError (..)
, PartialConf (..)
, Port (..)
, DBFilePath (..)
, Conf (..)
, FirstAppDB (..)
, RqType (..)
, ContentType (..)
, Comment (..)
, Topic
, CommentText
, mkTopic
, getTopic
, mkCommentText
, getCommentText
, renderContentType
, fromDbComment
, confPortToWai
) where
import System.IO.Error (IOError)
import GHC.Generics (Generic)
import GHC.Word (Word16)
import Data.ByteString (ByteString)
import Data.Text (Text)
import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)
import Data.Monoid (Last (..))
import Data.Semigroup (Semigroup ((<>)))
import Data.Aeson (ToJSON, FromJSON (..), (.:?))
import qualified Data.Aeson as A
import qualified Data.Aeson.Types as A
import Data.Time (UTCTime)
import Database.SQLite.Simple (Connection)
import Database.SQLite.SimpleErrors.Types (SQLiteResponse)
import FirstApp.DB.Types (DbComment (..))
import FirstApp.Types.Error (Error ( UnknownRoute
, EmptyCommentText
, EmptyTopic
, DBError
))
import FirstApp.Types.CommentText ( CommentText
, mkCommentText
, getCommentText
)
import FirstApp.Types.Topic (Topic, mkTopic, getTopic)
newtype CommentId = CommentId Int
deriving (Show, ToJSON)
data Comment = Comment
{ commentId :: CommentId
, commentTopic :: Topic
, commentText :: CommentText
, commentTime :: UTCTime
}
-- Generic has been added to our deriving list.
deriving ( Show, Generic )
-- Strip the prefix (which may fail if the prefix isn't present), fall
-- back to the original label if need be, then camel-case the name.
-- | modFieldLabel
-- >>> modFieldLabel "commentId"
-- "id"
-- >>> modFieldLabel "topic"
-- "topic"
-- >>> modFieldLabel ""
-- ""
modFieldLabel
:: String
-> String
modFieldLabel l =
A.camelTo2 '_'
. fromMaybe l
$ stripPrefix "comment" l
instance ToJSON Comment where
-- This is one place where we can take advantage of our Generic instance. Aeson
-- already has the encoding functions written for anything that implements the
-- Generic typeclass. So we don't have to write our encoding, we tell Aeson to
-- build it.
toEncoding = A.genericToEncoding opts
where
-- These options let us make some minor adjustments to how Aeson treats
-- our type. Our only adjustment is to alter the field names a little, to
-- remove the 'comment' prefix and use an Aeson function to handle the
-- rest of the name. This accepts any 'String -> String' function but it's
-- wise to keep the modifications simple.
opts = A.defaultOptions
{ A.fieldLabelModifier = modFieldLabel
}
-- For safety we take our stored DbComment and try to construct a Comment that
-- we would be okay with showing someone. However unlikely it may be, this is a
-- nice method for separating out the back and front end of a web app and
-- providing greater guarantees about data cleanliness.
fromDbComment
:: DbComment
-> Either Error Comment
fromDbComment dbc =
Comment (CommentId $ dbCommentId dbc)
<$> (mkTopic $ dbCommentTopic dbc)
<*> (mkCommentText $ dbCommentComment dbc)
<*> (pure $ dbCommentTime dbc)
data RqType
= AddRq Topic CommentText
| ViewRq Topic
| ListRq
-- Provide a type to list our response content types so we don't try to
-- do the wrong thing with what we meant to be used as text or JSON etc.
data ContentType
= PlainText
| JSON
renderContentType
:: ContentType
-> ByteString
renderContentType PlainText = "text/plain"
renderContentType JSON = "application/json"
-----------------
-- Config Types
-----------------
-- This is an alternative way of defining a `newtype`. You define it as a simple
-- record and this lets you specify an unwrapping function at the same time. Which
-- technique you choose is a matter for your specific needs and preference.
--
newtype Port = Port
{ getPort :: Word16 }
deriving (Eq, Show)
newtype DBFilePath = DBFilePath
{ getDBFilePath :: FilePath }
deriving (Eq, Show)
-- The ``Conf`` type will need:
-- - A customisable port number: ``Port``
-- - A filepath for our SQLite database: ``DBFilePath``
data Conf = Conf
{ port :: Port
, dbFilePath :: DBFilePath
}
deriving Eq
-- We're storing our Port as a Word16 to be more precise and prevent invalid
-- values from being used in our application. However Wai is not so stringent.
-- To accommodate this and make our lives a bit easier, we will write this
-- helper function to take ``Conf`` value and convert it to an ``Int``.
confPortToWai
:: Conf
-> Int
confPortToWai =
fromIntegral . getPort . port
-- Similar to when we were considering our application types, leave this empty
-- for now and add to it as you go.
data ConfigError
= MissingPort
| MissingDBFilePath
| JSONDecodeError String
| ConfigFileReadError IOError
deriving Show
-- Our application will be able to load configuration from both a file and
-- command line input. We want to be able to use the command line to temporarily
-- override the configuration from our file. How do we combine the different
-- inputs to enable this property?
-- We want the command line configuration to take precedence over the File
-- configuration, so if we think about combining each of our ``Conf`` records,
-- we want to be able to write something like this:
-- ``defaults <> file <> commandLine``
-- We can use the ``Monoid`` typeclass to handle combining the ``Conf`` records
-- together, and the ``Last`` type to wrap up our values to handle the desired
-- precedence. The ``Last`` type is a wrapper for Maybe that when used with its
-- ``Monoid`` instance will always preference the last ``Just`` value that it
-- has:
-- Last (Just 3) <> Last (Just 1) = Last (Just 1)
-- Last Nothing <> Last (Just 1) = Last (Just 1)
-- Last (Just 1) <> Last Nothing = Last (Just 1)
-- To make this easier, we'll make a new type ``PartialConf`` that will have our
-- ``Last`` wrapped values. We can then define a ``Monoid`` instance for it and
-- have our ``Conf`` be a known good configuration.
data PartialConf = PartialConf
{ pcPort :: Last Port
, pcDBFilePath :: Last DBFilePath
}
-- Before we can define our ``Monoid`` instance for ``PartialConf``, we'll have
-- to define a Semigroup instance. We define our ``(<>)`` function to lean
-- on the ``Semigroup`` instance for Last to always get the last value.
instance Semigroup PartialConf where
_a <> _b = PartialConf
{ pcPort = error "pcPort (<>) not implemented"
, pcDBFilePath = error "pcDBFilePath (<>) not implemented"
}
-- We now define our ``Monoid`` instance for ``PartialConf``. Allowing us to
-- define our always empty configuration, which would always fail our
-- requirements. We just define `mappend` to be an alias of ``(<>)``
instance Monoid PartialConf where
mempty = PartialConf mempty mempty
mappend = (<>)
-- When it comes to reading the configuration options from the command-line, we
-- use the 'optparse-applicative' package. This part of the exercise has already
-- been completed for you, feel free to have a look through the 'CommandLine'
-- module and see how it works.
--
-- For reading the configuration from the file, we're going to use the aeson
-- library to handle the parsing and decoding for us. In order to do this, we
-- have to tell aeson how to go about converting the JSON into our PartialConf
-- data structure.
instance FromJSON PartialConf where
parseJSON = A.withObject "PartialConf" $ \o -> PartialConf
<$> parseToLast "port" Port o
<*> parseToLast "dbFilePath" DBFilePath o
where
parseToLast k c o =
Last . fmap c <$> o .:? k
-- We have a data type to simplify passing around the information we need to run
-- our database queries. This also allows things to change over time without
-- having to rewrite all of the functions that need to interact with DB related
-- things in different ways.
newtype FirstAppDB = FirstAppDB
{ dbConn :: Connection
}

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: lts-10.4
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.6"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1 +0,0 @@
{"foo":33}

View File

@ -1,11 +0,0 @@
module Main where
import Test.DocTest (doctest)
main :: IO ()
main = doctest
[ "-isrc"
, "src/FirstApp/Conf.hs"
, "src/FirstApp/DB.hs"
, "src/FirstApp/Types.hs"
]

View File

@ -1,6 +1,6 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
module FirstApp.Main (runApp) where
module Level01.Core (runApp) where
import Network.Wai (Application, Request, Response,
ResponseReceived, responseLBS)
@ -42,4 +42,3 @@ app _ cb =
-- executable Main.hs.
runApp :: IO ()
runApp = run undefined undefined

View File

@ -2,7 +2,7 @@
The purpose of this exercise is to whet our appetite by creating a basic web
app. The focus will be on reading the [Hackage] documentation for the [Wai]
framework. Consult the ``src/FirstApp/Main.hs`` to find the parts that are
framework. Consult the ``src/Level01/Core.hs`` to find the parts that are
missing and what we need from the [Wai] package to build our "Hello, World!"
application.

View File

@ -1,5 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
module FirstApp.Main (runApp) where
module Level02.Core (runApp) where
import Network.Wai (Application, Request, Response,
pathInfo, requestMethod, responseLBS,
@ -16,12 +16,12 @@ import Data.Either (either)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import FirstApp.Types (ContentType, Error, RqType,
import Level02.Types (ContentType, Error, RqType,
mkCommentText, mkTopic,
renderContentType)
-- --------------------------------------------
-- - Don't start here, go to FirstApp.Types! -
-- - Don't start here, go to Level02.Types! -
-- --------------------------------------------
-- | Some helper functions to make our lives a little more DRY.

View File

@ -38,7 +38,7 @@ GET /<topic>/view
GET /list
```
The starting point for this exercise is the ``src/FirstApp/Types.hs``.
The starting point for this exercise is the ``src/Level02/Types.hs``.
### Running the program:

View File

@ -1,6 +1,6 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-dodgy-exports #-}
module FirstApp.Types
module Level02.Types
( Topic
, CommentText
, ContentType (..)
@ -122,3 +122,5 @@ getCommentText
-> Text
getCommentText =
error "getCommentText not implemented"
---- Go to `src/Level02/Core.hs` next

View File

@ -1,5 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
module FirstApp.Main (runApp, app) where
module Level03.Core (runApp, app) where
import Network.Wai (Application, Request, Response,
pathInfo, requestMethod, responseLBS,
@ -16,7 +16,7 @@ import Data.Either (either)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import FirstApp.Types (ContentType (PlainText), Error (EmptyCommentText, EmptyTopic, UnknownRoute),
import Level03.Types (ContentType (PlainText), Error (EmptyCommentText, EmptyTopic, UnknownRoute),
RqType (AddRq, ListRq, ViewRq),
mkCommentText, mkTopic,
renderContentType)
@ -83,17 +83,13 @@ mkRequest
mkRequest rq =
case ( pathInfo rq, requestMethod rq ) of
-- Commenting on a given topic
( [t, "add"], "POST" ) ->
mkAddRequest t <$> strictRequestBody rq
( [t, "add"], "POST" ) -> mkAddRequest t <$> strictRequestBody rq
-- View the comments on a given topic
( [t, "view"], "GET" ) ->
pure ( mkViewRequest t )
( [t, "view"], "GET" ) -> pure ( mkViewRequest t )
-- List the current topics
( ["list"], "GET" ) ->
pure mkListRequest
( ["list"], "GET" ) -> pure mkListRequest
-- Finally we don't care about any other requests so throw your hands in the air
_ ->
pure ( Left UnknownRoute )
_ -> pure ( Left UnknownRoute )
mkAddRequest
:: Text

View File

@ -23,6 +23,14 @@ $ cabal configure --enable-tests
$ cabal test
```
If you're using Cabal 2.0 or greater (You can check your cabal version with `$ cabal --version`):
```shell
$ cabal new-configure --enable-tests
$ cabal new-build --enable-tests
$ cabal new-test
```
For a stack environment:
```shell
@ -33,10 +41,10 @@ To load the tests in the REPL:
```shell
# Cabal
$ cabal repl level03-tests
$ cabal new-repl app-fp-tests
# Stack
$ stack ghci level03:test:level03-tests
$ stack ghci applied-fp-course:test:app-fp-tests
```
To run the tests in the repl:
@ -45,7 +53,7 @@ To run the tests in the repl:
*Main> :main
```
Start in ``tests/Test.hs``.
Start in ``tests/Level03Tests.hs``.
[HSpec]: (http://hspec.github.io/)
[hspec-wai]: (https://hackage.haskell.org/package/hspec-wai)

Some files were not shown because too many files have changed in this diff Show More