├── code ├── tests │ ├── sql │ │ ├── account.sql │ │ ├── place_category.sql │ │ ├── insert_transactions.sql │ │ ├── place.sql │ │ ├── transaction.sql │ │ ├── transaction_visa.sql │ │ ├── transaction_direct_credit.sql │ │ ├── transaction_atm_operator_fee.sql │ │ └── transaction_internet_transfer.sql │ ├── csv │ │ ├── broken.csv │ │ └── ok.csv │ ├── Main.hs │ ├── DbTests │ │ ├── Place.hs │ │ ├── Account.hs │ │ ├── PlaceCategory.hs │ │ ├── TransactionVisa.hs │ │ ├── Transaction.hs │ │ ├── TransactionDirectCredit.hs │ │ ├── TransactionAtmOperatorFee.hs │ │ ├── TransactionInternetTransfer.hs │ │ └── Internal.hs │ └── CsvTests.hs ├── Setup.hs ├── app.cfg ├── LICENSE ├── src │ ├── Utils.hs │ ├── App.hs │ ├── Db │ │ ├── PlaceCategory.hs │ │ ├── Place.hs │ │ ├── Account.hs │ │ ├── TransactionDirectCredit.hs │ │ ├── TransactionVisa.hs │ │ ├── TransactionAtmOperatorFee.hs │ │ ├── TransactionInternetTransfer.hs │ │ ├── Internal.hs │ │ └── Transaction.hs │ └── Types.hs ├── README.md ├── schema.sql ├── transaction-importer.cabal └── Main.hs ├── code-classy ├── tests │ ├── sql │ │ ├── account.sql │ │ ├── place_category.sql │ │ ├── insert_transactions.sql │ │ ├── place.sql │ │ ├── transaction.sql │ │ ├── transaction_visa.sql │ │ ├── transaction_direct_credit.sql │ │ ├── transaction_atm_operator_fee.sql │ │ └── transaction_internet_transfer.sql │ ├── csv │ │ ├── broken.csv │ │ └── ok.csv │ ├── Main.hs │ ├── DbTests │ │ ├── Place.hs │ │ ├── Account.hs │ │ ├── PlaceCategory.hs │ │ ├── TransactionVisa.hs │ │ ├── Transaction.hs │ │ ├── TransactionDirectCredit.hs │ │ ├── TransactionAtmOperatorFee.hs │ │ ├── TransactionInternetTransfer.hs │ │ └── Internal.hs │ └── CsvTests.hs ├── Setup.hs ├── app.cfg ├── src │ ├── Utils.hs │ ├── App.hs │ ├── Db │ │ ├── PlaceCategory.hs │ │ ├── Place.hs │ │ ├── Account.hs │ │ ├── TransactionDirectCredit.hs │ │ ├── TransactionVisa.hs │ │ ├── TransactionAtmOperatorFee.hs │ │ ├── TransactionInternetTransfer.hs │ │ ├── Transaction.hs │ │ └── Internal.hs │ └── Types.hs ├── LICENSE ├── README.md ├── schema.sql ├── transaction-importer.cabal └── Main.hs ├── slides ├── .travis.yml ├── images │ ├── lego.jpg │ ├── detour.png │ └── grandstand.jpg ├── lib │ ├── font │ │ ├── league-gothic │ │ │ ├── LICENSE │ │ │ ├── league-gothic.eot │ │ │ ├── league-gothic.ttf │ │ │ ├── league-gothic.woff │ │ │ └── league-gothic.css │ │ └── source-sans-pro │ │ │ ├── source-sans-pro-italic.eot │ │ │ ├── source-sans-pro-italic.ttf │ │ │ ├── source-sans-pro-italic.woff │ │ │ ├── source-sans-pro-regular.eot │ │ │ ├── source-sans-pro-regular.ttf │ │ │ ├── source-sans-pro-regular.woff │ │ │ ├── source-sans-pro-semibold.eot │ │ │ ├── source-sans-pro-semibold.ttf │ │ │ ├── source-sans-pro-semibold.woff │ │ │ ├── source-sans-pro-semibolditalic.eot │ │ │ ├── source-sans-pro-semibolditalic.ttf │ │ │ ├── source-sans-pro-semibolditalic.woff │ │ │ └── source-sans-pro.css │ ├── js │ │ ├── html5shiv.js │ │ ├── classList.js │ │ └── head.min.js │ └── css │ │ ├── tomorrow-night-blue.css │ │ ├── solarized-light.css │ │ ├── github.css │ │ └── zenburn.css ├── .gitignore ├── test │ ├── examples │ │ ├── assets │ │ │ ├── image1.png │ │ │ └── image2.png │ │ ├── barebones.html │ │ └── embedded-media.html │ ├── test-markdown.js │ ├── test-pdf.js │ ├── test-markdown.html │ ├── test-pdf.html │ ├── test.html │ ├── test-markdown-element-attributes.js │ ├── test-markdown-slide-attributes.js │ ├── test-markdown-slide-attributes.html │ └── test-markdown-element-attributes.html ├── plugin │ ├── markdown │ │ └── example.md │ ├── multiplex │ │ ├── client.js │ │ ├── master.js │ │ └── index.js │ ├── print-pdf │ │ └── print-pdf.js │ ├── remotes │ │ └── remotes.js │ ├── math │ │ └── math.js │ ├── notes-server │ │ ├── client.js │ │ └── index.js │ └── notes │ │ └── notes.js ├── CONTRIBUTING.md ├── css │ ├── theme │ │ ├── source │ │ │ ├── night.scss │ │ │ ├── serif.scss │ │ │ ├── league.scss │ │ │ ├── simple.scss │ │ │ ├── sky.scss │ │ │ ├── beige.scss │ │ │ ├── black.scss │ │ │ ├── white.scss │ │ │ ├── moon.scss │ │ │ ├── solarized.scss │ │ │ └── blood.scss │ │ ├── template │ │ │ ├── settings.scss │ │ │ └── mixins.scss │ │ └── README.md │ ├── bens.css │ └── print │ │ └── pdf.css ├── LICENSE └── package.json ├── .gitignore ├── README.md ├── LICENSE └── talk.org /code/tests/sql/account.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code-classy/tests/sql/account.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code/tests/sql/place_category.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code-classy/tests/sql/place_category.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /code/tests/sql/insert_transactions.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code-classy/tests/sql/insert_transactions.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /code-classy/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /code/app.cfg: -------------------------------------------------------------------------------- 1 | db { 2 | user = "ben" 3 | database = "transactions" 4 | } 5 | -------------------------------------------------------------------------------- /code-classy/app.cfg: -------------------------------------------------------------------------------- 1 | db { 2 | user = "ben" 3 | database = "transactions" 4 | } 5 | -------------------------------------------------------------------------------- /slides/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | before_script: 5 | - npm install -g grunt-cli -------------------------------------------------------------------------------- /code/tests/sql/place.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO place_category (name) VALUES ('TShirts'); 4 | 5 | COMMIT; 6 | -------------------------------------------------------------------------------- /slides/images/lego.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/images/lego.jpg -------------------------------------------------------------------------------- /code-classy/tests/sql/place.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO place_category (name) VALUES ('TShirts'); 4 | 5 | COMMIT; 6 | -------------------------------------------------------------------------------- /slides/images/detour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/images/detour.png -------------------------------------------------------------------------------- /slides/images/grandstand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/images/grandstand.jpg -------------------------------------------------------------------------------- /slides/lib/font/league-gothic/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License (OFL) 2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 3 | -------------------------------------------------------------------------------- /slides/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .svn 3 | log/*.log 4 | tmp/** 5 | node_modules/ 6 | .sass-cache 7 | css/reveal.min.css 8 | js/reveal.min.js 9 | -------------------------------------------------------------------------------- /slides/test/examples/assets/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/test/examples/assets/image1.png -------------------------------------------------------------------------------- /slides/test/examples/assets/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/test/examples/assets/image2.png -------------------------------------------------------------------------------- /slides/lib/font/league-gothic/league-gothic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/league-gothic/league-gothic.eot -------------------------------------------------------------------------------- /slides/lib/font/league-gothic/league-gothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/league-gothic/league-gothic.ttf -------------------------------------------------------------------------------- /slides/lib/font/league-gothic/league-gothic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/league-gothic/league-gothic.woff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | cabal-dev 3 | *.o 4 | *.hi 5 | *.chi 6 | *.chs.h 7 | .virtualenv 8 | .hsenv 9 | .cabal-sandbox/ 10 | cabal.sandbox.config 11 | cabal.config 12 | -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-italic.eot -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-italic.ttf -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-italic.woff -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-regular.eot -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-regular.ttf -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-regular.woff -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibold.eot -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibold.ttf -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibold.woff -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf -------------------------------------------------------------------------------- /slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benkolera/talk-stacking-your-monads/HEAD/slides/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff -------------------------------------------------------------------------------- /code/tests/sql/transaction.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /code-classy/tests/sql/transaction.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /slides/lib/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | document.createElement('header'); 2 | document.createElement('nav'); 3 | document.createElement('section'); 4 | document.createElement('article'); 5 | document.createElement('aside'); 6 | document.createElement('footer'); 7 | document.createElement('hgroup'); -------------------------------------------------------------------------------- /code/tests/csv/broken.csv: -------------------------------------------------------------------------------- 1 | "Account History for Account:","Bank Account - EVERYDAY BASICS - 12345678" 2 | "2 items" 3 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 AU AUD","-$40.00","No Balance" 4 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 UK GBP","-$40.00","$60.00" 5 | -------------------------------------------------------------------------------- /code-classy/tests/csv/broken.csv: -------------------------------------------------------------------------------- 1 | "Account History for Account:","Bank Account - EVERYDAY BASICS - 12345678" 2 | "2 items" 3 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 AU AUD","-$40.00","No Balance" 4 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 UK GBP","-$40.00","$60.00" 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # talk-fp-in-the-large 2 | Talk & associated code written for the Brisbane Functional Programming Group 2015-02-24 3 | 4 | Check out the slides here: http://stackingyourmonads.benkolera.com/ 5 | (You may want to press 's' to bring up my speaker notes. 6 | 7 | And sample haskell code can be found in the code directory. Note that this requires GHC 7.10! 8 | -------------------------------------------------------------------------------- /slides/test/test-markdown.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 2, 'found two slides' ); 9 | }); 10 | 11 | 12 | } ); 13 | 14 | Reveal.initialize(); 15 | 16 | -------------------------------------------------------------------------------- /slides/lib/font/league-gothic/league-gothic.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'League Gothic'; 3 | src: url('league-gothic.eot'); 4 | src: url('league-gothic.eot?#iefix') format('embedded-opentype'), 5 | url('league-gothic.woff') format('woff'), 6 | url('league-gothic.ttf') format('truetype'); 7 | 8 | font-weight: normal; 9 | font-style: normal; 10 | } -------------------------------------------------------------------------------- /slides/test/test-pdf.js: -------------------------------------------------------------------------------- 1 | 2 | Reveal.addEventListener( 'ready', function() { 3 | 4 | // Only one test for now, we're mainly ensuring that there 5 | // are no execution errors when running PDF mode 6 | 7 | test( 'Reveal.isReady', function() { 8 | strictEqual( Reveal.isReady(), true, 'returns true' ); 9 | }); 10 | 11 | 12 | } ); 13 | 14 | Reveal.initialize({ pdf: true }); 15 | 16 | -------------------------------------------------------------------------------- /code/tests/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Main where 4 | 5 | import BasePrelude hiding (readFile) 6 | 7 | import Test.Tasty 8 | 9 | import CsvTests 10 | import DbTests 11 | 12 | main :: IO () 13 | main = defaultMain tests 14 | 15 | tests :: TestTree 16 | tests = testGroup "Tests" 17 | [ csvTests 18 | , dbTests 19 | ] 20 | -------------------------------------------------------------------------------- /code-classy/tests/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Main where 4 | 5 | import BasePrelude hiding (readFile) 6 | 7 | import Test.Tasty 8 | 9 | import CsvTests 10 | import DbTests 11 | 12 | main :: IO () 13 | main = defaultMain tests 14 | 15 | tests :: TestTree 16 | tests = testGroup "Tests" 17 | [ csvTests 18 | , dbTests 19 | ] 20 | -------------------------------------------------------------------------------- /slides/plugin/markdown/example.md: -------------------------------------------------------------------------------- 1 | # Markdown Demo 2 | 3 | 4 | 5 | ## External 1.1 6 | 7 | Content 1.1 8 | 9 | Note: This will only appear in the speaker notes window. 10 | 11 | 12 | ## External 1.2 13 | 14 | Content 1.2 15 | 16 | 17 | 18 | ## External 2 19 | 20 | Content 2.1 21 | 22 | 23 | 24 | ## External 3.1 25 | 26 | Content 3.1 27 | 28 | 29 | ## External 3.2 30 | 31 | Content 3.2 32 | -------------------------------------------------------------------------------- /code/tests/sql/transaction_visa.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'visa' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code-classy/tests/sql/transaction_visa.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'visa' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code/tests/sql/transaction_direct_credit.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'direct_credit' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code-classy/tests/sql/transaction_direct_credit.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'direct_credit' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code/tests/sql/transaction_atm_operator_fee.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'atm_operator_fee' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code-classy/tests/sql/transaction_atm_operator_fee.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'atm_operator_fee' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code/tests/sql/transaction_internet_transfer.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'internet_transfer_credit' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /code-classy/tests/sql/transaction_internet_transfer.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | INSERT INTO account (type,number,name) VALUES ('Savings',123456,'Savings Account'); 4 | INSERT INTO place_category (name) VALUES ('Shirts'); 5 | INSERT INTO place (name,place_category_id) VALUES ('TeeTurtle',1); 6 | INSERT INTO transaction (date,amount,balance,type,place_id,account_id) VALUES ( 7 | date(now()) 8 | , -10 9 | , 90 10 | , 'internet_transfer_credit' 11 | , 1 12 | , 1 13 | ); 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /slides/plugin/multiplex/client.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var multiplex = Reveal.getConfig().multiplex; 3 | var socketId = multiplex.id; 4 | var socket = io.connect(multiplex.url); 5 | 6 | socket.on(multiplex.id, function(data) { 7 | // ignore data from sockets that aren't ours 8 | if (data.socketId !== socketId) { return; } 9 | if( window.location.host === 'localhost:1947' ) return; 10 | 11 | Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote'); 12 | }); 13 | }()); 14 | -------------------------------------------------------------------------------- /code/tests/DbTests/Place.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Place (placeTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | placeTests :: TestTree 12 | placeTests = testGroup "Place" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "place" 17 | insertPlace 18 | getPlace 19 | (flip (set placeId)) 20 | (Place Nothing "TeeTurtle" (Just 1)) 21 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/Place.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Place (placeTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | placeTests :: TestTree 12 | placeTests = testGroup "Place" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "place" 17 | insertPlace 18 | getPlace 19 | (flip (set placeId)) 20 | (Place Nothing "TeeTurtle" (Just 1)) 21 | -------------------------------------------------------------------------------- /code/tests/DbTests/Account.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Account (accountTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | accountTests :: TestTree 12 | accountTests = testGroup "Account" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "account" 17 | insertAccount 18 | getAccount 19 | (flip (set accountId)) 20 | (Account Nothing "Savings" 1234 "Savings Account") 21 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/Account.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Account (accountTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | accountTests :: TestTree 12 | accountTests = testGroup "Account" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "account" 17 | insertAccount 18 | getAccount 19 | (flip (set accountId)) 20 | (Account Nothing "Savings" 1234 "Savings Account") 21 | -------------------------------------------------------------------------------- /code/tests/DbTests/PlaceCategory.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.PlaceCategory (placeCategoryTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | placeCategoryTests :: TestTree 12 | placeCategoryTests = testGroup "PlaceCategory" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "place_category" 17 | insertPlaceCategory 18 | getPlaceCategory 19 | (flip (set placeCategoryId)) 20 | (PlaceCategory Nothing "TShirts") 21 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/PlaceCategory.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.PlaceCategory (placeCategoryTests) where 3 | 4 | import Control.Lens 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | placeCategoryTests :: TestTree 12 | placeCategoryTests = testGroup "PlaceCategory" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "place_category" 17 | insertPlaceCategory 18 | getPlaceCategory 19 | (flip (set placeCategoryId)) 20 | (PlaceCategory Nothing "TShirts") 21 | -------------------------------------------------------------------------------- /code/tests/DbTests/TransactionVisa.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionVisa (transactionVisaTests) where 3 | 4 | import Data.Time 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | transactionVisaTests :: TestTree 12 | transactionVisaTests = testGroup "TransactionVisa" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "transaction_visa" 17 | insertTransactionVisa 18 | getTransactionVisa 19 | (\ n _ -> n) 20 | (TransactionVisa 1 (fromGregorian 2015 2 9) "AUD" "AU") 21 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/TransactionVisa.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionVisa (transactionVisaTests) where 3 | 4 | import Data.Time 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Db 9 | import DbTests.Internal 10 | 11 | transactionVisaTests :: TestTree 12 | transactionVisaTests = testGroup "TransactionVisa" 13 | [ testCase "roundTrip" testRoundTrip ] 14 | 15 | testRoundTrip :: Assertion 16 | testRoundTrip = roundTripTest "transaction_visa" 17 | insertTransactionVisa 18 | getTransactionVisa 19 | (\ n _ -> n) 20 | (TransactionVisa 1 (fromGregorian 2015 2 9) "AUD" "AU") 21 | -------------------------------------------------------------------------------- /code/tests/DbTests/Transaction.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Transaction (transactionTests) where 3 | 4 | import Control.Lens 5 | import Data.Time 6 | import Test.Tasty 7 | import Test.Tasty.HUnit 8 | 9 | import Db 10 | import DbTests.Internal 11 | 12 | transactionTests :: TestTree 13 | transactionTests = testGroup "Transaction" 14 | [ testCase "roundTrip" testRoundTrip ] 15 | 16 | testRoundTrip :: Assertion 17 | testRoundTrip = roundTripTest "transaction" 18 | insertTransaction 19 | getTransaction 20 | (flip (set transactionId)) 21 | (Transaction Nothing (fromGregorian 2015 2 23) (-10) 90 "eftpos" (Just 1) 1) 22 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/Transaction.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.Transaction (transactionTests) where 3 | 4 | import Control.Lens 5 | import Data.Time 6 | import Test.Tasty 7 | import Test.Tasty.HUnit 8 | 9 | import Db 10 | import DbTests.Internal 11 | 12 | transactionTests :: TestTree 13 | transactionTests = testGroup "Transaction" 14 | [ testCase "roundTrip" testRoundTrip ] 15 | 16 | testRoundTrip :: Assertion 17 | testRoundTrip = roundTripTest "transaction" 18 | insertTransaction 19 | getTransaction 20 | (flip (set transactionId)) 21 | (Transaction Nothing (fromGregorian 2015 2 23) (-10) 90 "eftpos" (Just 1) 1) 22 | -------------------------------------------------------------------------------- /code/tests/DbTests/TransactionDirectCredit.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionDirectCredit (transactionDirectCreditTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionDirectCreditTests :: TestTree 11 | transactionDirectCreditTests = testGroup "TransactionDirectCredit" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_direct_credit" 16 | insertTransactionDirectCredit 17 | getTransactionDirectCredit 18 | (\ n _ -> n) 19 | (TransactionDirectCredit 1 123456) 20 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/TransactionDirectCredit.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionDirectCredit (transactionDirectCreditTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionDirectCreditTests :: TestTree 11 | transactionDirectCreditTests = testGroup "TransactionDirectCredit" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_direct_credit" 16 | insertTransactionDirectCredit 17 | getTransactionDirectCredit 18 | (\ n _ -> n) 19 | (TransactionDirectCredit 1 123456) 20 | -------------------------------------------------------------------------------- /code/tests/DbTests/TransactionAtmOperatorFee.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionAtmOperatorFee (transactionAtmOperatorFeeTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionAtmOperatorFeeTests :: TestTree 11 | transactionAtmOperatorFeeTests = testGroup "TransactionAtmOperatorFee" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_atm_operator_fee" 16 | insertTransactionAtmOperatorFee 17 | getTransactionAtmOperatorFee 18 | (\ n _ -> n) 19 | (TransactionAtmOperatorFee 1 "Withdrawal") 20 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/TransactionAtmOperatorFee.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionAtmOperatorFee (transactionAtmOperatorFeeTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionAtmOperatorFeeTests :: TestTree 11 | transactionAtmOperatorFeeTests = testGroup "TransactionAtmOperatorFee" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_atm_operator_fee" 16 | insertTransactionAtmOperatorFee 17 | getTransactionAtmOperatorFee 18 | (\ n _ -> n) 19 | (TransactionAtmOperatorFee 1 "Withdrawal") 20 | -------------------------------------------------------------------------------- /code/tests/DbTests/TransactionInternetTransfer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionInternetTransfer (transactionInternetTransferTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionInternetTransferTests :: TestTree 11 | transactionInternetTransferTests = testGroup "TransactionInternetTransfer" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_internet_transfer" 16 | insertTransactionInternetTransfer 17 | getTransactionInternetTransfer 18 | (\ n _ -> n) 19 | (TransactionInternetTransfer 1 123456 "Xact Ref") 20 | -------------------------------------------------------------------------------- /code-classy/tests/DbTests/TransactionInternetTransfer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module DbTests.TransactionInternetTransfer (transactionInternetTransferTests) where 3 | 4 | import Test.Tasty 5 | import Test.Tasty.HUnit 6 | 7 | import Db 8 | import DbTests.Internal 9 | 10 | transactionInternetTransferTests :: TestTree 11 | transactionInternetTransferTests = testGroup "TransactionInternetTransfer" 12 | [ testCase "roundTrip" testRoundTrip ] 13 | 14 | testRoundTrip :: Assertion 15 | testRoundTrip = roundTripTest "transaction_internet_transfer" 16 | insertTransactionInternetTransfer 17 | getTransactionInternetTransfer 18 | (\ n _ -> n) 19 | (TransactionInternetTransfer 1 123456 "Xact Ref") 20 | -------------------------------------------------------------------------------- /slides/test/examples/barebones.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Barebones 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |
19 |

Barebones Presentation

20 |

This example contains the bare minimum includes and markup required to run a reveal.js presentation.

21 |
22 | 23 |
24 |

No Theme

25 |

There's no theme included, so it will fall back on browser defaults.

26 |
27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /slides/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Please keep the [issue tracker](http://github.com/hakimel/reveal.js/issues) limited to **bug reports**, **feature requests** and **pull requests**. 4 | 5 | 6 | ### Personal Support 7 | If you have personal support or setup questions the best place to ask those are [StackOverflow](http://stackoverflow.com/questions/tagged/reveal.js). 8 | 9 | 10 | ### Bug Reports 11 | When reporting a bug make sure to include information about which browser and operating system you are on as well as the necessary steps to reproduce the issue. If possible please include a link to a sample presentation where the bug can be tested. 12 | 13 | 14 | ### Pull Requests 15 | - Should follow the coding style of the file you work in, most importantly: 16 | - Tabs to indent 17 | - Single-quoted strings 18 | - Should be made towards the **dev branch** 19 | - Should be submitted from a feature/topic branch (not your master) 20 | -------------------------------------------------------------------------------- /code/tests/csv/ok.csv: -------------------------------------------------------------------------------- 1 | "Account History for Account:","Bank Account - Everyday Basics - 12345678" 2 | "10 items" 3 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 AU AUD","-$40.00","$1,684.7" 4 | "14/02/2015","ATM OPERATOR FEE WITHDRAWAL Money Machine","-$2.50","$1,724.70" 5 | "14/02/2015","ATM WITHDRAWAL Money Machine","-$20.00","$1,727.20" 6 | "14/02/2015","VISA PURCHASE TeeTurtle 13/02 AU USD","-$20.00","$1,747.26" 7 | "13/02/2015","FOREIGN CURRENCY CONVERSION FEE","-$1.29","$1,767.26" 8 | "12/02/2015","EFTPOS WDL Tenkai Sushi Restaur AU","-$38.50","$1,768.55" 9 | "11/02/2015","EFTPOS WDL LB HAIR SALOON PTY L GREENSLOPES QLD","-$79.95","$1,807.05" 10 | "10/02/2015","DIRECT CREDIT Magic Pay Place Ref 654321","$1337.00","$1,887.00" 11 | "10/02/2015","INTERNET TRANSFER CREDIT FROM 12345679 REF NO 41514802","$50.00","$550.00" 12 | "09/02/2015","INTERNET TRANSFER DEBIT TO 12345679 REFERENCE NO 13924810","-$200.00","$500.00" 13 | -------------------------------------------------------------------------------- /code-classy/tests/csv/ok.csv: -------------------------------------------------------------------------------- 1 | "Account History for Account:","Bank Account - Everyday Basics - 12345678" 2 | "10 items" 3 | "14/02/2015","VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 AU AUD","-$40.00","$1,684.7" 4 | "14/02/2015","ATM OPERATOR FEE WITHDRAWAL Money Machine","-$2.50","$1,724.70" 5 | "14/02/2015","ATM WITHDRAWAL Money Machine","-$20.00","$1,727.20" 6 | "14/02/2015","VISA PURCHASE TeeTurtle 13/02 AU USD","-$20.00","$1,747.26" 7 | "13/02/2015","FOREIGN CURRENCY CONVERSION FEE","-$1.29","$1,767.26" 8 | "12/02/2015","EFTPOS WDL Tenkai Sushi Restaur AU","-$38.50","$1,768.55" 9 | "11/02/2015","EFTPOS WDL LB HAIR SALOON PTY L GREENSLOPES QLD","-$79.95","$1,807.05" 10 | "10/02/2015","DIRECT CREDIT Magic Pay Place Ref 654321","$1337.00","$1,887.00" 11 | "10/02/2015","INTERNET TRANSFER CREDIT FROM 12345679 REF NO 41514802","$50.00","$550.00" 12 | "09/02/2015","INTERNET TRANSFER DEBIT TO 12345679 REFERENCE NO 13924810","-$200.00","$500.00" 13 | -------------------------------------------------------------------------------- /code-classy/src/Utils.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | module Utils where 3 | 4 | import BasePrelude 5 | 6 | import Control.Monad.Error.Hoist ((<%!?>)) 7 | import Data.Validation (AccValidation(..)) 8 | import Control.Monad.Trans (MonadIO,liftIO) 9 | import Control.Monad.Except (MonadError,throwError) 10 | import Control.Monad.Trans.Either (EitherT,eitherT) 11 | 12 | throwAccValidation :: (Applicative m, MonadError e m) => (es -> e) -> AccValidation es a -> m a 13 | throwAccValidation f (AccFailure es) = throwError (f es) 14 | throwAccValidation _ (AccSuccess a) = pure a 15 | 16 | wrapException 17 | :: (Exception e, MonadError e' m,MonadIO m, Applicative m) 18 | => (e -> e') 19 | -> IO a 20 | -> m a 21 | wrapException f a = do 22 | liftIO (catch (fmap Right a) (pure . Left . f)) <%!?> id 23 | 24 | wrapExceptions 25 | :: (MonadError e m,MonadIO m, Applicative m) 26 | => IO a 27 | -> [Handler e] 28 | -> m a 29 | wrapExceptions a hs = 30 | liftIO (catches (fmap Right a) handlers) <%!?> id 31 | where 32 | handlers = fmap (fmap Left) hs 33 | -------------------------------------------------------------------------------- /code/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ben Kolera 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /slides/css/theme/source/night.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Black theme for reveal.js. 3 | * 4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | // Include theme-specific fonts 15 | @import url(https://fonts.googleapis.com/css?family=Montserrat:700); 16 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); 17 | 18 | 19 | // Override theme settings (see ../template/settings.scss) 20 | $backgroundColor: #111; 21 | 22 | $mainFont: 'Open Sans', sans-serif; 23 | $linkColor: #e7ad52; 24 | $linkColorHover: lighten( $linkColor, 20% ); 25 | $headingFont: 'Montserrat', Impact, sans-serif; 26 | $headingTextShadow: none; 27 | $headingLetterSpacing: -0.03em; 28 | $headingTextTransform: none; 29 | $selectionBackgroundColor: #e7ad52; 30 | $mainFontSize: 30px; 31 | 32 | 33 | // Theme template ------------------------------ 34 | @import "../template/theme"; 35 | // --------------------------------------------- -------------------------------------------------------------------------------- /code-classy/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ben Kolera 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /slides/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Hakim El Hattab, http://hakim.se 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /slides/test/examples/embedded-media.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Embedded Media 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 |

Embedded Media Test

23 |
24 | 25 |
26 | 27 |
28 | 29 |
30 |

Empty Slide

31 |
32 | 33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ben Kolera 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /slides/css/theme/source/serif.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple theme for reveal.js presentations, similar 3 | * to the default theme. The accent color is brown. 4 | * 5 | * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. 6 | */ 7 | 8 | 9 | // Default mixins and settings ----------------- 10 | @import "../template/mixins"; 11 | @import "../template/settings"; 12 | // --------------------------------------------- 13 | 14 | 15 | 16 | // Override theme settings (see ../template/settings.scss) 17 | $mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 18 | $mainColor: #000; 19 | $headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 20 | $headingColor: #383D3D; 21 | $headingTextShadow: none; 22 | $headingTextTransform: none; 23 | $backgroundColor: #F0F1EB; 24 | $linkColor: #51483D; 25 | $linkColorHover: lighten( $linkColor, 20% ); 26 | $selectionBackgroundColor: #26351C; 27 | 28 | .reveal a { 29 | line-height: 1.3em; 30 | } 31 | 32 | 33 | // Theme template ------------------------------ 34 | @import "../template/theme"; 35 | // --------------------------------------------- 36 | -------------------------------------------------------------------------------- /slides/css/theme/template/settings.scss: -------------------------------------------------------------------------------- 1 | // Base settings for all themes that can optionally be 2 | // overridden by the super-theme 3 | 4 | // Background of the presentation 5 | $backgroundColor: #2b2b2b; 6 | 7 | // Primary/body text 8 | $mainFont: 'Lato', sans-serif; 9 | $mainFontSize: 36px; 10 | $mainColor: #eee; 11 | 12 | // Vertical spacing between blocks of text 13 | $blockMargin: 20px; 14 | 15 | // Headings 16 | $headingMargin: 0 0 $blockMargin 0; 17 | $headingFont: 'League Gothic', Impact, sans-serif; 18 | $headingColor: #eee; 19 | $headingLineHeight: 1.2; 20 | $headingLetterSpacing: normal; 21 | $headingTextTransform: uppercase; 22 | $headingTextShadow: none; 23 | $headingFontWeight: normal; 24 | $heading1TextShadow: $headingTextShadow; 25 | 26 | $heading1Size: 3.77em; 27 | $heading2Size: 2.11em; 28 | $heading3Size: 1.55em; 29 | $heading4Size: 1.00em; 30 | 31 | // Links and actions 32 | $linkColor: #13DAEC; 33 | $linkColorHover: lighten( $linkColor, 20% ); 34 | 35 | // Text selection 36 | $selectionBackgroundColor: #FF5E99; 37 | $selectionColor: #fff; 38 | 39 | // Generates the presentation background, can be overridden 40 | // to return a background image or gradient 41 | @mixin bodyBackground() { 42 | background: $backgroundColor; 43 | } -------------------------------------------------------------------------------- /slides/css/theme/source/league.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * League theme for reveal.js. 3 | * 4 | * This was the default theme pre-3.0.0. 5 | * 6 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 7 | */ 8 | 9 | 10 | // Default mixins and settings ----------------- 11 | @import "../template/mixins"; 12 | @import "../template/settings"; 13 | // --------------------------------------------- 14 | 15 | 16 | 17 | // Include theme-specific fonts 18 | @import url(../../lib/font/league-gothic/league-gothic.css); 19 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); 20 | 21 | // Override theme settings (see ../template/settings.scss) 22 | $headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); 23 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); 24 | 25 | // Background generator 26 | @mixin bodyBackground() { 27 | @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) ); 28 | } 29 | 30 | 31 | 32 | // Theme template ------------------------------ 33 | @import "../template/theme"; 34 | // --------------------------------------------- -------------------------------------------------------------------------------- /code/src/Utils.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | module Utils where 3 | 4 | import BasePrelude 5 | 6 | import Data.Validation (AccValidation(..)) 7 | import Control.Monad.Trans (MonadIO,liftIO) 8 | import Control.Monad.Except (MonadError,throwError) 9 | import Control.Monad.Trans.Either (EitherT,eitherT) 10 | 11 | throwEither :: (Applicative m, MonadError e m) => Either e a -> m a 12 | throwEither = either throwError pure 13 | 14 | throwEitherT :: (Applicative m, MonadError e m) => EitherT e m a -> m a 15 | throwEitherT = eitherT throwError pure 16 | 17 | throwAccValidation :: (Applicative m, MonadError e m) => (es -> e) -> AccValidation es a -> m a 18 | throwAccValidation f (AccFailure es) = throwError (f es) 19 | throwAccValidation _ (AccSuccess a) = pure a 20 | 21 | wrapException 22 | :: (Exception e, MonadError e' m,MonadIO m, Applicative m) 23 | => (e -> e') 24 | -> IO a 25 | -> m a 26 | wrapException f a = do 27 | liftIO (catch (fmap Right a) (pure . Left . f)) >>= throwEither 28 | 29 | wrapExceptions 30 | :: (MonadError e m,MonadIO m, Applicative m) 31 | => IO a 32 | -> [Handler e] 33 | -> m a 34 | wrapExceptions a hs = 35 | liftIO (catches (fmap Right a) handlers) >>= throwEither 36 | where 37 | handlers = fmap (fmap Left) hs 38 | -------------------------------------------------------------------------------- /slides/css/theme/source/simple.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple theme for reveal.js presentations, similar 3 | * to the default theme. The accent color is darkblue. 4 | * 5 | * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. 6 | * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 7 | */ 8 | 9 | 10 | // Default mixins and settings ----------------- 11 | @import "../template/mixins"; 12 | @import "../template/settings"; 13 | // --------------------------------------------- 14 | 15 | 16 | 17 | // Include theme-specific fonts 18 | @import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); 19 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); 20 | 21 | 22 | // Override theme settings (see ../template/settings.scss) 23 | $mainFont: 'Lato', sans-serif; 24 | $mainColor: #000; 25 | $headingFont: 'News Cycle', Impact, sans-serif; 26 | $headingColor: #000; 27 | $headingTextShadow: none; 28 | $headingTextTransform: none; 29 | $backgroundColor: #fff; 30 | $linkColor: #00008B; 31 | $linkColorHover: lighten( $linkColor, 20% ); 32 | $selectionBackgroundColor: rgba(0, 0, 0, 0.99); 33 | 34 | 35 | 36 | // Theme template ------------------------------ 37 | @import "../template/theme"; 38 | // --------------------------------------------- -------------------------------------------------------------------------------- /slides/css/theme/source/sky.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Sky theme for reveal.js. 3 | * 4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | 15 | // Include theme-specific fonts 16 | @import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); 17 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); 18 | 19 | 20 | // Override theme settings (see ../template/settings.scss) 21 | $mainFont: 'Open Sans', sans-serif; 22 | $mainColor: #333; 23 | $headingFont: 'Quicksand', sans-serif; 24 | $headingColor: #333; 25 | $headingLetterSpacing: -0.08em; 26 | $headingTextShadow: none; 27 | $backgroundColor: #f7fbfc; 28 | $linkColor: #3b759e; 29 | $linkColorHover: lighten( $linkColor, 20% ); 30 | $selectionBackgroundColor: #134674; 31 | 32 | // Fix links so they are not cut off 33 | .reveal a { 34 | line-height: 1.3em; 35 | } 36 | 37 | // Background generator 38 | @mixin bodyBackground() { 39 | @include radial-gradient( #add9e4, #f7fbfc ); 40 | } 41 | 42 | 43 | 44 | // Theme template ------------------------------ 45 | @import "../template/theme"; 46 | // --------------------------------------------- 47 | -------------------------------------------------------------------------------- /slides/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reveal.js", 3 | "version": "3.0.0", 4 | "description": "The HTML Presentation Framework", 5 | "homepage": "http://lab.hakim.se/reveal-js", 6 | "subdomain": "revealjs", 7 | "scripts": { 8 | "test": "grunt test", 9 | "start": "" 10 | }, 11 | "author": { 12 | "name": "Hakim El Hattab", 13 | "email": "hakim.elhattab@gmail.com", 14 | "web": "http://hakim.se" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/hakimel/reveal.js.git" 19 | }, 20 | "engines": { 21 | "node": "~0.10.0" 22 | }, 23 | "dependencies": { 24 | "underscore": "~1.5.1", 25 | "express": "~2.5.9", 26 | "mustache": "~0.7.2", 27 | "socket.io": "~0.9.16" 28 | }, 29 | "devDependencies": { 30 | "grunt-contrib-qunit": "~0.5.2", 31 | "grunt-contrib-jshint": "~0.6.4", 32 | "grunt-contrib-cssmin": "~0.4.1", 33 | "grunt-contrib-uglify": "~0.2.4", 34 | "grunt-contrib-watch": "~0.5.3", 35 | "grunt-sass": "~0.14.0", 36 | "grunt-contrib-connect": "~0.8.0", 37 | "grunt-autoprefixer": "~1.0.1", 38 | "grunt-zip": "~0.7.0", 39 | "grunt": "~0.4.0", 40 | "node-sass": "~0.9.3" 41 | }, 42 | "licenses": [ 43 | { 44 | "type": "MIT", 45 | "url": "https://github.com/hakimel/reveal.js/blob/master/LICENSE" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /code-classy/src/App.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | {-# LANGUAGE NoImplicitPrelude #-} 5 | {-# LANGUAGE TemplateHaskell #-} 6 | {-# LANGUAGE ConstraintKinds #-} 7 | module App where 8 | 9 | import BasePrelude hiding (first) 10 | 11 | import Control.Lens 12 | import Control.Monad.Except (ExceptT, MonadError (..), runExceptT) 13 | import Control.Monad.Reader (MonadReader, ReaderT, runReaderT) 14 | import Control.Monad.Trans (MonadIO, liftIO) 15 | import Data.Bifunctor (first) 16 | 17 | import Csv 18 | import Db 19 | import Utils 20 | 21 | data AppEnv = AppEnv { _appEnvDb :: DbEnv } 22 | makeClassy ''AppEnv 23 | data AppError = AppCsvError CsvError | AppDbError DbError 24 | makeClassyPrisms ''AppError 25 | 26 | instance AsDbError AppError where 27 | _DbError = _AppDbError . _DbError 28 | 29 | instance AsCsvError AppError where 30 | _CsvError = _AppCsvError . _CsvError 31 | 32 | instance HasDbEnv AppEnv where 33 | dbEnv = appEnvDb . dbEnv 34 | 35 | type CanApp c e m = 36 | ( CanDb c e m 37 | , CanCsv e m 38 | , AsAppError e 39 | , HasAppEnv c 40 | ) 41 | 42 | loadAndInsert :: CanApp c e m => FilePath -> m [Int] 43 | loadAndInsert p = do 44 | xacts <- readTransactions p 45 | insertTransactions xacts 46 | -------------------------------------------------------------------------------- /talk.org: -------------------------------------------------------------------------------- 1 | Stacking your Monads - Monad Transformers for FP Error Handling & Configuration 2 | 3 | * Me 4 | ** Dev Team Manager at iseek Communications 5 | ** We build enterprise-grade networks, data centers and cloud infrastructure 6 | ** Lots of little distributed APIs, some written on top of vendor APIs 7 | ** Types and FP help keep us sane 8 | * Our Journey Tonight 9 | ** Getting rid of exceptions with IO (Either e a) 10 | ** Enter ExceptT & what it means to be a monad transformer 11 | ** ReaderT to weave configuration into our contexts 12 | ** Stacking it all together and hiding the layers 13 | ** Neat effects of the MonadReader/MonadIO/MonadError typeclasses on your code 14 | * Conclusions & Take aways 15 | ** Why Monad Transformers are useful to stack different monads into one thing. 16 | ** How the mtl classes help you not to care about the exact stack. 17 | ** Get the inklings of how you'd use this in a bigger project. 18 | * Other Things of mention 19 | ** The Errors Package: Lots of goodies to help you avoid partial functions and emit good errors. 20 | ** AccValidation: Allows you to accumulate all errors rather than terminate at the first. 21 | ** You can do this in scala, too. 22 | * Useful Resources 23 | ** RWH Ch18: Monad Transformers: http://book.realworldhaskell.org/read/monad-transformers.html 24 | ** RWH Ch19: Error Handling: http://book.realworldhaskell.org/read/error-handling.html 25 | -------------------------------------------------------------------------------- /slides/css/theme/source/beige.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Beige theme for reveal.js. 3 | * 4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | 15 | // Include theme-specific fonts 16 | @import url(../../lib/font/league-gothic/league-gothic.css); 17 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); 18 | 19 | 20 | // Override theme settings (see ../template/settings.scss) 21 | $mainColor: #333; 22 | $headingColor: #333; 23 | $headingTextShadow: none; 24 | $backgroundColor: #f7f3de; 25 | $linkColor: #8b743d; 26 | $linkColorHover: lighten( $linkColor, 20% ); 27 | $selectionBackgroundColor: rgba(79, 64, 28, 0.99); 28 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); 29 | 30 | // Background generator 31 | @mixin bodyBackground() { 32 | @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) ); 33 | } 34 | 35 | 36 | 37 | // Theme template ------------------------------ 38 | @import "../template/theme"; 39 | // --------------------------------------------- -------------------------------------------------------------------------------- /slides/css/theme/source/black.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Black theme for reveal.js. This is the opposite of the 'white' theme. 3 | * 4 | * Copyright (C) 2015 Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | // Include theme-specific fonts 15 | @import url(../../lib/font/source-sans-pro/source-sans-pro.css); 16 | 17 | 18 | // Override theme settings (see ../template/settings.scss) 19 | $backgroundColor: #222; 20 | 21 | $mainColor: #fff; 22 | $headingColor: #fff; 23 | 24 | $mainFontSize: 38px; 25 | $mainFont: 'Source Sans Pro', Helvetica, sans-serif; 26 | $headingFont: 'Source Sans Pro', Helvetica, sans-serif; 27 | $headingTextShadow: none; 28 | $headingLetterSpacing: normal; 29 | $headingTextTransform: uppercase; 30 | $headingFontWeight: 600; 31 | $linkColor: #42affa; 32 | $linkColorHover: lighten( $linkColor, 15% ); 33 | $selectionBackgroundColor: lighten( $linkColor, 25% ); 34 | 35 | $heading1Size: 2.5em; 36 | $heading2Size: 1.6em; 37 | $heading3Size: 1.3em; 38 | $heading4Size: 1.0em; 39 | 40 | section.has-light-background { 41 | &, h1, h2, h3, h4, h5, h6 { 42 | color: #222; 43 | } 44 | } 45 | 46 | 47 | // Theme template ------------------------------ 48 | @import "../template/theme"; 49 | // --------------------------------------------- -------------------------------------------------------------------------------- /slides/css/theme/source/white.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * White theme for reveal.js. This is the opposite of the 'black' theme. 3 | * 4 | * Copyright (C) 2015 Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | // Include theme-specific fonts 15 | @import url(../../lib/font/source-sans-pro/source-sans-pro.css); 16 | 17 | 18 | // Override theme settings (see ../template/settings.scss) 19 | $backgroundColor: #fff; 20 | 21 | $mainColor: #222; 22 | $headingColor: #222; 23 | 24 | $mainFontSize: 38px; 25 | $mainFont: 'Source Sans Pro', Helvetica, sans-serif; 26 | $headingFont: 'Source Sans Pro', Helvetica, sans-serif; 27 | $headingTextShadow: none; 28 | $headingLetterSpacing: normal; 29 | $headingTextTransform: uppercase; 30 | $headingFontWeight: 600; 31 | $linkColor: #2a76dd; 32 | $linkColorHover: lighten( $linkColor, 15% ); 33 | $selectionBackgroundColor: lighten( $linkColor, 25% ); 34 | 35 | $heading1Size: 2.5em; 36 | $heading2Size: 1.6em; 37 | $heading3Size: 1.3em; 38 | $heading4Size: 1.0em; 39 | 40 | section.has-dark-background { 41 | &, h1, h2, h3, h4, h5, h6 { 42 | color: #fff; 43 | } 44 | } 45 | 46 | 47 | // Theme template ------------------------------ 48 | @import "../template/theme"; 49 | // --------------------------------------------- -------------------------------------------------------------------------------- /slides/test/test-markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /slides/plugin/print-pdf/print-pdf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * phantomjs script for printing presentations to PDF. 3 | * 4 | * Example: 5 | * phantomjs print-pdf.js "http://lab.hakim.se/reveal-js?print-pdf" reveal-demo.pdf 6 | * 7 | * By Manuel Bieh (https://github.com/manuelbieh) 8 | */ 9 | 10 | // html2pdf.js 11 | var page = new WebPage(); 12 | var system = require( 'system' ); 13 | 14 | var slideWidth = system.args[3] ? system.args[3].split( 'x' )[0] : 960; 15 | var slideHeight = system.args[3] ? system.args[3].split( 'x' )[1] : 700; 16 | 17 | page.viewportSize = { 18 | width: slideWidth, 19 | height: slideHeight 20 | }; 21 | 22 | // TODO 23 | // Something is wrong with these config values. An input 24 | // paper width of 1920px actually results in a 756px wide 25 | // PDF. 26 | page.paperSize = { 27 | width: Math.round( slideWidth * 2 ), 28 | height: Math.round( slideHeight * 2 ), 29 | border: 0 30 | }; 31 | 32 | var inputFile = system.args[1] || 'index.html?print-pdf'; 33 | var outputFile = system.args[2] || 'slides.pdf'; 34 | 35 | if( outputFile.match( /\.pdf$/gi ) === null ) { 36 | outputFile += '.pdf'; 37 | } 38 | 39 | console.log( 'Printing PDF (Paper size: '+ page.paperSize.width + 'x' + page.paperSize.height +')' ); 40 | 41 | page.open( inputFile, function( status ) { 42 | window.setTimeout( function() { 43 | console.log( 'Printed succesfully' ); 44 | page.render( outputFile ); 45 | phantom.exit(); 46 | }, 1000 ); 47 | } ); 48 | 49 | -------------------------------------------------------------------------------- /slides/css/theme/source/moon.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Solarized Dark theme for reveal.js. 3 | * Author: Achim Staebler 4 | */ 5 | 6 | 7 | // Default mixins and settings ----------------- 8 | @import "../template/mixins"; 9 | @import "../template/settings"; 10 | // --------------------------------------------- 11 | 12 | 13 | 14 | // Include theme-specific fonts 15 | @import url(../../lib/font/league-gothic/league-gothic.css); 16 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); 17 | 18 | /** 19 | * Solarized colors by Ethan Schoonover 20 | */ 21 | html * { 22 | color-profile: sRGB; 23 | rendering-intent: auto; 24 | } 25 | 26 | // Solarized colors 27 | $base03: #002b36; 28 | $base02: #073642; 29 | $base01: #586e75; 30 | $base00: #657b83; 31 | $base0: #839496; 32 | $base1: #93a1a1; 33 | $base2: #eee8d5; 34 | $base3: #fdf6e3; 35 | $yellow: #b58900; 36 | $orange: #cb4b16; 37 | $red: #dc322f; 38 | $magenta: #d33682; 39 | $violet: #6c71c4; 40 | $blue: #268bd2; 41 | $cyan: #2aa198; 42 | $green: #859900; 43 | 44 | // Override theme settings (see ../template/settings.scss) 45 | $mainColor: $base1; 46 | $headingColor: $base2; 47 | $headingTextShadow: none; 48 | $backgroundColor: $base03; 49 | $linkColor: $blue; 50 | $linkColorHover: lighten( $linkColor, 20% ); 51 | $selectionBackgroundColor: $magenta; 52 | 53 | 54 | 55 | // Theme template ------------------------------ 56 | @import "../template/theme"; 57 | // --------------------------------------------- 58 | -------------------------------------------------------------------------------- /code/README.md: -------------------------------------------------------------------------------- 1 | Setup 2 | ===== 3 | 4 | Installation 5 | ------------ 6 | 7 | First you'll want to create a sandbox to install all of the deps into: 8 | 9 | cabal sandbox init 10 | cabal install --only-dependencies 11 | 12 | To run the code and tests, you'll need a locally running postgres 13 | server that will accept a passwordless login from the current user. 14 | 15 | Tests 16 | ----- 17 | The test cases create a database with a random name for each test, 18 | so the current user will need to have permission to create a database 19 | (having superuser priviledges is the easiest way to achieve this). 20 | 21 | If you need another way to login to the DB (say with a password) you'll 22 | have to change the connectPostgreSQL string in the test case, sadly. 23 | 24 | Run the tests by running: 25 | cabal test 26 | 27 | Code 28 | ---- 29 | To run the code, you'll need to create a database with the desired 30 | schema in it. 31 | 32 | createdb transaction_importer 33 | psql transaction_importer < schema.sql 34 | 35 | Then fill in app.cfg with a user,database (and an optional port,password and host) 36 | 37 | cabal run -- tests/csv/ok.csv 38 | 39 | What does it do? 40 | ---------------- 41 | The idea is that it was supposed to parse a CSV that I get from my internet 42 | banking and insert the transactions into a database for reporting. 43 | 44 | This involves reading the files off disk and parsing them in our Csv monad. 45 | 46 | Then the get inserted into our normalised table stucture in the Db monad. 47 | 48 | For an example of the csv, look in tests/csv/ok.csv 49 | -------------------------------------------------------------------------------- /slides/lib/js/classList.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ 2 | if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p App a -> IO (Either AppError a) 37 | runApp e = runExceptT . flip runReaderT e . unApp 38 | 39 | loadAndInsert :: FilePath -> App [Int] 40 | loadAndInsert p = do 41 | xacts <- liftCsv $ readTransactions p 42 | liftDb $ insertTransactions xacts 43 | 44 | liftCsv :: (Applicative m,MonadError AppError m,MonadIO m) => Csv a -> m a 45 | liftCsv c = do 46 | res <- liftIO $ runCsv c 47 | throwEither . first AppCsvError $ res 48 | 49 | liftDb :: (Applicative m,MonadReader AppEnv m, MonadError AppError m,MonadIO m) => Db a -> m a 50 | liftDb c = do 51 | e <- view appEnvDb 52 | res <- liftIO $ runDb e c 53 | throwEither . first AppDbError $ res 54 | -------------------------------------------------------------------------------- /slides/css/theme/template/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin vertical-gradient( $top, $bottom ) { 2 | background: $top; 3 | background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); 4 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); 5 | background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); 6 | background: -o-linear-gradient( top, $top 0%, $bottom 100% ); 7 | background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); 8 | background: linear-gradient( top, $top 0%, $bottom 100% ); 9 | } 10 | 11 | @mixin horizontal-gradient( $top, $bottom ) { 12 | background: $top; 13 | background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); 14 | background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); 15 | background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); 16 | background: -o-linear-gradient( left, $top 0%, $bottom 100% ); 17 | background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); 18 | background: linear-gradient( left, $top 0%, $bottom 100% ); 19 | } 20 | 21 | @mixin radial-gradient( $outer, $inner, $type: circle ) { 22 | background: $outer; 23 | background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 24 | background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); 25 | background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 26 | background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 27 | background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 28 | background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 29 | } -------------------------------------------------------------------------------- /slides/plugin/multiplex/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var io = require('socket.io'); 4 | var crypto = require('crypto'); 5 | 6 | var app = express.createServer(); 7 | var staticDir = express.static; 8 | 9 | io = io.listen(app); 10 | 11 | var opts = { 12 | port: 1948, 13 | baseDir : __dirname + '/../../' 14 | }; 15 | 16 | io.sockets.on('connection', function(socket) { 17 | socket.on('slidechanged', function(slideData) { 18 | if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return; 19 | if (createHash(slideData.secret) === slideData.socketId) { 20 | slideData.secret = null; 21 | socket.broadcast.emit(slideData.socketId, slideData); 22 | }; 23 | }); 24 | }); 25 | 26 | app.configure(function() { 27 | [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) { 28 | app.use('/' + dir, staticDir(opts.baseDir + dir)); 29 | }); 30 | }); 31 | 32 | app.get("/", function(req, res) { 33 | res.writeHead(200, {'Content-Type': 'text/html'}); 34 | fs.createReadStream(opts.baseDir + '/index.html').pipe(res); 35 | }); 36 | 37 | app.get("/token", function(req,res) { 38 | var ts = new Date().getTime(); 39 | var rand = Math.floor(Math.random()*9999999); 40 | var secret = ts.toString() + rand.toString(); 41 | res.send({secret: secret, socketId: createHash(secret)}); 42 | }); 43 | 44 | var createHash = function(secret) { 45 | var cipher = crypto.createCipher('blowfish', secret); 46 | return(cipher.final('hex')); 47 | }; 48 | 49 | // Actually listen 50 | app.listen(opts.port || null); 51 | 52 | var brown = '\033[33m', 53 | green = '\033[32m', 54 | reset = '\033[0m'; 55 | 56 | console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset ); -------------------------------------------------------------------------------- /slides/css/theme/README.md: -------------------------------------------------------------------------------- 1 | ## Dependencies 2 | 3 | Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceding: https://github.com/hakimel/reveal.js#full-setup 4 | 5 | You also need to install Ruby and then Sass (with `gem install sass`). 6 | 7 | ## Creating a Theme 8 | 9 | To create your own theme, start by duplicating any ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source) and adding it to the compilation list in the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js). 10 | 11 | Each theme file does four things in the following order: 12 | 13 | 1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)** 14 | Shared utility functions. 15 | 16 | 2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)** 17 | Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3. 18 | 19 | 3. **Override** 20 | This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding full selectors with hardcoded styles. 21 | 22 | 4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** 23 | The template theme file which will generate final CSS output based on the currently defined variables. 24 | 25 | When you are done, run `grunt themes` to compile the Sass file to CSS and you are ready to use your new theme. 26 | -------------------------------------------------------------------------------- /slides/plugin/math/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax. 4 | * 5 | * @author Hakim El Hattab 6 | */ 7 | var RevealMath = window.RevealMath || (function(){ 8 | 9 | var options = Reveal.getConfig().math || {}; 10 | options.mathjax = options.mathjax || 'http://cdn.mathjax.org/mathjax/latest/MathJax.js'; 11 | options.config = options.config || 'TeX-AMS_HTML-full'; 12 | 13 | loadScript( options.mathjax + '?config=' + options.config, function() { 14 | 15 | MathJax.Hub.Config({ 16 | messageStyle: 'none', 17 | tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] }, 18 | skipStartupTypeset: true 19 | }); 20 | 21 | // Typeset followed by an immediate reveal.js layout since 22 | // the typesetting process could affect slide height 23 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] ); 24 | MathJax.Hub.Queue( Reveal.layout ); 25 | 26 | // Reprocess equations in slides when they turn visible 27 | Reveal.addEventListener( 'slidechanged', function( event ) { 28 | 29 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] ); 30 | 31 | } ); 32 | 33 | } ); 34 | 35 | function loadScript( url, callback ) { 36 | 37 | var head = document.querySelector( 'head' ); 38 | var script = document.createElement( 'script' ); 39 | script.type = 'text/javascript'; 40 | script.src = url; 41 | 42 | // Wrapper for callback to make sure it only fires once 43 | var finish = function() { 44 | if( typeof callback === 'function' ) { 45 | callback.call(); 46 | callback = null; 47 | } 48 | } 49 | 50 | script.onload = finish; 51 | 52 | // IE 53 | script.onreadystatechange = function() { 54 | if ( this.readyState === 'loaded' ) { 55 | finish(); 56 | } 57 | } 58 | 59 | // Normal browsers 60 | head.appendChild( script ); 61 | 62 | } 63 | 64 | })(); 65 | -------------------------------------------------------------------------------- /slides/plugin/notes-server/client.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // don't emit events from inside the previews themselves 4 | if( window.location.search.match( /receiver/gi ) ) { return; } 5 | 6 | var socket = io.connect( window.location.origin ), 7 | socketId = Math.random().toString().slice( 2 ); 8 | 9 | console.log( 'View slide notes at ' + window.location.origin + '/notes/' + socketId ); 10 | 11 | window.open( window.location.origin + '/notes/' + socketId, 'notes-' + socketId ); 12 | 13 | /** 14 | * Posts the current slide data to the notes window 15 | */ 16 | function post() { 17 | 18 | var slideElement = Reveal.getCurrentSlide(), 19 | notesElement = slideElement.querySelector( 'aside.notes' ); 20 | 21 | var messageData = { 22 | notes: '', 23 | markdown: false, 24 | socketId: socketId, 25 | state: Reveal.getState() 26 | }; 27 | 28 | // Look for notes defined in a slide attribute 29 | if( slideElement.hasAttribute( 'data-notes' ) ) { 30 | messageData.notes = slideElement.getAttribute( 'data-notes' ); 31 | } 32 | 33 | // Look for notes defined in an aside element 34 | if( notesElement ) { 35 | messageData.notes = notesElement.innerHTML; 36 | messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; 37 | } 38 | 39 | socket.emit( 'statechanged', messageData ); 40 | 41 | } 42 | 43 | // When a new notes window connects, post our current state 44 | socket.on( 'connect', function( data ) { 45 | post(); 46 | } ); 47 | 48 | // Monitor events that trigger a change in state 49 | Reveal.addEventListener( 'slidechanged', post ); 50 | Reveal.addEventListener( 'fragmentshown', post ); 51 | Reveal.addEventListener( 'fragmenthidden', post ); 52 | Reveal.addEventListener( 'overviewhidden', post ); 53 | Reveal.addEventListener( 'overviewshown', post ); 54 | Reveal.addEventListener( 'paused', post ); 55 | Reveal.addEventListener( 'resumed', post ); 56 | 57 | // Post the initial state 58 | post(); 59 | 60 | }()); 61 | -------------------------------------------------------------------------------- /slides/plugin/notes-server/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var io = require('socket.io'); 4 | var _ = require('underscore'); 5 | var Mustache = require('mustache'); 6 | 7 | var app = express.createServer(); 8 | var staticDir = express.static; 9 | 10 | io = io.listen(app); 11 | 12 | var opts = { 13 | port : 1947, 14 | baseDir : __dirname + '/../../' 15 | }; 16 | 17 | io.sockets.on( 'connection', function( socket ) { 18 | 19 | socket.on( 'connect', function( data ) { 20 | socket.broadcast.emit( 'connect', data ); 21 | }); 22 | 23 | socket.on( 'statechanged', function( data ) { 24 | socket.broadcast.emit( 'statechanged', data ); 25 | }); 26 | 27 | }); 28 | 29 | app.configure( function() { 30 | 31 | [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) { 32 | app.use( '/' + dir, staticDir( opts.baseDir + dir ) ); 33 | }); 34 | 35 | }); 36 | 37 | app.get('/', function( req, res ) { 38 | 39 | res.writeHead( 200, { 'Content-Type': 'text/html' } ); 40 | fs.createReadStream( opts.baseDir + '/index.html' ).pipe( res ); 41 | 42 | }); 43 | 44 | app.get( '/notes/:socketId', function( req, res ) { 45 | 46 | fs.readFile( opts.baseDir + 'plugin/notes-server/notes.html', function( err, data ) { 47 | res.send( Mustache.to_html( data.toString(), { 48 | socketId : req.params.socketId 49 | })); 50 | }); 51 | 52 | }); 53 | 54 | // Actually listen 55 | app.listen( opts.port || null ); 56 | 57 | var brown = '\033[33m', 58 | green = '\033[32m', 59 | reset = '\033[0m'; 60 | 61 | var slidesLocation = 'http://localhost' + ( opts.port ? ( ':' + opts.port ) : '' ); 62 | 63 | console.log( brown + 'reveal.js - Speaker Notes' + reset ); 64 | console.log( '1. Open the slides at ' + green + slidesLocation + reset ); 65 | console.log( '2. Click on the link your JS console to go to the notes page' ); 66 | console.log( '3. Advance through your slides and your notes will advance automatically' ); 67 | -------------------------------------------------------------------------------- /slides/test/test-pdf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test PDF exports 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /code/tests/DbTests/Internal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module DbTests.Internal where 4 | 5 | import BasePrelude 6 | 7 | import Control.Monad.Random 8 | import qualified Data.ByteString as B 9 | import qualified Data.ByteString.Char8 as B8 10 | import Database.PostgreSQL.Simple 11 | import Database.PostgreSQL.Simple.Types 12 | import Test.Tasty.HUnit 13 | 14 | import Db 15 | 16 | rnd :: (RandomGen g) => Rand g Char 17 | rnd = getRandomR ('a','z') 18 | 19 | dbName :: IO String 20 | dbName = ("transactions_test_" <>) <$> evalRandIO (sequence (replicate 10 rnd)) 21 | 22 | assertDbResult :: Either DbError a -> (a -> Assertion) -> Assertion 23 | assertDbResult e f = either (error . ("DB Failed: " <>) . show) f e 24 | 25 | roundTripTest 26 | :: (Eq a, Show a) 27 | => String 28 | -> (n -> Db i) 29 | -> (i -> Db (Maybe a)) 30 | -> (n -> i -> a) 31 | -> n 32 | -> Assertion 33 | roundTripTest testName insertN queryA newToExisting new = 34 | withDb testName $ \ e -> do 35 | res <- runDb e $ do 36 | i <- insertN new 37 | a <- queryA i 38 | pure (i,a) 39 | assertDbResult res $ \ (i,a) -> Just (newToExisting new i) @=? a 40 | 41 | withDb :: String -> (DbEnv -> Assertion) -> Assertion 42 | withDb testName f = do 43 | pc <- connectPostgreSQL "dbname=postgres" 44 | n <- dbName 45 | let nb = B8.pack n 46 | bracket 47 | (setup pc nb) 48 | (cleanup pc nb) 49 | f 50 | 51 | where 52 | setup pc nb = do 53 | void . execute_ pc . Query $ "CREATE DATABASE " <> nb 54 | tc <- connectPostgreSQL ("dbname=" <> nb) 55 | schemaSql <- B.readFile "schema.sql" 56 | void . execute_ tc . Query $ schemaSql 57 | dataSql <- B.readFile $ "tests/sql/" <> testName <> ".sql" 58 | void . execute_ tc . Query $ dataSql 59 | pure (DbEnv tc) 60 | 61 | cleanup pc nb tc = do 62 | closeDbEnv tc 63 | void . execute_ pc . Query $ "DROP DATABASE " <> nb 64 | -------------------------------------------------------------------------------- /slides/test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /slides/lib/css/tomorrow-night-blue.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Blue Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #7285b7; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-tag, 15 | .hljs-regexp, 16 | .ruby .hljs-constant, 17 | .xml .hljs-tag .hljs-title, 18 | .xml .hljs-pi, 19 | .xml .hljs-doctype, 20 | .html .hljs-doctype, 21 | .css .hljs-id, 22 | .css .hljs-class, 23 | .css .hljs-pseudo { 24 | color: #ff9da4; 25 | } 26 | 27 | /* Tomorrow Orange */ 28 | .hljs-number, 29 | .hljs-preprocessor, 30 | .hljs-pragma, 31 | .hljs-built_in, 32 | .hljs-literal, 33 | .hljs-params, 34 | .hljs-constant { 35 | color: #ffc58f; 36 | } 37 | 38 | /* Tomorrow Yellow */ 39 | .ruby .hljs-class .hljs-title, 40 | .css .hljs-rules .hljs-attribute { 41 | color: #ffeead; 42 | } 43 | 44 | /* Tomorrow Green */ 45 | .hljs-string, 46 | .hljs-value, 47 | .hljs-inheritance, 48 | .hljs-header, 49 | .ruby .hljs-symbol, 50 | .xml .hljs-cdata { 51 | color: #d1f1a9; 52 | } 53 | 54 | /* Tomorrow Aqua */ 55 | .hljs-title, 56 | .css .hljs-hexcolor { 57 | color: #99ffff; 58 | } 59 | 60 | /* Tomorrow Blue */ 61 | .hljs-function, 62 | .python .hljs-decorator, 63 | .python .hljs-title, 64 | .ruby .hljs-function .hljs-title, 65 | .ruby .hljs-title .hljs-keyword, 66 | .perl .hljs-sub, 67 | .javascript .hljs-title, 68 | .coffeescript .hljs-title { 69 | color: #bbdaff; 70 | } 71 | 72 | /* Tomorrow Purple */ 73 | .hljs-keyword, 74 | .javascript .hljs-function { 75 | color: #ebbbff; 76 | } 77 | 78 | .hljs { 79 | display: block; 80 | overflow-x: auto; 81 | background: #002451; 82 | color: white; 83 | padding: 0.5em; 84 | -webkit-text-size-adjust: none; 85 | } 86 | 87 | .coffeescript .javascript, 88 | .javascript .xml, 89 | .tex .hljs-formula, 90 | .xml .javascript, 91 | .xml .vbscript, 92 | .xml .css, 93 | .xml .hljs-cdata { 94 | opacity: 0.5; 95 | } -------------------------------------------------------------------------------- /code/schema.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | CREATE TABLE account ( 4 | id SERIAL NOT NULL PRIMARY KEY 5 | , type VARCHAR(100) NOT NULL 6 | , number INTEGER NOT NULL 7 | , name VARCHAR(100) NOT NULL 8 | ); 9 | 10 | CREATE TABLE place_category( 11 | id SERIAL NOT NULL PRIMARY KEY 12 | , name VARCHAR(25) NOT NULL 13 | ); 14 | 15 | CREATE TABLE place ( 16 | id SERIAL NOT NULL PRIMARY KEY 17 | , name VARCHAR(100) NOT NULL UNIQUE 18 | , place_category_id INTEGER REFERENCES place_category(id) 19 | ); 20 | 21 | CREATE TABLE transaction ( 22 | id SERIAL NOT NULL PRIMARY KEY 23 | , date DATE NOT NULL 24 | , amount DOUBLE PRECISION NOT NULL -- These should be numerics, but opaleye doesn't support them well. 25 | , balance DOUBLE PRECISION NOT NULL -- Floating point numbers and money are bad, mmkay? 26 | , type VARCHAR(50) NOT NULL CHECK (type in 27 | ('visa','eftpos','foreign_currency_conversion_fee','atm_operator_fee' 28 | ,'atm_withdrawal','direct_credit','internet_transfer_credit' 29 | ,'internet_transfer_debit')) 30 | , place_id INTEGER REFERENCES place(id) 31 | , account_id INTEGER NOT NULL REFERENCES account(id) 32 | ); 33 | 34 | CREATE TABLE transaction_visa ( 35 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 36 | , purchase_date DATE NOT NULL 37 | , currency_code VARCHAR(10) NOT NULL 38 | , country_code VARCHAR(10) NOT NULL 39 | ); 40 | 41 | CREATE TABLE transaction_atm_operator_fee ( 42 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 43 | , atm_transaction_type VARCHAR(10) NOT NULL 44 | ); 45 | 46 | CREATE TABLE transaction_direct_credit ( 47 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 48 | , bsb INTEGER NOT NULL 49 | ); 50 | 51 | CREATE TABLE transaction_internet_transfer ( 52 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 53 | , account INTEGER NOT NULL 54 | , ref VARCHAR(25) NOT NULL 55 | ); 56 | 57 | COMMIT; 58 | -------------------------------------------------------------------------------- /code-classy/schema.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | CREATE TABLE account ( 4 | id SERIAL NOT NULL PRIMARY KEY 5 | , type VARCHAR(100) NOT NULL 6 | , number INTEGER NOT NULL 7 | , name VARCHAR(100) NOT NULL 8 | ); 9 | 10 | CREATE TABLE place_category( 11 | id SERIAL NOT NULL PRIMARY KEY 12 | , name VARCHAR(25) NOT NULL 13 | ); 14 | 15 | CREATE TABLE place ( 16 | id SERIAL NOT NULL PRIMARY KEY 17 | , name VARCHAR(100) NOT NULL UNIQUE 18 | , place_category_id INTEGER REFERENCES place_category(id) 19 | ); 20 | 21 | CREATE TABLE transaction ( 22 | id SERIAL NOT NULL PRIMARY KEY 23 | , date DATE NOT NULL 24 | , amount DOUBLE PRECISION NOT NULL -- These should be numerics, but opaleye doesn't support them well. 25 | , balance DOUBLE PRECISION NOT NULL -- Floating point numbers and money are bad, mmkay? 26 | , type VARCHAR(50) NOT NULL CHECK (type in 27 | ('visa','eftpos','foreign_currency_conversion_fee','atm_operator_fee' 28 | ,'atm_withdrawal','direct_credit','internet_transfer_credit' 29 | ,'internet_transfer_debit')) 30 | , place_id INTEGER REFERENCES place(id) 31 | , account_id INTEGER NOT NULL REFERENCES account(id) 32 | ); 33 | 34 | CREATE TABLE transaction_visa ( 35 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 36 | , purchase_date DATE NOT NULL 37 | , currency_code VARCHAR(10) NOT NULL 38 | , country_code VARCHAR(10) NOT NULL 39 | ); 40 | 41 | CREATE TABLE transaction_atm_operator_fee ( 42 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 43 | , atm_transaction_type VARCHAR(10) NOT NULL 44 | ); 45 | 46 | CREATE TABLE transaction_direct_credit ( 47 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 48 | , bsb INTEGER NOT NULL 49 | ); 50 | 51 | CREATE TABLE transaction_internet_transfer ( 52 | transaction_id INTEGER NOT NULL REFERENCES transaction(id) 53 | , account INTEGER NOT NULL 54 | , ref VARCHAR(25) NOT NULL 55 | ); 56 | 57 | COMMIT; 58 | -------------------------------------------------------------------------------- /slides/lib/css/solarized-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #fdf6e3; 12 | color: #657b83; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-doctype, 19 | .hljs-pi, 20 | .lisp .hljs-string, 21 | .hljs-javadoc { 22 | color: #93a1a1; 23 | } 24 | 25 | /* Solarized Green */ 26 | .hljs-keyword, 27 | .hljs-winutils, 28 | .method, 29 | .hljs-addition, 30 | .css .hljs-tag, 31 | .hljs-request, 32 | .hljs-status, 33 | .nginx .hljs-title { 34 | color: #859900; 35 | } 36 | 37 | /* Solarized Cyan */ 38 | .hljs-number, 39 | .hljs-command, 40 | .hljs-string, 41 | .hljs-tag .hljs-value, 42 | .hljs-rules .hljs-value, 43 | .hljs-phpdoc, 44 | .hljs-dartdoc, 45 | .tex .hljs-formula, 46 | .hljs-regexp, 47 | .hljs-hexcolor, 48 | .hljs-link_url { 49 | color: #2aa198; 50 | } 51 | 52 | /* Solarized Blue */ 53 | .hljs-title, 54 | .hljs-localvars, 55 | .hljs-chunk, 56 | .hljs-decorator, 57 | .hljs-built_in, 58 | .hljs-identifier, 59 | .vhdl .hljs-literal, 60 | .hljs-id, 61 | .css .hljs-function { 62 | color: #268bd2; 63 | } 64 | 65 | /* Solarized Yellow */ 66 | .hljs-attribute, 67 | .hljs-variable, 68 | .lisp .hljs-body, 69 | .smalltalk .hljs-number, 70 | .hljs-constant, 71 | .hljs-class .hljs-title, 72 | .hljs-parent, 73 | .hljs-type, 74 | .hljs-link_reference { 75 | color: #b58900; 76 | } 77 | 78 | /* Solarized Orange */ 79 | .hljs-preprocessor, 80 | .hljs-preprocessor .hljs-keyword, 81 | .hljs-pragma, 82 | .hljs-shebang, 83 | .hljs-symbol, 84 | .hljs-symbol .hljs-string, 85 | .diff .hljs-change, 86 | .hljs-special, 87 | .hljs-attr_selector, 88 | .hljs-subst, 89 | .hljs-cdata, 90 | .css .hljs-pseudo, 91 | .hljs-header { 92 | color: #cb4b16; 93 | } 94 | 95 | /* Solarized Red */ 96 | .hljs-deletion, 97 | .hljs-important { 98 | color: #dc322f; 99 | } 100 | 101 | /* Solarized Violet */ 102 | .hljs-link_label { 103 | color: #6c71c4; 104 | } 105 | 106 | .tex .hljs-formula { 107 | background: #eee8d5; 108 | } -------------------------------------------------------------------------------- /code/src/Db/PlaceCategory.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.PlaceCategory 8 | ( PlaceCategory'(PlaceCategory) 9 | , NewPlaceCategory 10 | , PlaceCategory 11 | , placeCategoryQuery 12 | , getPlaceCategory 13 | , insertPlaceCategory 14 | , placeCategoryId 15 | , placeCategoryName 16 | ) where 17 | 18 | import BasePrelude hiding (optional) 19 | 20 | import Control.Lens 21 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 22 | import Data.Text (Text) 23 | import Opaleye 24 | 25 | import Db.Internal 26 | 27 | data PlaceCategory' a b = PlaceCategory 28 | { _placeCategoryId :: a 29 | , _placeCategoryName :: b 30 | } deriving (Eq,Show) 31 | makeLenses ''PlaceCategory' 32 | 33 | type PlaceCategory = PlaceCategory' Int Text 34 | type PlaceCategoryColumn = PlaceCategory' (Column PGInt4) (Column PGText) 35 | 36 | makeAdaptorAndInstance "pPlaceCategory" ''PlaceCategory' 37 | 38 | type NewPlaceCategory = PlaceCategory' (Maybe Int) Text 39 | 40 | type NewPlaceCategoryColumn = PlaceCategory' (Maybe (Column PGInt4)) (Column PGText) 41 | 42 | placeCategoryTable :: Table NewPlaceCategoryColumn PlaceCategoryColumn 43 | placeCategoryTable = Table "place_category" $ pPlaceCategory PlaceCategory 44 | { _placeCategoryId = optional "id" 45 | , _placeCategoryName = required "name" 46 | } 47 | 48 | placeCategoryQuery :: Query PlaceCategoryColumn 49 | placeCategoryQuery = queryTable placeCategoryTable 50 | 51 | insertPlaceCategory :: NewPlaceCategory -> Db Int 52 | insertPlaceCategory = 53 | liftInsertReturningFirst placeCategoryTable (view placeCategoryId) . packNew 54 | 55 | getPlaceCategory :: Int -> Db (Maybe PlaceCategory) 56 | getPlaceCategory i = liftQueryFirst $ proc () -> do 57 | p <- placeCategoryQuery -< () 58 | restrict -< p^.placeCategoryId .== pgInt4 i 59 | returnA -< p 60 | 61 | packNew :: NewPlaceCategory -> NewPlaceCategoryColumn 62 | packNew = pPlaceCategory PlaceCategory 63 | { _placeCategoryId = fmap pgInt4 64 | , _placeCategoryName = pgStrictText 65 | } 66 | -------------------------------------------------------------------------------- /code-classy/src/Db/PlaceCategory.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.PlaceCategory 8 | ( PlaceCategory'(PlaceCategory) 9 | , NewPlaceCategory 10 | , PlaceCategory 11 | , placeCategoryQuery 12 | , getPlaceCategory 13 | , insertPlaceCategory 14 | , placeCategoryId 15 | , placeCategoryName 16 | ) where 17 | 18 | import BasePrelude hiding (optional) 19 | 20 | import Control.Lens 21 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 22 | import Data.Text (Text) 23 | import Opaleye 24 | 25 | import Db.Internal 26 | 27 | data PlaceCategory' a b = PlaceCategory 28 | { _placeCategoryId :: a 29 | , _placeCategoryName :: b 30 | } deriving (Eq,Show) 31 | makeLenses ''PlaceCategory' 32 | 33 | type PlaceCategory = PlaceCategory' Int Text 34 | type PlaceCategoryColumn = PlaceCategory' (Column PGInt4) (Column PGText) 35 | 36 | makeAdaptorAndInstance "pPlaceCategory" ''PlaceCategory' 37 | 38 | type NewPlaceCategory = PlaceCategory' (Maybe Int) Text 39 | 40 | type NewPlaceCategoryColumn = PlaceCategory' (Maybe (Column PGInt4)) (Column PGText) 41 | 42 | placeCategoryTable :: Table NewPlaceCategoryColumn PlaceCategoryColumn 43 | placeCategoryTable = Table "place_category" $ pPlaceCategory PlaceCategory 44 | { _placeCategoryId = optional "id" 45 | , _placeCategoryName = required "name" 46 | } 47 | 48 | placeCategoryQuery :: Query PlaceCategoryColumn 49 | placeCategoryQuery = queryTable placeCategoryTable 50 | 51 | insertPlaceCategory :: CanDb c e m => NewPlaceCategory -> m Int 52 | insertPlaceCategory = 53 | liftInsertReturningFirst placeCategoryTable (view placeCategoryId) . packNew 54 | 55 | getPlaceCategory :: CanDb c e m => Int -> m (Maybe PlaceCategory) 56 | getPlaceCategory i = liftQueryFirst $ proc () -> do 57 | p <- placeCategoryQuery -< () 58 | restrict -< p^.placeCategoryId .== pgInt4 i 59 | returnA -< p 60 | 61 | packNew :: NewPlaceCategory -> NewPlaceCategoryColumn 62 | packNew = pPlaceCategory PlaceCategory 63 | { _placeCategoryId = fmap pgInt4 64 | , _placeCategoryName = pgStrictText 65 | } 66 | -------------------------------------------------------------------------------- /slides/lib/css/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #333; 31 | font-weight: bold; 32 | } 33 | 34 | .hljs-number, 35 | .hljs-hexcolor, 36 | .ruby .hljs-constant { 37 | color: #008080; 38 | } 39 | 40 | .hljs-string, 41 | .hljs-tag .hljs-value, 42 | .hljs-phpdoc, 43 | .hljs-dartdoc, 44 | .tex .hljs-formula { 45 | color: #d14; 46 | } 47 | 48 | .hljs-title, 49 | .hljs-id, 50 | .scss .hljs-preprocessor { 51 | color: #900; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-list .hljs-keyword, 56 | .hljs-subst { 57 | font-weight: normal; 58 | } 59 | 60 | .hljs-class .hljs-title, 61 | .hljs-type, 62 | .vhdl .hljs-literal, 63 | .tex .hljs-command { 64 | color: #458; 65 | font-weight: bold; 66 | } 67 | 68 | .hljs-tag, 69 | .hljs-tag .hljs-title, 70 | .hljs-rules .hljs-property, 71 | .django .hljs-tag .hljs-keyword { 72 | color: #000080; 73 | font-weight: normal; 74 | } 75 | 76 | .hljs-attribute, 77 | .hljs-variable, 78 | .lisp .hljs-body { 79 | color: #008080; 80 | } 81 | 82 | .hljs-regexp { 83 | color: #009926; 84 | } 85 | 86 | .hljs-symbol, 87 | .ruby .hljs-symbol .hljs-string, 88 | .lisp .hljs-keyword, 89 | .clojure .hljs-keyword, 90 | .scheme .hljs-keyword, 91 | .tex .hljs-special, 92 | .hljs-prompt { 93 | color: #990073; 94 | } 95 | 96 | .hljs-built_in { 97 | color: #0086b3; 98 | } 99 | 100 | .hljs-preprocessor, 101 | .hljs-pragma, 102 | .hljs-pi, 103 | .hljs-doctype, 104 | .hljs-shebang, 105 | .hljs-cdata { 106 | color: #999; 107 | font-weight: bold; 108 | } 109 | 110 | .hljs-deletion { 111 | background: #fdd; 112 | } 113 | 114 | .hljs-addition { 115 | background: #dfd; 116 | } 117 | 118 | .diff .hljs-change { 119 | background: #0086b3; 120 | } 121 | 122 | .hljs-chunk { 123 | color: #aaa; 124 | } -------------------------------------------------------------------------------- /slides/css/bens.css: -------------------------------------------------------------------------------- 1 | #title-text { 2 | background-color: #FFF; 3 | background-color: rgba(255, 255, 255, 0.6); 4 | padding: 20px; 5 | border-radius: 25px; 6 | } 7 | 8 | #title-text h1 , #title-text h3 , #title-text p { 9 | /* color: #52676d; */ 10 | color: #3e4e53; 11 | } 12 | 13 | code pre { 14 | background-color: #fff !important; 15 | } 16 | 17 | .reveal pre code { 18 | background-color: #f8f8f8; 19 | } 20 | 21 | .reveal .table-monad { 22 | border: 1px solid; 23 | } 24 | 25 | .reveal .table-centered td , 26 | .reveal .table-centered th { 27 | text-align: center; 28 | } 29 | 30 | .reveal img.img-no-border { 31 | border: 0px; 32 | box-shadow: 0 0 0 rgba(0,0,0,0); 33 | } 34 | 35 | .reveal pre.highlight { 36 | zoom: 1.2; 37 | } 38 | 39 | .reveal .table-monad td { 40 | text-align: center; 41 | border: 1px solid; 42 | color: black; 43 | } 44 | .reveal .slides section .fragment.highlight-red.visible span { 45 | color: #ff2c2d !important; 46 | } 47 | 48 | .table-monad.highlight-red.visible.current-fragment , .table-monad td.highlight-red.visible.current-fragment { 49 | background: #f47e7d; 50 | color: black !important; 51 | } 52 | 53 | .reveal section .table-monad.fragment.current-visible.visible , 54 | .reveal section .table-monad *.fragment.highlight-red.visible { 55 | color: black; 56 | } 57 | 58 | *.fragment.fade-out.fade-out-no-display.visible { 59 | display: none; 60 | } 61 | 62 | *.fragment.fade-in-no-display { 63 | display: none; 64 | } 65 | *.fragment.fade-in-no-display.visible { 66 | display: inline; 67 | } 68 | 69 | .types-walkthrough *.fragment.fade-out.visible { 70 | display: none; 71 | } 72 | 73 | .types-walkthrough *.fragment.current-visible.current-fragment { 74 | display: block; 75 | } 76 | 77 | .types-walkthrough *.fragment.current-visible { 78 | display: none; 79 | } 80 | 81 | .table-monad .table-monad { 82 | width: 100%; 83 | } 84 | 85 | .monad-colour1 { 86 | background-color: #b5d045; 87 | } 88 | 89 | .monad-colour2 { 90 | background-color: #389090; 91 | } 92 | 93 | .monad-colour3 { 94 | background-color: #fb8335; 95 | } 96 | 97 | .monad-colour4 { 98 | background-color: #e0c7a8; 99 | } 100 | 101 | .monad-colour5 { 102 | background-color: #81c0c5; 103 | } -------------------------------------------------------------------------------- /slides/css/theme/source/blood.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Blood theme for reveal.js 3 | * Author: Walther http://github.com/Walther 4 | * 5 | * Designed to be used with highlight.js theme 6 | * "monokai_sublime.css" available from 7 | * https://github.com/isagalaev/highlight.js/ 8 | * 9 | * For other themes, change $codeBackground accordingly. 10 | * 11 | */ 12 | 13 | // Default mixins and settings ----------------- 14 | @import "../template/mixins"; 15 | @import "../template/settings"; 16 | // --------------------------------------------- 17 | 18 | // Include theme-specific fonts 19 | 20 | @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); 21 | 22 | // Colors used in the theme 23 | $blood: #a23; 24 | $coal: #222; 25 | $codeBackground: #23241f; 26 | 27 | // Main text 28 | $mainFont: Ubuntu, 'sans-serif'; 29 | $mainFontSize: 36px; 30 | $mainColor: #eee; 31 | 32 | // Headings 33 | $headingFont: Ubuntu, 'sans-serif'; 34 | $headingTextShadow: 2px 2px 2px $coal; 35 | 36 | // h1 shadow, borrowed humbly from 37 | // (c) Default theme by Hakim El Hattab 38 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); 39 | 40 | // Links 41 | $linkColor: $blood; 42 | $linkColorHover: lighten( $linkColor, 20% ); 43 | 44 | // Text selection 45 | $selectionBackgroundColor: $blood; 46 | $selectionColor: #fff; 47 | 48 | // Background generator 49 | @mixin bodyBackground() { 50 | @include radial-gradient( $coal, lighten( $coal, 25% ) ); 51 | } 52 | 53 | // Theme template ------------------------------ 54 | @import "../template/theme"; 55 | // --------------------------------------------- 56 | 57 | // some overrides after theme template import 58 | 59 | .reveal p { 60 | font-weight: 300; 61 | text-shadow: 1px 1px $coal; 62 | } 63 | 64 | .reveal h1, 65 | .reveal h2, 66 | .reveal h3, 67 | .reveal h4, 68 | .reveal h5, 69 | .reveal h6 { 70 | font-weight: 700; 71 | } 72 | 73 | .reveal a, 74 | .reveal a:hover { 75 | text-shadow: 2px 2px 2px #000; 76 | } 77 | 78 | .reveal small a, 79 | .reveal small a:hover { 80 | text-shadow: 1px 1px 1px #000; 81 | } 82 | 83 | .reveal p code { 84 | background-color: $codeBackground; 85 | display: inline-block; 86 | border-radius: 7px; 87 | } 88 | 89 | .reveal small code { 90 | vertical-align: baseline; 91 | } -------------------------------------------------------------------------------- /code-classy/tests/DbTests/Internal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE ConstraintKinds #-} 4 | module DbTests.Internal where 5 | 6 | import BasePrelude 7 | 8 | import Control.Lens ((^.)) 9 | import Control.Monad.Random 10 | import Control.Monad.Except (ExceptT,runExceptT) 11 | import Control.Monad.Reader (ReaderT,runReaderT) 12 | import qualified Data.ByteString as B 13 | import qualified Data.ByteString.Char8 as B8 14 | import Database.PostgreSQL.Simple 15 | import Database.PostgreSQL.Simple.Types 16 | import Test.Tasty.HUnit 17 | 18 | import Db 19 | 20 | rnd :: (RandomGen g) => Rand g Char 21 | rnd = getRandomR ('a','z') 22 | 23 | dbName :: IO String 24 | dbName = ("transactions_test_" <>) <$> evalRandIO (sequence (replicate 10 rnd)) 25 | 26 | assertDbResult :: Either DbError a -> (a -> Assertion) -> Assertion 27 | assertDbResult e f = either (error . ("DB Failed: " <>) . show) f e 28 | 29 | roundTripTest 30 | :: (Eq a, Show a) 31 | => String 32 | -> (n -> ExceptT DbError (ReaderT DbEnv IO) i) 33 | -> (i -> ExceptT DbError (ReaderT DbEnv IO) (Maybe a)) 34 | -> (n -> i -> a) 35 | -> n 36 | -> Assertion 37 | roundTripTest testName insertN queryA newToExisting new = 38 | withDb testName $ \ e -> do 39 | res <- runDb e $ do 40 | i <- insertN new 41 | a <- queryA i 42 | pure (i,a) 43 | assertDbResult res $ \ (i,a) -> Just (newToExisting new i) @=? a 44 | 45 | runDb :: DbEnv -> ExceptT DbError (ReaderT DbEnv IO) a -> IO (Either DbError a) 46 | runDb env = flip runReaderT env . runExceptT 47 | 48 | withDb :: String -> (DbEnv -> Assertion) -> Assertion 49 | withDb testName f = do 50 | pc <- connectPostgreSQL "dbname=postgres" 51 | n <- dbName 52 | let nb = B8.pack n 53 | bracket 54 | (setup pc nb) 55 | (cleanup pc nb) 56 | f 57 | 58 | where 59 | setup pc nb = do 60 | void . execute_ pc . Query $ "CREATE DATABASE " <> nb 61 | tc <- connectPostgreSQL ("dbname=" <> nb) 62 | schemaSql <- B.readFile "schema.sql" 63 | void . execute_ tc . Query $ schemaSql 64 | dataSql <- B.readFile $ "tests/sql/" <> testName <> ".sql" 65 | void . execute_ tc . Query $ dataSql 66 | pure (DbEnv tc) 67 | 68 | cleanup pc nb tc = do 69 | close $ tc^.dbEnvConnection 70 | void . execute_ pc . Query $ "DROP DATABASE " <> nb 71 | -------------------------------------------------------------------------------- /slides/lib/css/zenburn.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 4 | based on dark.css by Ivan Sagalaev 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; padding: 0.5em; 10 | background: #3F3F3F; 11 | color: #DCDCDC; 12 | } 13 | 14 | .hljs-keyword, 15 | .hljs-tag, 16 | .css .hljs-class, 17 | .css .hljs-id, 18 | .lisp .hljs-title, 19 | .nginx .hljs-title, 20 | .hljs-request, 21 | .hljs-status, 22 | .clojure .hljs-attribute { 23 | color: #E3CEAB; 24 | } 25 | 26 | .django .hljs-template_tag, 27 | .django .hljs-variable, 28 | .django .hljs-filter .hljs-argument { 29 | color: #DCDCDC; 30 | } 31 | 32 | .hljs-number, 33 | .hljs-date { 34 | color: #8CD0D3; 35 | } 36 | 37 | .dos .hljs-envvar, 38 | .dos .hljs-stream, 39 | .hljs-variable, 40 | .apache .hljs-sqbracket { 41 | color: #EFDCBC; 42 | } 43 | 44 | .dos .hljs-flow, 45 | .diff .hljs-change, 46 | .python .exception, 47 | .python .hljs-built_in, 48 | .hljs-literal, 49 | .tex .hljs-special { 50 | color: #EFEFAF; 51 | } 52 | 53 | .diff .hljs-chunk, 54 | .hljs-subst { 55 | color: #8F8F8F; 56 | } 57 | 58 | .dos .hljs-keyword, 59 | .python .hljs-decorator, 60 | .hljs-title, 61 | .haskell .hljs-type, 62 | .diff .hljs-header, 63 | .ruby .hljs-class .hljs-parent, 64 | .apache .hljs-tag, 65 | .nginx .hljs-built_in, 66 | .tex .hljs-command, 67 | .hljs-prompt { 68 | color: #efef8f; 69 | } 70 | 71 | .dos .hljs-winutils, 72 | .ruby .hljs-symbol, 73 | .ruby .hljs-symbol .hljs-string, 74 | .ruby .hljs-string { 75 | color: #DCA3A3; 76 | } 77 | 78 | .diff .hljs-deletion, 79 | .hljs-string, 80 | .hljs-tag .hljs-value, 81 | .hljs-preprocessor, 82 | .hljs-pragma, 83 | .hljs-built_in, 84 | .sql .hljs-aggregate, 85 | .hljs-javadoc, 86 | .smalltalk .hljs-class, 87 | .smalltalk .hljs-localvars, 88 | .smalltalk .hljs-array, 89 | .css .hljs-rules .hljs-value, 90 | .hljs-attr_selector, 91 | .hljs-pseudo, 92 | .apache .hljs-cbracket, 93 | .tex .hljs-formula, 94 | .coffeescript .hljs-attribute { 95 | color: #CC9393; 96 | } 97 | 98 | .hljs-shebang, 99 | .diff .hljs-addition, 100 | .hljs-comment, 101 | .java .hljs-annotation, 102 | .hljs-template_comment, 103 | .hljs-pi, 104 | .hljs-doctype { 105 | color: #7F9F7F; 106 | } 107 | 108 | .coffeescript .javascript, 109 | .javascript .xml, 110 | .tex .hljs-formula, 111 | .xml .javascript, 112 | .xml .vbscript, 113 | .xml .css, 114 | .xml .hljs-cdata { 115 | opacity: 0.5; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /code/transaction-importer.cabal: -------------------------------------------------------------------------------- 1 | name: transaction-importer 2 | version: 0.1.0.0 3 | synopsis: Imports transactions from a Suncorp transaction CSV export into a postgres DB. 4 | description: This is example code for a BFPG talk that I'm giving about configuration 5 | and error handling in Haskell. 6 | license: MIT 7 | license-file: LICENSE 8 | author: Ben Kolera 9 | maintainer: ben.kolera@gmail.com 10 | build-type: Simple 11 | cabal-version: >=1.10 12 | 13 | library 14 | exposed-modules: App,Csv,Db,Types 15 | other-modules: 16 | Db.Account 17 | , Db.Internal 18 | , Db.Place 19 | , Db.PlaceCategory 20 | , Db.Transaction 21 | , Db.TransactionDirectCredit 22 | , Db.TransactionAtmOperatorFee 23 | , Db.TransactionInternetTransfer 24 | , Db.TransactionVisa 25 | , Utils 26 | 27 | build-depends: 28 | base == 4.8.* 29 | , base-prelude 30 | , bifunctors >= 5 && < 5.1 31 | , bytestring == 0.10.* 32 | , cassava == 0.4.* 33 | , either == 4.4.* 34 | , errors == 2.0.* 35 | , exceptions == 0.6.* 36 | , lens == 4.11.* 37 | , mtl == 2.2.* 38 | , opaleye == 0.3.* 39 | , parsec == 3.1.* 40 | , postgresql-simple == 0.4.* 41 | , product-profunctors == 0.6.* 42 | , text == 1.2.* 43 | , time == 1.5.* 44 | , UtilityTM == 0.0.* 45 | , validation == 0.5.* 46 | , vector == 0.10.* 47 | hs-source-dirs: src 48 | default-language: Haskell2010 49 | 50 | executable transaction-importer 51 | main-is: Main.hs 52 | build-depends: 53 | base == 4.8.* 54 | , base-prelude 55 | , configurator == 0.3.* 56 | , errors 57 | , postgresql-simple 58 | , transaction-importer 59 | 60 | default-language: Haskell2010 61 | test-suite test 62 | type: exitcode-stdio-1.0 63 | default-language: Haskell2010 64 | hs-source-dirs: tests 65 | main-is: Main.hs 66 | build-depends: 67 | base 68 | , transaction-importer 69 | , base-prelude 70 | , bytestring 71 | , errors 72 | , lens 73 | , mtl 74 | , MonadRandom 75 | , postgresql-simple 76 | , tasty 77 | , tasty-hunit 78 | , time 79 | , UtilityTM 80 | -------------------------------------------------------------------------------- /code/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Main where 4 | 5 | import BasePrelude hiding (left) 6 | import Data.Configurator 7 | import Data.Configurator.Types (Config) 8 | import Database.PostgreSQL.Simple (ConnectInfo (..), Connection, connect, 9 | defaultConnectInfo) 10 | 11 | import App 12 | import Control.Error 13 | import Csv (CsvError (..)) 14 | import Db (DbEnv (..), DbError (..)) 15 | 16 | main :: IO () 17 | main = runScript $ do 18 | args <- scriptIO $ getArgs 19 | env <- loadConfig 20 | fn <- headMay args ?? usage 21 | e <- scriptIO . runApp env . loadAndInsert $ fn 22 | scriptIO . putStrLn . either appErrorString successString $ e 23 | 24 | loadConfig :: Script AppEnv 25 | loadConfig = fmapLT ("There were problems loading the config:\n" <>) $ do 26 | conf <- scriptIO $ load [Required "app.cfg"] 27 | conn <- scriptIO $ requireConnection conf 28 | pure $ AppEnv (DbEnv conn) 29 | 30 | requireConnection :: Config -> IO Connection 31 | requireConnection c = do 32 | host <- lookupDefault (connectHost defaultConnectInfo) c "db.host" 33 | port <- lookupDefault (connectPort defaultConnectInfo) c "db.port" 34 | user <- lookupDefault (connectUser defaultConnectInfo) c "db.user" 35 | pass <- lookupDefault (connectPassword defaultConnectInfo) c "db.password" 36 | db <- lookupDefault (connectDatabase defaultConnectInfo) c "db.database" 37 | connect (ConnectInfo host port user pass db) 38 | 39 | -- This can also be achieved with the prisms from Control.Lens 40 | appErrorString :: AppError -> String 41 | appErrorString (AppDbError db) = dbErrorString db 42 | appErrorString (AppCsvError csv) = csvErrorString csv 43 | 44 | dbErrorString :: DbError -> String 45 | dbErrorString (DbQueryError q) = "There was a problem with one of the sql queries: " <> show q 46 | dbErrorString (DbSqlError s) = "There was a problem connecting to the database: " <> show s 47 | 48 | csvErrorString :: CsvError -> String 49 | csvErrorString (CsvIoError i) = "There was file error reading the csv file: " <> show i 50 | csvErrorString (CsvHeaderParseError s) = "Failed parsing the csv header to get the account number: " <> s 51 | csvErrorString (CsvDecodeErrors s) = unlines $ "Some csv lines failed to parse:" : s 52 | 53 | successString :: [Int] -> String 54 | successString ids = "Great Success! " <> (show . length $ ids) <> " rows inserted" 55 | 56 | usage :: String 57 | usage = "missing file name: run with cabal run -- " 58 | -------------------------------------------------------------------------------- /code-classy/transaction-importer.cabal: -------------------------------------------------------------------------------- 1 | name: transaction-importer 2 | version: 0.1.0.0 3 | synopsis: Imports transactions from a Suncorp transaction CSV export into a postgres DB. 4 | description: This is example code for a BFPG talk that I'm giving about configuration 5 | and error handling in Haskell. 6 | license: MIT 7 | license-file: LICENSE 8 | author: Ben Kolera 9 | maintainer: ben.kolera@gmail.com 10 | build-type: Simple 11 | cabal-version: >=1.10 12 | 13 | library 14 | exposed-modules: App,Csv,Db,Types 15 | other-modules: 16 | Db.Account 17 | , Db.Internal 18 | , Db.Place 19 | , Db.PlaceCategory 20 | , Db.Transaction 21 | , Db.TransactionDirectCredit 22 | , Db.TransactionAtmOperatorFee 23 | , Db.TransactionInternetTransfer 24 | , Db.TransactionVisa 25 | , Utils 26 | 27 | build-depends: 28 | base == 4.8.* 29 | , base-prelude 30 | , bifunctors >= 5 && < 5.1 31 | , bytestring == 0.10.* 32 | , cassava == 0.4.* 33 | , either == 4.4.* 34 | , errors == 2.0.* 35 | , exceptions == 0.6.* 36 | , hoist-error == 0.1.* 37 | , lens == 4.11.* 38 | , mtl == 2.2.* 39 | , opaleye == 0.3.* 40 | , parsec == 3.1.* 41 | , postgresql-simple == 0.4.* 42 | , product-profunctors == 0.6.* 43 | , text == 1.2.* 44 | , time == 1.5.* 45 | , UtilityTM == 0.0.* 46 | , validation == 0.5.* 47 | , vector == 0.10.* 48 | hs-source-dirs: src 49 | default-language: Haskell2010 50 | 51 | executable transaction-importer 52 | main-is: Main.hs 53 | build-depends: 54 | base == 4.8.* 55 | , base-prelude 56 | , configurator == 0.3.* 57 | , errors 58 | , mtl == 2.2.* 59 | , postgresql-simple 60 | , transaction-importer 61 | 62 | default-language: Haskell2010 63 | test-suite test 64 | type: exitcode-stdio-1.0 65 | default-language: Haskell2010 66 | hs-source-dirs: tests 67 | main-is: Main.hs 68 | build-depends: 69 | base 70 | , transaction-importer 71 | , base-prelude 72 | , bytestring 73 | , errors 74 | , lens 75 | , mtl 76 | , MonadRandom 77 | , postgresql-simple 78 | , tasty 79 | , tasty-hunit 80 | , time 81 | , UtilityTM 82 | -------------------------------------------------------------------------------- /code/src/Db/Place.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Place 8 | ( Place'(Place) 9 | , NewPlace 10 | , Place 11 | , placeQuery 12 | , allPlaces 13 | , getPlace 14 | , insertPlace 15 | , upsertPlaceByName 16 | , placeId 17 | , placeName 18 | , placePlaceCategoryId 19 | ) where 20 | 21 | import BasePrelude hiding (optional) 22 | 23 | import Control.Lens 24 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 25 | import Data.Text (Text) 26 | import Opaleye 27 | 28 | import Db.Internal 29 | 30 | data Place' a b c = Place 31 | { _placeId :: a 32 | , _placeName :: b 33 | , _placePlaceCategoryId :: c 34 | } deriving (Eq,Show) 35 | makeLenses ''Place' 36 | 37 | type Place = Place' Int Text (Maybe Int) 38 | type PlaceColumn = Place' (Column PGInt4) (Column PGText) (Column (Nullable PGInt4)) 39 | 40 | makeAdaptorAndInstance "pPlace" ''Place' 41 | 42 | type NewPlace = Place' (Maybe Int) Text (Maybe Int) 43 | 44 | type NewPlaceColumn = Place' (Maybe (Column PGInt4)) (Column PGText) (Column (Nullable PGInt4)) 45 | 46 | placeTable :: Table NewPlaceColumn PlaceColumn 47 | placeTable = Table "place" $ pPlace Place 48 | { _placeId = optional "id" 49 | , _placeName = required "name" 50 | , _placePlaceCategoryId = required "place_category_id" 51 | } 52 | 53 | placeQuery :: Query PlaceColumn 54 | placeQuery = queryTable placeTable 55 | 56 | allPlaces :: Db [Place] 57 | allPlaces = liftQuery placeQuery 58 | 59 | insertPlace :: NewPlace -> Db Int 60 | insertPlace = 61 | liftInsertReturningFirst placeTable (view placeId) . packNew 62 | 63 | findPlaceByName :: Text -> Db (Maybe Place) 64 | findPlaceByName n = liftQueryFirst $ proc () -> do 65 | a <- placeQuery -< () 66 | restrict -< a^.placeName .== pgStrictText n 67 | returnA -< a 68 | 69 | upsertPlaceByName :: NewPlace -> Db Int 70 | upsertPlaceByName na = do 71 | a <- findPlaceByName (na^.placeName) 72 | maybe (insertPlace na) (pure . (^.placeId)) a 73 | 74 | getPlace :: Int -> Db (Maybe Place) 75 | getPlace i = liftQueryFirst $ proc () -> do 76 | p <- placeQuery -< () 77 | restrict -< p^.placeId .== pgInt4 i 78 | returnA -< p 79 | 80 | packNew :: NewPlace -> NewPlaceColumn 81 | packNew = pPlace Place 82 | { _placeId = fmap pgInt4 83 | , _placeName = pgStrictText 84 | , _placePlaceCategoryId = maybeToNullable . fmap pgInt4 85 | } 86 | -------------------------------------------------------------------------------- /code-classy/src/Db/Place.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Place 8 | ( Place'(Place) 9 | , NewPlace 10 | , Place 11 | , placeQuery 12 | , allPlaces 13 | , getPlace 14 | , insertPlace 15 | , upsertPlaceByName 16 | , placeId 17 | , placeName 18 | , placePlaceCategoryId 19 | ) where 20 | 21 | import BasePrelude hiding (optional) 22 | 23 | import Control.Lens 24 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 25 | import Data.Text (Text) 26 | import Opaleye 27 | 28 | import Db.Internal 29 | 30 | data Place' a b c = Place 31 | { _placeId :: a 32 | , _placeName :: b 33 | , _placePlaceCategoryId :: c 34 | } deriving (Eq,Show) 35 | makeLenses ''Place' 36 | 37 | type Place = Place' Int Text (Maybe Int) 38 | type PlaceColumn = Place' (Column PGInt4) (Column PGText) (Column (Nullable PGInt4)) 39 | 40 | makeAdaptorAndInstance "pPlace" ''Place' 41 | 42 | type NewPlace = Place' (Maybe Int) Text (Maybe Int) 43 | 44 | type NewPlaceColumn = Place' (Maybe (Column PGInt4)) (Column PGText) (Column (Nullable PGInt4)) 45 | 46 | placeTable :: Table NewPlaceColumn PlaceColumn 47 | placeTable = Table "place" $ pPlace Place 48 | { _placeId = optional "id" 49 | , _placeName = required "name" 50 | , _placePlaceCategoryId = required "place_category_id" 51 | } 52 | 53 | placeQuery :: Query PlaceColumn 54 | placeQuery = queryTable placeTable 55 | 56 | allPlaces :: CanDb c e m => m [Place] 57 | allPlaces = liftQuery placeQuery 58 | 59 | insertPlace :: CanDb c e m => NewPlace -> m Int 60 | insertPlace = 61 | liftInsertReturningFirst placeTable (view placeId) . packNew 62 | 63 | findPlaceByName :: CanDb c e m => Text -> m (Maybe Place) 64 | findPlaceByName n = liftQueryFirst $ proc () -> do 65 | a <- placeQuery -< () 66 | restrict -< a^.placeName .== pgStrictText n 67 | returnA -< a 68 | 69 | upsertPlaceByName :: CanDb c e m => NewPlace -> m Int 70 | upsertPlaceByName na = do 71 | a <- findPlaceByName (na^.placeName) 72 | maybe (insertPlace na) (pure . (^.placeId)) a 73 | 74 | getPlace :: CanDb c e m => Int -> m (Maybe Place) 75 | getPlace i = liftQueryFirst $ proc () -> do 76 | p <- placeQuery -< () 77 | restrict -< p^.placeId .== pgInt4 i 78 | returnA -< p 79 | 80 | packNew :: NewPlace -> NewPlaceColumn 81 | packNew = pPlace Place 82 | { _placeId = fmap pgInt4 83 | , _placeName = pgStrictText 84 | , _placePlaceCategoryId = maybeToNullable . fmap pgInt4 85 | } 86 | -------------------------------------------------------------------------------- /code-classy/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Main where 4 | 5 | import BasePrelude hiding (left) 6 | import Control.Monad.Except (ExceptT,runExceptT) 7 | import Control.Monad.Reader (ReaderT,runReaderT) 8 | import Data.Configurator 9 | import Data.Configurator.Types (Config) 10 | import Database.PostgreSQL.Simple (ConnectInfo (..), Connection, connect, 11 | defaultConnectInfo) 12 | 13 | import App 14 | import Control.Error 15 | import Csv (CsvError (..)) 16 | import Db (DbEnv (..), DbError (..)) 17 | 18 | main :: IO () 19 | main = runScript $ do 20 | args <- scriptIO $ getArgs 21 | env <- loadConfig 22 | fn <- headMay args ?? usage 23 | e <- scriptIO . runApp env . loadAndInsert $ fn 24 | scriptIO . putStrLn . either appErrorString successString $ e 25 | where 26 | runApp :: AppEnv -> ExceptT AppError (ReaderT AppEnv IO) a -> IO (Either AppError a) 27 | runApp env = flip runReaderT env . runExceptT 28 | 29 | loadConfig :: Script AppEnv 30 | loadConfig = fmapLT ("There were problems loading the config:\n" <>) $ do 31 | conf <- scriptIO $ load [Required "app.cfg"] 32 | conn <- scriptIO $ requireConnection conf 33 | pure $ AppEnv (DbEnv conn) 34 | 35 | requireConnection :: Config -> IO Connection 36 | requireConnection c = do 37 | host <- lookupDefault (connectHost defaultConnectInfo) c "db.host" 38 | port <- lookupDefault (connectPort defaultConnectInfo) c "db.port" 39 | user <- lookupDefault (connectUser defaultConnectInfo) c "db.user" 40 | pass <- lookupDefault (connectPassword defaultConnectInfo) c "db.password" 41 | db <- lookupDefault (connectDatabase defaultConnectInfo) c "db.database" 42 | connect (ConnectInfo host port user pass db) 43 | 44 | -- This can also be achieved with the prisms from Control.Lens 45 | appErrorString :: AppError -> String 46 | appErrorString (AppDbError db) = dbErrorString db 47 | appErrorString (AppCsvError csv) = csvErrorString csv 48 | 49 | dbErrorString :: DbError -> String 50 | dbErrorString (DbQueryError q) = "There was a problem with one of the sql queries: " <> show q 51 | dbErrorString (DbSqlError s) = "There was a problem connecting to the database: " <> show s 52 | 53 | csvErrorString :: CsvError -> String 54 | csvErrorString (CsvIoError i) = "There was file error reading the csv file: " <> show i 55 | csvErrorString (CsvHeaderParseError s) = "Failed parsing the csv header to get the account number: " <> s 56 | csvErrorString (CsvDecodeErrors s) = unlines $ "Some csv lines failed to parse:" : s 57 | 58 | successString :: [Int] -> String 59 | successString ids = "Great Success! " <> (show . length $ ids) <> " rows inserted" 60 | 61 | usage :: String 62 | usage = "missing file name: run with cabal run -- " 63 | -------------------------------------------------------------------------------- /code/src/Db/Account.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Account 8 | ( Account'(Account) 9 | , NewAccount 10 | , Account 11 | , allAccounts 12 | , accountQuery 13 | , insertAccount 14 | , upsertAccountByNumber 15 | , getAccount 16 | , accountId 17 | , accountName 18 | , accountNumber 19 | , accountType 20 | ) where 21 | 22 | import BasePrelude hiding (optional) 23 | 24 | import Control.Lens 25 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 26 | import Data.Text (Text) 27 | import Opaleye 28 | 29 | import Db.Internal 30 | 31 | data Account' a b c d = Account 32 | { _accountId :: a 33 | , _accountType :: b 34 | , _accountNumber :: c 35 | , _accountName :: d 36 | } deriving (Eq,Show) 37 | makeLenses ''Account' 38 | 39 | type Account = Account' Int Text Int Text 40 | type AccountColumn = Account' 41 | (Column PGInt4) 42 | (Column PGText) 43 | (Column PGInt4) 44 | (Column PGText) 45 | 46 | makeAdaptorAndInstance "pAccount" ''Account' 47 | 48 | type NewAccount = Account' (Maybe Int) Text Int Text 49 | 50 | type NewAccountColumn = Account' 51 | (Maybe (Column PGInt4)) (Column PGText) (Column PGInt4) (Column PGText) 52 | 53 | accountTable :: Table NewAccountColumn AccountColumn 54 | accountTable = Table "account" $ pAccount Account 55 | { _accountId = optional "id" 56 | , _accountType = required "type" 57 | , _accountNumber = required "number" 58 | , _accountName = required "name" 59 | } 60 | 61 | accountQuery :: Query AccountColumn 62 | accountQuery = queryTable accountTable 63 | 64 | allAccounts :: Db [Account] 65 | allAccounts = liftQuery accountQuery 66 | 67 | getAccount :: Int -> Db (Maybe Account) 68 | getAccount i = liftQueryFirst $ proc () -> do 69 | a <- accountQuery -< () 70 | restrict -< a^.accountId .== pgInt4 i 71 | returnA -< a 72 | 73 | findAccountByNumber :: Int -> Db (Maybe Account) 74 | findAccountByNumber n = liftQueryFirst $ proc () -> do 75 | a <- accountQuery -< () 76 | restrict -< a^.accountNumber .== pgInt4 n 77 | returnA -< a 78 | 79 | upsertAccountByNumber :: NewAccount -> Db Int 80 | upsertAccountByNumber na = do 81 | a <- findAccountByNumber (na^.accountNumber) 82 | maybe (insertAccount na) (pure . (^.accountId)) a 83 | 84 | insertAccount :: NewAccount -> Db Int 85 | insertAccount = do 86 | liftInsertReturningFirst accountTable (view accountId) . packNew 87 | 88 | packNew :: NewAccount -> NewAccountColumn 89 | packNew = pAccount Account 90 | { _accountId = fmap pgInt4 91 | , _accountType = pgStrictText 92 | , _accountNumber = pgInt4 93 | , _accountName = pgStrictText 94 | } 95 | -------------------------------------------------------------------------------- /slides/test/test-markdown-element-attributes.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 4, 'found four slides' ); 9 | }); 10 | 11 | 12 | test( 'Attributes on element header in vertical slides', function() { 13 | strictEqual( document.querySelectorAll( '.reveal .slides section>section h2.fragment.fade-out' ).length, 1, 'found one vertical slide with class fragment.fade-out on header' ); 14 | strictEqual( document.querySelectorAll( '.reveal .slides section>section h2.fragment.shrink' ).length, 1, 'found one vertical slide with class fragment.shrink on header' ); 15 | }); 16 | 17 | test( 'Attributes on element paragraphs in vertical slides', function() { 18 | strictEqual( document.querySelectorAll( '.reveal .slides section>section p.fragment.grow' ).length, 2, 'found a vertical slide with two paragraphs with class fragment.grow' ); 19 | }); 20 | 21 | test( 'Attributes on element list items in vertical slides', function() { 22 | strictEqual( document.querySelectorAll( '.reveal .slides section>section li.fragment.roll-in' ).length, 3, 'found a vertical slide with three list items with class fragment.roll-in' ); 23 | }); 24 | 25 | test( 'Attributes on element paragraphs in horizontal slides', function() { 26 | strictEqual( document.querySelectorAll( '.reveal .slides section p.fragment.highlight-red' ).length, 4, 'found a horizontal slide with four paragraphs with class fragment.grow' ); 27 | }); 28 | test( 'Attributes on element list items in horizontal slides', function() { 29 | strictEqual( document.querySelectorAll( '.reveal .slides section li.fragment.highlight-green' ).length, 5, 'found a horizontal slide with five list items with class fragment.roll-in' ); 30 | }); 31 | test( 'Attributes on element list items in horizontal slides', function() { 32 | strictEqual( document.querySelectorAll( '.reveal .slides section img.reveal.stretch' ).length, 1, 'found a horizontal slide with stretched image, class img.reveal.stretch' ); 33 | }); 34 | 35 | test( 'Attributes on elements in vertical slides with default element attribute separator', function() { 36 | strictEqual( document.querySelectorAll( '.reveal .slides section h2.fragment.highlight-red' ).length, 2, 'found two h2 titles with fragment highlight-red in vertical slides with default element attribute separator' ); 37 | }); 38 | 39 | test( 'Attributes on elements in single slides with default element attribute separator', function() { 40 | strictEqual( document.querySelectorAll( '.reveal .slides section p.fragment.highlight-blue' ).length, 3, 'found three elements with fragment highlight-blue in single slide with default element attribute separator' ); 41 | }); 42 | 43 | } ); 44 | 45 | Reveal.initialize(); 46 | 47 | -------------------------------------------------------------------------------- /code-classy/src/Db/Account.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Account 8 | ( Account'(Account) 9 | , NewAccount 10 | , Account 11 | , allAccounts 12 | , accountQuery 13 | , insertAccount 14 | , upsertAccountByNumber 15 | , getAccount 16 | , accountId 17 | , accountName 18 | , accountNumber 19 | , accountType 20 | ) where 21 | 22 | import BasePrelude hiding (optional) 23 | 24 | import Control.Lens 25 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 26 | import Data.Text (Text) 27 | import Opaleye 28 | 29 | import Db.Internal 30 | 31 | data Account' a b c d = Account 32 | { _accountId :: a 33 | , _accountType :: b 34 | , _accountNumber :: c 35 | , _accountName :: d 36 | } deriving (Eq,Show) 37 | makeLenses ''Account' 38 | 39 | type Account = Account' Int Text Int Text 40 | type AccountColumn = Account' 41 | (Column PGInt4) 42 | (Column PGText) 43 | (Column PGInt4) 44 | (Column PGText) 45 | 46 | makeAdaptorAndInstance "pAccount" ''Account' 47 | 48 | type NewAccount = Account' (Maybe Int) Text Int Text 49 | 50 | type NewAccountColumn = Account' 51 | (Maybe (Column PGInt4)) (Column PGText) (Column PGInt4) (Column PGText) 52 | 53 | accountTable :: Table NewAccountColumn AccountColumn 54 | accountTable = Table "account" $ pAccount Account 55 | { _accountId = optional "id" 56 | , _accountType = required "type" 57 | , _accountNumber = required "number" 58 | , _accountName = required "name" 59 | } 60 | 61 | accountQuery :: Query AccountColumn 62 | accountQuery = queryTable accountTable 63 | 64 | allAccounts :: CanDb c e m => m [Account] 65 | allAccounts = liftQuery accountQuery 66 | 67 | getAccount :: CanDb c e m => Int -> m (Maybe Account) 68 | getAccount i = liftQueryFirst $ proc () -> do 69 | a <- accountQuery -< () 70 | restrict -< a^.accountId .== pgInt4 i 71 | returnA -< a 72 | 73 | findAccountByNumber :: CanDb c e m => Int -> m (Maybe Account) 74 | findAccountByNumber n = liftQueryFirst $ proc () -> do 75 | a <- accountQuery -< () 76 | restrict -< a^.accountNumber .== pgInt4 n 77 | returnA -< a 78 | 79 | upsertAccountByNumber :: CanDb c e m => NewAccount -> m Int 80 | upsertAccountByNumber na = do 81 | a <- findAccountByNumber (na^.accountNumber) 82 | maybe (insertAccount na) (pure . (^.accountId)) a 83 | 84 | insertAccount :: CanDb c e m => NewAccount -> m Int 85 | insertAccount = do 86 | liftInsertReturningFirst accountTable (view accountId) . packNew 87 | 88 | packNew :: NewAccount -> NewAccountColumn 89 | packNew = pAccount Account 90 | { _accountId = fmap pgInt4 91 | , _accountType = pgStrictText 92 | , _accountNumber = pgInt4 93 | , _accountName = pgStrictText 94 | } 95 | -------------------------------------------------------------------------------- /code/src/Db/TransactionDirectCredit.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionDirectCredit 8 | ( TransactionDirectCredit'(TransactionDirectCredit) 9 | , NewTransactionDirectCredit 10 | , TransactionDirectCredit 11 | , transactionDirectCreditQuery 12 | , allTransactionDirectCredits 13 | , getTransactionDirectCredit 14 | , insertTransactionDirectCredit 15 | , transactionDirectCreditTransactionId 16 | , transactionDirectCreditBsb 17 | ) where 18 | 19 | import BasePrelude hiding (optional) 20 | 21 | import Control.Lens 22 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 23 | import Opaleye 24 | 25 | import Db.Internal 26 | 27 | data TransactionDirectCredit' a b = TransactionDirectCredit 28 | { _transactionDirectCreditTransactionId :: a 29 | , _transactionDirectCreditBsb :: b 30 | } deriving (Eq,Show) 31 | makeLenses ''TransactionDirectCredit' 32 | 33 | type TransactionDirectCredit = TransactionDirectCredit' Int Int 34 | type TransactionDirectCreditColumn = TransactionDirectCredit' 35 | (Column PGInt4) 36 | (Column PGInt4) 37 | 38 | makeAdaptorAndInstance "pTransactionDirectCredit" ''TransactionDirectCredit' 39 | 40 | type NewTransactionDirectCredit = TransactionDirectCredit' Int Int 41 | 42 | type NewTransactionDirectCreditColumn = TransactionDirectCredit' 43 | (Column PGInt4) 44 | (Column PGInt4) 45 | 46 | transactionDirectCreditTable :: Table NewTransactionDirectCreditColumn TransactionDirectCreditColumn 47 | transactionDirectCreditTable = Table "transaction_direct_credit" $ pTransactionDirectCredit TransactionDirectCredit 48 | { _transactionDirectCreditTransactionId = required "transaction_id" 49 | , _transactionDirectCreditBsb = required "bsb" 50 | } 51 | 52 | transactionDirectCreditQuery :: Query TransactionDirectCreditColumn 53 | transactionDirectCreditQuery = queryTable transactionDirectCreditTable 54 | 55 | allTransactionDirectCredits :: Db [TransactionDirectCredit] 56 | allTransactionDirectCredits = liftQuery transactionDirectCreditQuery 57 | 58 | getTransactionDirectCredit :: Int -> Db (Maybe TransactionDirectCredit) 59 | getTransactionDirectCredit i = liftQueryFirst $ proc () -> do 60 | tc <- transactionDirectCreditQuery -< () 61 | restrict -< tc^.transactionDirectCreditTransactionId .== pgInt4 i 62 | returnA -< tc 63 | 64 | insertTransactionDirectCredit :: NewTransactionDirectCredit -> Db Int 65 | insertTransactionDirectCredit = 66 | liftInsertReturningFirst transactionDirectCreditTable (view transactionDirectCreditTransactionId) 67 | . packNew 68 | 69 | packNew :: NewTransactionDirectCredit -> NewTransactionDirectCreditColumn 70 | packNew = pTransactionDirectCredit TransactionDirectCredit 71 | { _transactionDirectCreditTransactionId = pgInt4 72 | , _transactionDirectCreditBsb = pgInt4 73 | } 74 | -------------------------------------------------------------------------------- /code/tests/CsvTests.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module CsvTests (csvTests) where 4 | 5 | import BasePrelude 6 | 7 | import Control.Lens 8 | import Data.Time 9 | import Test.Tasty 10 | import Test.Tasty.HUnit 11 | 12 | import Csv 13 | import Types 14 | 15 | csvTests :: TestTree 16 | csvTests = testGroup "CsvTests" 17 | [ testCase "IoException" ioExceptionTest 18 | , testCase "DecodeErrors" decodeErrorsTest 19 | , testCase "DecodeOk" decodeOkTest 20 | ] 21 | 22 | ioExceptionTest :: Assertion 23 | ioExceptionTest = do 24 | e <- runCsv $ readTransactions "idontexisttrolololol" 25 | e^?_Left._CsvIoError.to show @?= Just "idontexisttrolololol: openBinaryFile: does not exist (No such file or directory)" 26 | 27 | decodeErrorsTest :: Assertion 28 | decodeErrorsTest = do 29 | e <- runCsv $ readTransactions "tests/csv/broken.csv" 30 | e @?= Left (CsvDecodeErrors expectedErrors) 31 | where 32 | expectedErrors = 33 | [ "parse error (Failed reading: conversion error: Invalid currency: No Balance) at \"\\r\"" 34 | , "parse error (Failed reading: conversion error: \"VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 UK GBP\" (line 1, column 57):\nunexpected \"K\"\nexpecting \"US\") at \"\\r\"" 35 | ] 36 | 37 | decodeOkTest :: Assertion 38 | decodeOkTest = do 39 | e <- runCsv $ readTransactions "tests/csv/ok.csv" 40 | e @?= Right expectedTransactions 41 | where 42 | expectedTransactions = Transactions "Bank Account" "Everyday Basics" 12345678 43 | [ Transaction (fromGregorian 2015 2 14) (VisaPurchase (VisaPurchaseDesc (Place "MIEL CONTAINER PREMI BRISBANE CIT") (DdMm 13 2) AU AUD)) (Currency (-40)) (Currency 1684.7) 44 | , Transaction (fromGregorian 2015 2 14) (AtmOperatorFee (AtmOperatorFeeDesc Withdrawal (Place "Money Machine"))) (Currency (-2.5)) (Currency 1724.7) 45 | , Transaction (fromGregorian 2015 2 14) (AtmWithdrawal (Place "Money Machine")) (Currency (-20)) (Currency 1727.2) 46 | , Transaction (fromGregorian 2015 2 14) (VisaPurchase (VisaPurchaseDesc (Place "TeeTurtle") (DdMm 13 2) AU USD)) (Currency (-20)) (Currency 1747.26) 47 | , Transaction (fromGregorian 2015 2 13) ForeignCurrencyConversionFee (Currency (-1.29)) (Currency 1767.26) 48 | , Transaction (fromGregorian 2015 2 12) (EftposPurchase (Place "Tenkai Sushi Restaur AU")) (Currency (-38.5)) (Currency 1768.55) 49 | , Transaction (fromGregorian 2015 2 11) (EftposPurchase (Place "LB HAIR SALOON PTY L GREENSLOPES QLD")) (Currency (-79.95)) (Currency 1807.05) 50 | , Transaction (fromGregorian 2015 2 10) (DirectCredit (DirectCreditDesc (Place "Magic Pay Place Ref") 654321)) (Currency 1337) (Currency 1887) 51 | , Transaction (fromGregorian 2015 2 10) (InternetTransferCredit (InternetTransferDesc 12345679 "41514802")) (Currency 50) (Currency 550) 52 | , Transaction (fromGregorian 2015 2 9) (InternetTransferDebit (InternetTransferDesc 12345679 "13924810")) (Currency (-200)) (Currency 500) 53 | ] 54 | -------------------------------------------------------------------------------- /code-classy/src/Db/TransactionDirectCredit.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionDirectCredit 8 | ( TransactionDirectCredit'(TransactionDirectCredit) 9 | , NewTransactionDirectCredit 10 | , TransactionDirectCredit 11 | , transactionDirectCreditQuery 12 | , allTransactionDirectCredits 13 | , getTransactionDirectCredit 14 | , insertTransactionDirectCredit 15 | , transactionDirectCreditTransactionId 16 | , transactionDirectCreditBsb 17 | ) where 18 | 19 | import BasePrelude hiding (optional) 20 | 21 | import Control.Lens 22 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 23 | import Opaleye 24 | 25 | import Db.Internal 26 | 27 | data TransactionDirectCredit' a b = TransactionDirectCredit 28 | { _transactionDirectCreditTransactionId :: a 29 | , _transactionDirectCreditBsb :: b 30 | } deriving (Eq,Show) 31 | makeLenses ''TransactionDirectCredit' 32 | 33 | type TransactionDirectCredit = TransactionDirectCredit' Int Int 34 | type TransactionDirectCreditColumn = TransactionDirectCredit' 35 | (Column PGInt4) 36 | (Column PGInt4) 37 | 38 | makeAdaptorAndInstance "pTransactionDirectCredit" ''TransactionDirectCredit' 39 | 40 | type NewTransactionDirectCredit = TransactionDirectCredit' Int Int 41 | 42 | type NewTransactionDirectCreditColumn = TransactionDirectCredit' 43 | (Column PGInt4) 44 | (Column PGInt4) 45 | 46 | transactionDirectCreditTable :: Table NewTransactionDirectCreditColumn TransactionDirectCreditColumn 47 | transactionDirectCreditTable = Table "transaction_direct_credit" $ pTransactionDirectCredit TransactionDirectCredit 48 | { _transactionDirectCreditTransactionId = required "transaction_id" 49 | , _transactionDirectCreditBsb = required "bsb" 50 | } 51 | 52 | transactionDirectCreditQuery :: Query TransactionDirectCreditColumn 53 | transactionDirectCreditQuery = queryTable transactionDirectCreditTable 54 | 55 | allTransactionDirectCredits :: CanDb c e m => m [TransactionDirectCredit] 56 | allTransactionDirectCredits = liftQuery transactionDirectCreditQuery 57 | 58 | getTransactionDirectCredit :: CanDb c e m => Int -> m (Maybe TransactionDirectCredit) 59 | getTransactionDirectCredit i = liftQueryFirst $ proc () -> do 60 | tc <- transactionDirectCreditQuery -< () 61 | restrict -< tc^.transactionDirectCreditTransactionId .== pgInt4 i 62 | returnA -< tc 63 | 64 | insertTransactionDirectCredit :: CanDb c e m => NewTransactionDirectCredit -> m Int 65 | insertTransactionDirectCredit = 66 | liftInsertReturningFirst transactionDirectCreditTable (view transactionDirectCreditTransactionId) 67 | . packNew 68 | 69 | packNew :: NewTransactionDirectCredit -> NewTransactionDirectCreditColumn 70 | packNew = pTransactionDirectCredit TransactionDirectCredit 71 | { _transactionDirectCreditTransactionId = pgInt4 72 | , _transactionDirectCreditBsb = pgInt4 73 | } 74 | -------------------------------------------------------------------------------- /code/src/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | {-# LANGUAGE NoImplicitPrelude #-} 5 | {-# LANGUAGE TemplateHaskell #-} 6 | {-# LANGUAGE TypeFamilies #-} 7 | module Types where 8 | 9 | import BasePrelude 10 | 11 | import Control.Lens 12 | import Data.Text (Text) 13 | import Data.Time (Day) 14 | 15 | newtype Place = Place Text deriving (Eq,Show,IsString) 16 | makeWrapped ''Place 17 | 18 | newtype Currency = Currency Double deriving (Eq,Show,Num,Fractional) 19 | makeWrapped ''Currency 20 | 21 | data DdMm = DdMm 22 | { ddMmDay :: Int 23 | , ddMmMonth :: Int 24 | } deriving (Eq,Show) 25 | makeLenses ''DdMm 26 | 27 | data CountryCode = AU | US deriving (Eq,Show) 28 | makePrisms ''CountryCode 29 | 30 | data CurrencyCode = AUD | USD deriving (Eq,Show) 31 | makePrisms ''CurrencyCode 32 | 33 | data VisaPurchaseDesc = VisaPurchaseDesc 34 | { _visaPurchasePlace :: Place 35 | , _visaPurchaseDate :: DdMm 36 | , _visaPurchaseCountry :: CountryCode 37 | , _visaPurchaseCurrency :: CurrencyCode 38 | } deriving (Eq,Show) 39 | makeLenses ''VisaPurchaseDesc 40 | 41 | data AtmOperatorFeeType = Withdrawal deriving (Eq,Show) 42 | makePrisms ''AtmOperatorFeeType 43 | 44 | data AtmOperatorFeeDesc = AtmOperatorFeeDesc 45 | { _atmOperatorFeeType :: AtmOperatorFeeType 46 | , _atmOperatorFeePlace :: Place 47 | } deriving (Eq,Show) 48 | makeLenses ''AtmOperatorFeeDesc 49 | 50 | data DirectCreditDesc = DirectCreditDesc 51 | { _directCreditPlace :: Place 52 | , _directCreditBsb :: Int 53 | } deriving (Eq,Show) 54 | makeLenses ''DirectCreditDesc 55 | 56 | data InternetTransferDesc = InternetTransferDesc 57 | { _internetTransferAccount :: Int 58 | , _internetTransferRef :: Text 59 | } deriving (Eq,Show) 60 | makeLenses ''InternetTransferDesc 61 | 62 | data TransactionDesc 63 | = VisaPurchase VisaPurchaseDesc 64 | | EftposPurchase Place 65 | | ForeignCurrencyConversionFee 66 | | AtmOperatorFee AtmOperatorFeeDesc 67 | | AtmWithdrawal Place 68 | | DirectCredit DirectCreditDesc 69 | | InternetTransferCredit InternetTransferDesc 70 | | InternetTransferDebit InternetTransferDesc 71 | deriving (Eq,Show) 72 | makePrisms ''TransactionDesc 73 | 74 | transactionDescPlace :: Traversal' TransactionDesc Place 75 | transactionDescPlace = 76 | _VisaPurchase.visaPurchasePlace 77 | `failing` _EftposPurchase 78 | `failing` _AtmOperatorFee.atmOperatorFeePlace 79 | `failing` _DirectCredit.directCreditPlace 80 | `failing` _AtmWithdrawal 81 | 82 | data Transaction = Transaction 83 | { _transactionDate :: Day 84 | , _transactionDesc :: TransactionDesc 85 | , _transactionAmount :: Currency 86 | , _transactionBalance :: Currency 87 | } deriving (Eq,Show) 88 | makeLenses ''Transaction 89 | 90 | data Transactions = Transactions 91 | { _transactionsAcctName :: Text 92 | , _transactionsAcctType :: Text 93 | , _transactionsAcctNum :: Int 94 | , _transactions :: [Transaction] 95 | } deriving (Eq,Show) 96 | makeLenses ''Transactions 97 | -------------------------------------------------------------------------------- /slides/lib/js/head.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | Head JS The only script in your 3 | Copyright Tero Piirainen (tipiirai) 4 | License MIT / http://bit.ly/mit-license 5 | Version 0.96 6 | 7 | http://headjs.com 8 | */(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c Db (Maybe TransactionVisa) 71 | getTransactionVisa i = liftQueryFirst $ proc () -> do 72 | tc <- transactionVisaQuery -< () 73 | restrict -< tc^.transactionVisaTransactionId .== pgInt4 i 74 | returnA -< tc 75 | 76 | insertTransactionVisa :: NewTransactionVisa -> Db Int 77 | insertTransactionVisa = 78 | liftInsertReturningFirst transactionVisaTable (view transactionVisaTransactionId) 79 | . packNew 80 | 81 | packNew :: NewTransactionVisa -> NewTransactionVisaColumn 82 | packNew = pTransactionVisa TransactionVisa 83 | { _transactionVisaTransactionId = pgInt4 84 | , _transactionVisaPurchaseDate = pgDay 85 | , _transactionVisaCurrencyCode = pgStrictText 86 | , _transactionVisaCountryCode = pgStrictText 87 | } 88 | -------------------------------------------------------------------------------- /code-classy/tests/CsvTests.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NoImplicitPrelude #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module CsvTests (csvTests) where 4 | 5 | import BasePrelude 6 | 7 | import Control.Monad.Except (ExceptT,runExceptT) 8 | import Control.Lens 9 | import Data.Time 10 | import Test.Tasty 11 | import Test.Tasty.HUnit 12 | 13 | import Csv 14 | import Types 15 | 16 | csvTests :: TestTree 17 | csvTests = testGroup "CsvTests" 18 | [ testCase "IoException" ioExceptionTest 19 | , testCase "DecodeErrors" decodeErrorsTest 20 | , testCase "DecodeOk" decodeOkTest 21 | ] 22 | 23 | runCsv :: ExceptT CsvError IO a -> IO (Either CsvError a) 24 | runCsv = runExceptT 25 | 26 | ioExceptionTest :: Assertion 27 | ioExceptionTest = do 28 | e <- runCsv $ readTransactions "idontexisttrolololol" 29 | e^?_Left._CsvIoError.to show @?= Just "idontexisttrolololol: openBinaryFile: does not exist (No such file or directory)" 30 | 31 | decodeErrorsTest :: Assertion 32 | decodeErrorsTest = do 33 | e <- runCsv $ readTransactions "tests/csv/broken.csv" 34 | e @?= Left (CsvDecodeErrors expectedErrors) 35 | where 36 | expectedErrors = 37 | [ "parse error (Failed reading: conversion error: Invalid currency: No Balance) at \"\\r\"" 38 | , "parse error (Failed reading: conversion error: \"VISA PURCHASE MIEL CONTAINER PREMI BRISBANE CIT 13/02 UK GBP\" (line 1, column 57):\nunexpected \"K\"\nexpecting \"US\") at \"\\r\"" 39 | ] 40 | 41 | decodeOkTest :: Assertion 42 | decodeOkTest = do 43 | e <- runCsv $ readTransactions "tests/csv/ok.csv" 44 | e @?= Right expectedTransactions 45 | where 46 | expectedTransactions = Transactions "Bank Account" "Everyday Basics" 12345678 47 | [ Transaction (fromGregorian 2015 2 14) (VisaPurchase (VisaPurchaseDesc (Place "MIEL CONTAINER PREMI BRISBANE CIT") (DdMm 13 2) AU AUD)) (Currency (-40)) (Currency 1684.7) 48 | , Transaction (fromGregorian 2015 2 14) (AtmOperatorFee (AtmOperatorFeeDesc Withdrawal (Place "Money Machine"))) (Currency (-2.5)) (Currency 1724.7) 49 | , Transaction (fromGregorian 2015 2 14) (AtmWithdrawal (Place "Money Machine")) (Currency (-20)) (Currency 1727.2) 50 | , Transaction (fromGregorian 2015 2 14) (VisaPurchase (VisaPurchaseDesc (Place "TeeTurtle") (DdMm 13 2) AU USD)) (Currency (-20)) (Currency 1747.26) 51 | , Transaction (fromGregorian 2015 2 13) ForeignCurrencyConversionFee (Currency (-1.29)) (Currency 1767.26) 52 | , Transaction (fromGregorian 2015 2 12) (EftposPurchase (Place "Tenkai Sushi Restaur AU")) (Currency (-38.5)) (Currency 1768.55) 53 | , Transaction (fromGregorian 2015 2 11) (EftposPurchase (Place "LB HAIR SALOON PTY L GREENSLOPES QLD")) (Currency (-79.95)) (Currency 1807.05) 54 | , Transaction (fromGregorian 2015 2 10) (DirectCredit (DirectCreditDesc (Place "Magic Pay Place Ref") 654321)) (Currency 1337) (Currency 1887) 55 | , Transaction (fromGregorian 2015 2 10) (InternetTransferCredit (InternetTransferDesc 12345679 "41514802")) (Currency 50) (Currency 550) 56 | , Transaction (fromGregorian 2015 2 9) (InternetTransferDebit (InternetTransferDesc 12345679 "13924810")) (Currency (-200)) (Currency 500) 57 | ] 58 | -------------------------------------------------------------------------------- /code-classy/src/Db/TransactionVisa.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionVisa 8 | ( TransactionVisa'(TransactionVisa) 9 | , NewTransactionVisa 10 | , TransactionVisa 11 | , transactionVisaQuery 12 | , allTransactionVisas 13 | , getTransactionVisa 14 | , insertTransactionVisa 15 | , transactionVisaTransactionId 16 | , transactionVisaPurchaseDate 17 | , transactionVisaCurrencyCode 18 | , transactionVisaCountryCode 19 | ) where 20 | 21 | import BasePrelude hiding (optional) 22 | 23 | import Control.Lens 24 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 25 | import Data.Text (Text) 26 | import Data.Time (Day) 27 | import Opaleye 28 | 29 | import Db.Internal 30 | 31 | data TransactionVisa' a b c d = TransactionVisa 32 | { _transactionVisaTransactionId :: a 33 | , _transactionVisaPurchaseDate :: b 34 | , _transactionVisaCurrencyCode :: c 35 | , _transactionVisaCountryCode :: d 36 | } deriving (Eq,Show) 37 | makeLenses ''TransactionVisa' 38 | 39 | type TransactionVisa = TransactionVisa' Int Day Text Text 40 | type TransactionVisaColumn = TransactionVisa' 41 | (Column PGInt4) 42 | (Column PGDate) 43 | (Column PGText) 44 | (Column PGText) 45 | 46 | makeAdaptorAndInstance "pTransactionVisa" ''TransactionVisa' 47 | 48 | type NewTransactionVisa = TransactionVisa' Int Day Text Text 49 | 50 | type NewTransactionVisaColumn = TransactionVisa' 51 | (Column PGInt4) 52 | (Column PGDate) 53 | (Column PGText) 54 | (Column PGText) 55 | 56 | transactionVisaTable :: Table NewTransactionVisaColumn TransactionVisaColumn 57 | transactionVisaTable = Table "transaction_visa" $ pTransactionVisa TransactionVisa 58 | { _transactionVisaTransactionId = required "transaction_id" 59 | , _transactionVisaPurchaseDate = required "purchase_date" 60 | , _transactionVisaCurrencyCode = required "currency_code" 61 | , _transactionVisaCountryCode = required "country_code" 62 | } 63 | 64 | transactionVisaQuery :: Query TransactionVisaColumn 65 | transactionVisaQuery = queryTable transactionVisaTable 66 | 67 | allTransactionVisas :: CanDb c e m => m [TransactionVisa] 68 | allTransactionVisas = liftQuery transactionVisaQuery 69 | 70 | getTransactionVisa :: CanDb c e m => Int -> m (Maybe TransactionVisa) 71 | getTransactionVisa i = liftQueryFirst $ proc () -> do 72 | tc <- transactionVisaQuery -< () 73 | restrict -< tc^.transactionVisaTransactionId .== pgInt4 i 74 | returnA -< tc 75 | 76 | insertTransactionVisa :: CanDb c e m => NewTransactionVisa -> m Int 77 | insertTransactionVisa = 78 | liftInsertReturningFirst transactionVisaTable (view transactionVisaTransactionId) 79 | . packNew 80 | 81 | packNew :: NewTransactionVisa -> NewTransactionVisaColumn 82 | packNew = pTransactionVisa TransactionVisa 83 | { _transactionVisaTransactionId = pgInt4 84 | , _transactionVisaPurchaseDate = pgDay 85 | , _transactionVisaCurrencyCode = pgStrictText 86 | , _transactionVisaCountryCode = pgStrictText 87 | } 88 | -------------------------------------------------------------------------------- /code/src/Db/TransactionAtmOperatorFee.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionAtmOperatorFee 8 | ( TransactionAtmOperatorFee'(TransactionAtmOperatorFee) 9 | , NewTransactionAtmOperatorFee 10 | , TransactionAtmOperatorFee 11 | , transactionAtmOperatorFeeQuery 12 | , allTransactionAtmOperatorFees 13 | , getTransactionAtmOperatorFee 14 | , insertTransactionAtmOperatorFee 15 | , transactionAtmOperatorFeeTransactionId 16 | , transactionAtmOperatorFeeTransactionType 17 | ) where 18 | 19 | import BasePrelude hiding (optional) 20 | 21 | import Control.Lens 22 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 23 | import Data.Text (Text) 24 | import Opaleye 25 | 26 | import Db.Internal 27 | 28 | data TransactionAtmOperatorFee' a b = TransactionAtmOperatorFee 29 | { _transactionAtmOperatorFeeTransactionId :: a 30 | , _transactionAtmOperatorFeeTransactionType :: b 31 | } deriving (Eq,Show) 32 | makeLenses ''TransactionAtmOperatorFee' 33 | 34 | type TransactionAtmOperatorFee = TransactionAtmOperatorFee' Int Text 35 | type TransactionAtmOperatorFeeColumn = TransactionAtmOperatorFee' 36 | (Column PGInt4) 37 | (Column PGText) 38 | 39 | makeAdaptorAndInstance "pTransactionAtmOperatorFee" ''TransactionAtmOperatorFee' 40 | 41 | type NewTransactionAtmOperatorFee = TransactionAtmOperatorFee' Int Text 42 | 43 | type NewTransactionAtmOperatorFeeColumn = TransactionAtmOperatorFee' 44 | (Column PGInt4) 45 | (Column PGText) 46 | 47 | transactionAtmOperatorFeeTable :: Table NewTransactionAtmOperatorFeeColumn TransactionAtmOperatorFeeColumn 48 | transactionAtmOperatorFeeTable = Table "transaction_atm_operator_fee" $ pTransactionAtmOperatorFee TransactionAtmOperatorFee 49 | { _transactionAtmOperatorFeeTransactionId = required "transaction_id" 50 | , _transactionAtmOperatorFeeTransactionType = required "atm_transaction_type" 51 | } 52 | 53 | transactionAtmOperatorFeeQuery :: Query TransactionAtmOperatorFeeColumn 54 | transactionAtmOperatorFeeQuery = queryTable transactionAtmOperatorFeeTable 55 | 56 | allTransactionAtmOperatorFees :: Db [TransactionAtmOperatorFee] 57 | allTransactionAtmOperatorFees = liftQuery transactionAtmOperatorFeeQuery 58 | 59 | getTransactionAtmOperatorFee :: Int -> Db (Maybe TransactionAtmOperatorFee) 60 | getTransactionAtmOperatorFee i = liftQueryFirst $ proc () -> do 61 | tc <- transactionAtmOperatorFeeQuery -< () 62 | restrict -< tc^.transactionAtmOperatorFeeTransactionId .== pgInt4 i 63 | returnA -< tc 64 | 65 | insertTransactionAtmOperatorFee :: NewTransactionAtmOperatorFee -> Db Int 66 | insertTransactionAtmOperatorFee = 67 | liftInsertReturningFirst transactionAtmOperatorFeeTable (view transactionAtmOperatorFeeTransactionId) 68 | . packNew 69 | 70 | packNew :: NewTransactionAtmOperatorFee -> NewTransactionAtmOperatorFeeColumn 71 | packNew = pTransactionAtmOperatorFee TransactionAtmOperatorFee 72 | { _transactionAtmOperatorFeeTransactionId = pgInt4 73 | , _transactionAtmOperatorFeeTransactionType = pgStrictText 74 | } 75 | -------------------------------------------------------------------------------- /code-classy/src/Db/TransactionAtmOperatorFee.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionAtmOperatorFee 8 | ( TransactionAtmOperatorFee'(TransactionAtmOperatorFee) 9 | , NewTransactionAtmOperatorFee 10 | , TransactionAtmOperatorFee 11 | , transactionAtmOperatorFeeQuery 12 | , allTransactionAtmOperatorFees 13 | , getTransactionAtmOperatorFee 14 | , insertTransactionAtmOperatorFee 15 | , transactionAtmOperatorFeeTransactionId 16 | , transactionAtmOperatorFeeTransactionType 17 | ) where 18 | 19 | import BasePrelude hiding (optional) 20 | 21 | import Control.Lens 22 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 23 | import Data.Text (Text) 24 | import Opaleye 25 | 26 | import Db.Internal 27 | 28 | data TransactionAtmOperatorFee' a b = TransactionAtmOperatorFee 29 | { _transactionAtmOperatorFeeTransactionId :: a 30 | , _transactionAtmOperatorFeeTransactionType :: b 31 | } deriving (Eq,Show) 32 | makeLenses ''TransactionAtmOperatorFee' 33 | 34 | type TransactionAtmOperatorFee = TransactionAtmOperatorFee' Int Text 35 | type TransactionAtmOperatorFeeColumn = TransactionAtmOperatorFee' 36 | (Column PGInt4) 37 | (Column PGText) 38 | 39 | makeAdaptorAndInstance "pTransactionAtmOperatorFee" ''TransactionAtmOperatorFee' 40 | 41 | type NewTransactionAtmOperatorFee = TransactionAtmOperatorFee' Int Text 42 | 43 | type NewTransactionAtmOperatorFeeColumn = TransactionAtmOperatorFee' 44 | (Column PGInt4) 45 | (Column PGText) 46 | 47 | transactionAtmOperatorFeeTable :: Table NewTransactionAtmOperatorFeeColumn TransactionAtmOperatorFeeColumn 48 | transactionAtmOperatorFeeTable = Table "transaction_atm_operator_fee" $ pTransactionAtmOperatorFee TransactionAtmOperatorFee 49 | { _transactionAtmOperatorFeeTransactionId = required "transaction_id" 50 | , _transactionAtmOperatorFeeTransactionType = required "atm_transaction_type" 51 | } 52 | 53 | transactionAtmOperatorFeeQuery :: Query TransactionAtmOperatorFeeColumn 54 | transactionAtmOperatorFeeQuery = queryTable transactionAtmOperatorFeeTable 55 | 56 | allTransactionAtmOperatorFees :: CanDb c e m => m [TransactionAtmOperatorFee] 57 | allTransactionAtmOperatorFees = liftQuery transactionAtmOperatorFeeQuery 58 | 59 | getTransactionAtmOperatorFee :: CanDb c e m => Int -> m (Maybe TransactionAtmOperatorFee) 60 | getTransactionAtmOperatorFee i = liftQueryFirst $ proc () -> do 61 | tc <- transactionAtmOperatorFeeQuery -< () 62 | restrict -< tc^.transactionAtmOperatorFeeTransactionId .== pgInt4 i 63 | returnA -< tc 64 | 65 | insertTransactionAtmOperatorFee :: CanDb c e m => NewTransactionAtmOperatorFee -> m Int 66 | insertTransactionAtmOperatorFee = 67 | liftInsertReturningFirst transactionAtmOperatorFeeTable (view transactionAtmOperatorFeeTransactionId) 68 | . packNew 69 | 70 | packNew :: NewTransactionAtmOperatorFee -> NewTransactionAtmOperatorFeeColumn 71 | packNew = pTransactionAtmOperatorFee TransactionAtmOperatorFee 72 | { _transactionAtmOperatorFeeTransactionId = pgInt4 73 | , _transactionAtmOperatorFeeTransactionType = pgStrictText 74 | } 75 | -------------------------------------------------------------------------------- /slides/test/test-markdown-slide-attributes.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 6, 'found six vertical slides' ); 9 | }); 10 | 11 | test( 'Id on slide', function() { 12 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section#slide2' ).length, 1, 'found one slide with id slide2' ); 13 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section a[href="#/slide2"]' ).length, 1, 'found one slide with a link to slide2' ); 14 | }); 15 | 16 | test( 'data-background attributes', function() { 17 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#A0C66B"]' ).length, 1, 'found one vertical slide with data-background="#A0C66B"' ); 18 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#ff0000"]' ).length, 1, 'found one vertical slide with data-background="#ff0000"' ); 19 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#C6916B"]' ).length, 1, 'found one slide with data-background="#C6916B"' ); 20 | }); 21 | 22 | test( 'data-transition attributes', function() { 23 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="zoom"]' ).length, 1, 'found one vertical slide with data-transition="zoom"' ); 24 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="fade"]' ).length, 1, 'found one vertical slide with data-transition="fade"' ); 25 | strictEqual( document.querySelectorAll( '.reveal .slides section [data-transition="zoom"]' ).length, 1, 'found one slide with data-transition="zoom"' ); 26 | }); 27 | 28 | test( 'data-background attributes with default separator', function() { 29 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#A7C66B"]' ).length, 1, 'found one vertical slide with data-background="#A0C66B"' ); 30 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#f70000"]' ).length, 1, 'found one vertical slide with data-background="#ff0000"' ); 31 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#C7916B"]' ).length, 1, 'found one slide with data-background="#C6916B"' ); 32 | }); 33 | 34 | test( 'data-transition attributes with default separator', function() { 35 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="concave"]' ).length, 1, 'found one vertical slide with data-transition="zoom"' ); 36 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="page"]' ).length, 1, 'found one vertical slide with data-transition="fade"' ); 37 | strictEqual( document.querySelectorAll( '.reveal .slides section [data-transition="concave"]' ).length, 1, 'found one slide with data-transition="zoom"' ); 38 | }); 39 | 40 | test( 'data-transition attributes with inline content', function() { 41 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#ff0000"]' ).length, 3, 'found three horizontal slides with data-background="#ff0000"' ); 42 | }); 43 | 44 | } ); 45 | 46 | Reveal.initialize(); 47 | 48 | -------------------------------------------------------------------------------- /code/src/Db/TransactionInternetTransfer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionInternetTransfer 8 | ( TransactionInternetTransfer'(TransactionInternetTransfer) 9 | , NewTransactionInternetTransfer 10 | , TransactionInternetTransfer 11 | , transactionInternetTransferQuery 12 | , allTransactionInternetTransfers 13 | , getTransactionInternetTransfer 14 | , insertTransactionInternetTransfer 15 | , transactionInternetTransferTransactionId 16 | , transactionInternetTransferAccount 17 | , transactionInternetTransferRef 18 | ) where 19 | 20 | import BasePrelude hiding (optional) 21 | 22 | import Control.Lens 23 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 24 | import Data.Text (Text) 25 | import Opaleye 26 | 27 | import Db.Internal 28 | 29 | data TransactionInternetTransfer' a b c = TransactionInternetTransfer 30 | { _transactionInternetTransferTransactionId :: a 31 | , _transactionInternetTransferAccount :: b 32 | , _transactionInternetTransferRef :: c 33 | } deriving (Eq,Show) 34 | makeLenses ''TransactionInternetTransfer' 35 | 36 | type TransactionInternetTransfer = TransactionInternetTransfer' Int Int Text 37 | type TransactionInternetTransferColumn = TransactionInternetTransfer' 38 | (Column PGInt4) 39 | (Column PGInt4) 40 | (Column PGText) 41 | 42 | makeAdaptorAndInstance "pTransactionInternetTransfer" ''TransactionInternetTransfer' 43 | 44 | type NewTransactionInternetTransfer = TransactionInternetTransfer' Int Int Text 45 | 46 | type NewTransactionInternetTransferColumn = TransactionInternetTransfer' 47 | (Column PGInt4) 48 | (Column PGInt4) 49 | (Column PGText) 50 | 51 | transactionInternetTransferTable :: Table NewTransactionInternetTransferColumn TransactionInternetTransferColumn 52 | transactionInternetTransferTable = Table "transaction_internet_transfer" $ pTransactionInternetTransfer TransactionInternetTransfer 53 | { _transactionInternetTransferTransactionId = required "transaction_id" 54 | , _transactionInternetTransferAccount = required "account" 55 | , _transactionInternetTransferRef = required "ref" 56 | } 57 | 58 | transactionInternetTransferQuery :: Query TransactionInternetTransferColumn 59 | transactionInternetTransferQuery = queryTable transactionInternetTransferTable 60 | 61 | allTransactionInternetTransfers :: Db [TransactionInternetTransfer] 62 | allTransactionInternetTransfers = liftQuery transactionInternetTransferQuery 63 | 64 | getTransactionInternetTransfer :: Int -> Db (Maybe TransactionInternetTransfer) 65 | getTransactionInternetTransfer i = liftQueryFirst $ proc () -> do 66 | tc <- transactionInternetTransferQuery -< () 67 | restrict -< tc^.transactionInternetTransferTransactionId .== pgInt4 i 68 | returnA -< tc 69 | 70 | insertTransactionInternetTransfer :: NewTransactionInternetTransfer -> Db Int 71 | insertTransactionInternetTransfer = 72 | liftInsertReturningFirst transactionInternetTransferTable (view transactionInternetTransferTransactionId) 73 | . packNew 74 | 75 | packNew :: NewTransactionInternetTransfer -> NewTransactionInternetTransferColumn 76 | packNew = pTransactionInternetTransfer TransactionInternetTransfer 77 | { _transactionInternetTransferTransactionId = pgInt4 78 | , _transactionInternetTransferAccount = pgInt4 79 | , _transactionInternetTransferRef = pgStrictText 80 | } 81 | -------------------------------------------------------------------------------- /code/src/Db/Internal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | {-# LANGUAGE NoImplicitPrelude #-} 5 | {-# LANGUAGE TemplateHaskell #-} 6 | module Db.Internal where 7 | 8 | import BasePrelude 9 | 10 | import Control.Error (headMay) 11 | import Control.Lens (makeLenses, makePrisms, view) 12 | import Control.Monad.Except (ExceptT, MonadError, runExceptT) 13 | import Control.Monad.Reader (MonadReader, ReaderT, runReaderT) 14 | import Control.Monad.Trans (MonadIO) 15 | import Data.Profunctor.Product.Default (Default) 16 | import Database.PostgreSQL.Simple (Connection, QueryError, SqlError, close) 17 | import Opaleye (Column, PGBool, Query, QueryRunner, 18 | Table, Unpackspec, runDelete, runInsert, 19 | runInsertReturning, runQuery, runUpdate) 20 | 21 | import Utils (wrapExceptions) 22 | 23 | data DbError 24 | = DbQueryError QueryError 25 | | DbSqlError SqlError 26 | deriving (Show) 27 | makePrisms ''DbError 28 | 29 | data DbEnv = DbEnv 30 | { _dbEnvConnection :: Connection 31 | } 32 | makeLenses ''DbEnv 33 | 34 | newtype Db a = Db 35 | { unDb :: ExceptT DbError (ReaderT DbEnv IO) a 36 | } deriving 37 | ( Functor 38 | , Applicative 39 | , Monad 40 | , MonadReader DbEnv 41 | , MonadError DbError 42 | , MonadIO 43 | ) 44 | 45 | runDb :: DbEnv -> Db a -> IO (Either DbError a) 46 | runDb e = flip runReaderT e . runExceptT . unDb 47 | 48 | closeDbEnv :: DbEnv -> IO () 49 | closeDbEnv = close . view dbEnvConnection 50 | 51 | liftQuery 52 | :: ( Default QueryRunner columnsW haskells ) 53 | => Query columnsW 54 | -> Db [haskells] 55 | liftQuery q = withConnection (`runQuery` q) 56 | 57 | liftQueryFirst 58 | :: ( Default QueryRunner columnsW haskells ) 59 | => Query columnsW 60 | -> Db (Maybe haskells) 61 | liftQueryFirst = fmap headMay . liftQuery 62 | 63 | liftInsert 64 | :: Table columnsW columnsR 65 | -> columnsW 66 | -> Db Int64 67 | liftInsert t c = withConnection (\ con -> runInsert con t c) 68 | 69 | liftInsertReturning 70 | :: ( Default QueryRunner returned haskells 71 | , Default Unpackspec returned returned 72 | ) 73 | => Table columnsW columnsR 74 | -> (columnsR -> returned) 75 | -> columnsW 76 | -> Db [haskells] 77 | liftInsertReturning t f c = withConnection (\ con -> runInsertReturning con t c f) 78 | 79 | liftInsertReturningFirst 80 | :: ( Default QueryRunner returned haskells 81 | , Default Unpackspec returned returned 82 | ) 83 | => Table columnsW columnsR 84 | -> (columnsR -> returned) 85 | -> columnsW 86 | -> Db haskells 87 | liftInsertReturningFirst t f = fmap head . liftInsertReturning t f 88 | 89 | liftUpdate 90 | :: Table columnsW columnsR 91 | -> (columnsR -> columnsW) 92 | -> (columnsR -> Column PGBool) 93 | -> Db Int64 94 | liftUpdate t f w = withConnection (\ con -> runUpdate con t f w) 95 | 96 | liftDelete 97 | :: Table columnsW columnsR 98 | -> (columnsR -> Column PGBool) 99 | -> Db Int64 100 | liftDelete t w = withConnection (\ con -> runDelete con t w) 101 | 102 | withConnection :: (Connection -> IO a) -> Db a 103 | withConnection f = do 104 | c <- view dbEnvConnection 105 | wrapExceptions (f c) 106 | [ Handler (pure . DbSqlError) 107 | , Handler (pure . DbQueryError) 108 | ] 109 | -------------------------------------------------------------------------------- /slides/test/test-markdown-slide-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown Attributes 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /code-classy/src/Db/TransactionInternetTransfer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.TransactionInternetTransfer 8 | ( TransactionInternetTransfer'(TransactionInternetTransfer) 9 | , NewTransactionInternetTransfer 10 | , TransactionInternetTransfer 11 | , transactionInternetTransferQuery 12 | , allTransactionInternetTransfers 13 | , getTransactionInternetTransfer 14 | , insertTransactionInternetTransfer 15 | , transactionInternetTransferTransactionId 16 | , transactionInternetTransferAccount 17 | , transactionInternetTransferRef 18 | ) where 19 | 20 | import BasePrelude hiding (optional) 21 | 22 | import Control.Lens 23 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 24 | import Data.Text (Text) 25 | import Opaleye 26 | 27 | import Db.Internal 28 | 29 | data TransactionInternetTransfer' a b c = TransactionInternetTransfer 30 | { _transactionInternetTransferTransactionId :: a 31 | , _transactionInternetTransferAccount :: b 32 | , _transactionInternetTransferRef :: c 33 | } deriving (Eq,Show) 34 | makeLenses ''TransactionInternetTransfer' 35 | 36 | type TransactionInternetTransfer = TransactionInternetTransfer' Int Int Text 37 | type TransactionInternetTransferColumn = TransactionInternetTransfer' 38 | (Column PGInt4) 39 | (Column PGInt4) 40 | (Column PGText) 41 | 42 | makeAdaptorAndInstance "pTransactionInternetTransfer" ''TransactionInternetTransfer' 43 | 44 | type NewTransactionInternetTransfer = TransactionInternetTransfer' Int Int Text 45 | 46 | type NewTransactionInternetTransferColumn = TransactionInternetTransfer' 47 | (Column PGInt4) 48 | (Column PGInt4) 49 | (Column PGText) 50 | 51 | transactionInternetTransferTable :: Table NewTransactionInternetTransferColumn TransactionInternetTransferColumn 52 | transactionInternetTransferTable = Table "transaction_internet_transfer" $ pTransactionInternetTransfer TransactionInternetTransfer 53 | { _transactionInternetTransferTransactionId = required "transaction_id" 54 | , _transactionInternetTransferAccount = required "account" 55 | , _transactionInternetTransferRef = required "ref" 56 | } 57 | 58 | transactionInternetTransferQuery :: Query TransactionInternetTransferColumn 59 | transactionInternetTransferQuery = queryTable transactionInternetTransferTable 60 | 61 | allTransactionInternetTransfers :: CanDb c e m => m [TransactionInternetTransfer] 62 | allTransactionInternetTransfers = liftQuery transactionInternetTransferQuery 63 | 64 | getTransactionInternetTransfer :: CanDb c e m => Int -> m (Maybe TransactionInternetTransfer) 65 | getTransactionInternetTransfer i = liftQueryFirst $ proc () -> do 66 | tc <- transactionInternetTransferQuery -< () 67 | restrict -< tc^.transactionInternetTransferTransactionId .== pgInt4 i 68 | returnA -< tc 69 | 70 | insertTransactionInternetTransfer :: CanDb c e m => NewTransactionInternetTransfer -> m Int 71 | insertTransactionInternetTransfer = 72 | liftInsertReturningFirst transactionInternetTransferTable (view transactionInternetTransferTransactionId) 73 | . packNew 74 | 75 | packNew :: NewTransactionInternetTransfer -> NewTransactionInternetTransferColumn 76 | packNew = pTransactionInternetTransfer TransactionInternetTransfer 77 | { _transactionInternetTransferTransactionId = pgInt4 78 | , _transactionInternetTransferAccount = pgInt4 79 | , _transactionInternetTransferRef = pgStrictText 80 | } 81 | -------------------------------------------------------------------------------- /code/src/Db/Transaction.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Transaction 8 | ( Transaction'(Transaction) 9 | , NewTransaction 10 | , Transaction 11 | , transactionQuery 12 | , allTransactions 13 | , getTransaction 14 | , insertTransaction 15 | , transactionId 16 | , transactionDate 17 | , transactionAmount 18 | , transactionBalance 19 | , transactionType 20 | , transactionPlaceId 21 | , transactionAccountId 22 | ) where 23 | 24 | import BasePrelude hiding (optional) 25 | 26 | import Control.Lens 27 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 28 | import Data.Text (Text) 29 | import Data.Time (Day) 30 | import Opaleye 31 | 32 | import Db.Internal 33 | 34 | data Transaction' a b c d e f g = Transaction 35 | { _transactionId :: a 36 | , _transactionDate :: b 37 | , _transactionAmount :: c 38 | , _transactionBalance :: d 39 | , _transactionType :: e 40 | , _transactionPlaceId :: f 41 | , _transactionAccountId :: g 42 | } deriving (Eq,Show) 43 | makeLenses ''Transaction' 44 | 45 | type Transaction = Transaction' Int Day Double Double Text (Maybe Int) Int 46 | type TransactionColumn = Transaction' 47 | (Column PGInt4) 48 | (Column PGDate) 49 | (Column PGFloat8) -- These should be non-floating point numbers 50 | (Column PGFloat8) -- but Opaleye doesn't support these yet. :( 51 | (Column PGText) 52 | (Column (Nullable PGInt4)) 53 | (Column PGInt4) 54 | 55 | makeAdaptorAndInstance "pTransaction" ''Transaction' 56 | 57 | type NewTransaction = Transaction' (Maybe Int) Day Double Double Text (Maybe Int) Int 58 | 59 | type NewTransactionColumn = Transaction' 60 | (Maybe (Column PGInt4)) 61 | (Column PGDate) 62 | (Column PGFloat8) 63 | (Column PGFloat8) 64 | (Column PGText) 65 | (Column (Nullable PGInt4)) 66 | (Column PGInt4) 67 | 68 | transactionTable :: Table NewTransactionColumn TransactionColumn 69 | transactionTable = Table "transaction" $ pTransaction Transaction 70 | { _transactionId = optional "id" 71 | , _transactionDate = required "date" 72 | , _transactionAmount = required "amount" 73 | , _transactionBalance = required "balance" 74 | , _transactionType = required "type" 75 | , _transactionPlaceId = required "place_id" 76 | , _transactionAccountId = required "account_id" 77 | } 78 | 79 | transactionQuery :: Query TransactionColumn 80 | transactionQuery = queryTable transactionTable 81 | 82 | allTransactions :: Db [Transaction] 83 | allTransactions = liftQuery transactionQuery 84 | 85 | insertTransaction :: NewTransaction -> Db Int 86 | insertTransaction = 87 | liftInsertReturningFirst transactionTable (view transactionId) . packNew 88 | 89 | getTransaction :: Int -> Db (Maybe Transaction) 90 | getTransaction i = liftQueryFirst $ proc () -> do 91 | t <- transactionQuery -< () 92 | restrict -< t^.transactionId .== pgInt4 i 93 | returnA -< t 94 | 95 | packNew :: NewTransaction -> NewTransactionColumn 96 | packNew = pTransaction Transaction 97 | { _transactionId = fmap pgInt4 98 | , _transactionDate = pgDay 99 | , _transactionAmount = pgDouble 100 | , _transactionBalance = pgDouble 101 | , _transactionType = pgStrictText 102 | , _transactionPlaceId = maybeToNullable . fmap pgInt4 103 | , _transactionAccountId = pgInt4 104 | } 105 | -------------------------------------------------------------------------------- /code-classy/src/Db/Transaction.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE Arrows #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE NoImplicitPrelude #-} 6 | {-# LANGUAGE TemplateHaskell #-} 7 | module Db.Transaction 8 | ( Transaction'(Transaction) 9 | , NewTransaction 10 | , Transaction 11 | , transactionQuery 12 | , allTransactions 13 | , getTransaction 14 | , insertTransaction 15 | , transactionId 16 | , transactionDate 17 | , transactionAmount 18 | , transactionBalance 19 | , transactionType 20 | , transactionPlaceId 21 | , transactionAccountId 22 | ) where 23 | 24 | import BasePrelude hiding (optional) 25 | 26 | import Control.Lens 27 | import Data.Profunctor.Product.TH (makeAdaptorAndInstance) 28 | import Data.Text (Text) 29 | import Data.Time (Day) 30 | import Opaleye 31 | 32 | import Db.Internal 33 | 34 | data Transaction' a b c d e f g = Transaction 35 | { _transactionId :: a 36 | , _transactionDate :: b 37 | , _transactionAmount :: c 38 | , _transactionBalance :: d 39 | , _transactionType :: e 40 | , _transactionPlaceId :: f 41 | , _transactionAccountId :: g 42 | } deriving (Eq,Show) 43 | makeLenses ''Transaction' 44 | 45 | type Transaction = Transaction' Int Day Double Double Text (Maybe Int) Int 46 | type TransactionColumn = Transaction' 47 | (Column PGInt4) 48 | (Column PGDate) 49 | (Column PGFloat8) -- These should be non-floating point numbers 50 | (Column PGFloat8) -- but Opaleye doesn't support these yet. :( 51 | (Column PGText) 52 | (Column (Nullable PGInt4)) 53 | (Column PGInt4) 54 | 55 | makeAdaptorAndInstance "pTransaction" ''Transaction' 56 | 57 | type NewTransaction = Transaction' (Maybe Int) Day Double Double Text (Maybe Int) Int 58 | 59 | type NewTransactionColumn = Transaction' 60 | (Maybe (Column PGInt4)) 61 | (Column PGDate) 62 | (Column PGFloat8) 63 | (Column PGFloat8) 64 | (Column PGText) 65 | (Column (Nullable PGInt4)) 66 | (Column PGInt4) 67 | 68 | transactionTable :: Table NewTransactionColumn TransactionColumn 69 | transactionTable = Table "transaction" $ pTransaction Transaction 70 | { _transactionId = optional "id" 71 | , _transactionDate = required "date" 72 | , _transactionAmount = required "amount" 73 | , _transactionBalance = required "balance" 74 | , _transactionType = required "type" 75 | , _transactionPlaceId = required "place_id" 76 | , _transactionAccountId = required "account_id" 77 | } 78 | 79 | transactionQuery :: Query TransactionColumn 80 | transactionQuery = queryTable transactionTable 81 | 82 | allTransactions :: CanDb c e m => m [Transaction] 83 | allTransactions = liftQuery transactionQuery 84 | 85 | insertTransaction :: CanDb c e m => NewTransaction -> m Int 86 | insertTransaction = 87 | liftInsertReturningFirst transactionTable (view transactionId) . packNew 88 | 89 | getTransaction :: CanDb c e m => Int -> m (Maybe Transaction) 90 | getTransaction i = liftQueryFirst $ proc () -> do 91 | t <- transactionQuery -< () 92 | restrict -< t^.transactionId .== pgInt4 i 93 | returnA -< t 94 | 95 | packNew :: NewTransaction -> NewTransactionColumn 96 | packNew = pTransaction Transaction 97 | { _transactionId = fmap pgInt4 98 | , _transactionDate = pgDay 99 | , _transactionAmount = pgDouble 100 | , _transactionBalance = pgDouble 101 | , _transactionType = pgStrictText 102 | , _transactionPlaceId = maybeToNullable . fmap pgInt4 103 | , _transactionAccountId = pgInt4 104 | } 105 | -------------------------------------------------------------------------------- /code-classy/src/Db/Internal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | {-# LANGUAGE TemplateHaskell #-} 5 | {-# LANGUAGE ConstraintKinds #-} 6 | module Db.Internal where 7 | 8 | import Control.Error (headMay) 9 | import Control.Lens (makeClassy, makeClassyPrisms, view, 10 | Prism',(#)) 11 | import Control.Exception (SomeException,catches) 12 | import Control.Exception.Lens (exception) 13 | import Control.Monad.Except (MonadError,throwError) 14 | import Control.Monad.Error.Lens (handler) 15 | import Control.Monad.Reader (MonadReader) 16 | import Control.Monad.Trans (MonadIO,liftIO) 17 | import Data.Int (Int64) 18 | import Data.Profunctor.Product.Default (Default) 19 | import Database.PostgreSQL.Simple (Connection, QueryError, SqlError, 20 | close) 21 | import Opaleye (Column, PGBool, Query, QueryRunner, 22 | Table, Unpackspec, runDelete, 23 | runInsert, runInsertReturning, 24 | runQuery, runUpdate) 25 | 26 | import Utils (wrapExceptions) 27 | 28 | data DbError 29 | = DbQueryError QueryError 30 | | DbSqlError SqlError 31 | deriving (Show) 32 | makeClassyPrisms ''DbError 33 | 34 | data DbEnv = DbEnv 35 | { _dbEnvConnection :: Connection 36 | } 37 | makeClassy ''DbEnv 38 | 39 | type CanDb c e m = 40 | ( ProvidesDbEnv c m 41 | , CanDbError e m 42 | , MonadIO m 43 | ) 44 | 45 | type ProvidesDbEnv c m = (MonadReader c m, HasDbEnv c) 46 | type CanDbError e m = (MonadError e m, AsDbError e) 47 | 48 | liftQuery 49 | :: ( CanDb c e m, Default QueryRunner columnsW haskells ) 50 | => Query columnsW 51 | -> m [haskells] 52 | liftQuery q = withConnection (`runQuery` q) 53 | 54 | liftQueryFirst 55 | :: ( CanDb c e m, Default QueryRunner columnsW haskells ) 56 | => Query columnsW 57 | -> m (Maybe haskells) 58 | liftQueryFirst = fmap headMay . liftQuery 59 | 60 | liftInsert 61 | :: CanDb c e m 62 | => Table columnsW columnsR 63 | -> columnsW 64 | -> m Int64 65 | liftInsert t c = withConnection (\ con -> runInsert con t c) 66 | 67 | liftInsertReturning 68 | :: ( CanDb c e m 69 | , Default QueryRunner returned haskells 70 | , Default Unpackspec returned returned 71 | ) 72 | => Table columnsW columnsR 73 | -> (columnsR -> returned) 74 | -> columnsW 75 | -> m [haskells] 76 | liftInsertReturning t f c = withConnection (\ con -> runInsertReturning con t c f) 77 | 78 | liftInsertReturningFirst 79 | :: ( CanDb c e m 80 | , Default QueryRunner returned haskells 81 | , Default Unpackspec returned returned 82 | ) 83 | => Table columnsW columnsR 84 | -> (columnsR -> returned) 85 | -> columnsW 86 | -> m haskells 87 | liftInsertReturningFirst t f = fmap head . liftInsertReturning t f 88 | 89 | liftUpdate 90 | :: CanDb c e m 91 | => Table columnsW columnsR 92 | -> (columnsR -> columnsW) 93 | -> (columnsR -> Column PGBool) 94 | -> m Int64 95 | liftUpdate t f w = withConnection (\ con -> runUpdate con t f w) 96 | 97 | liftDelete 98 | :: CanDb c e m 99 | => Table columnsW columnsR 100 | -> (columnsR -> Column PGBool) 101 | -> m Int64 102 | liftDelete t w = withConnection (\ con -> runDelete con t w) 103 | 104 | _SqlError :: Prism' SomeException SqlError 105 | _SqlError = exception 106 | 107 | _QueryError :: Prism' SomeException QueryError 108 | _QueryError = exception 109 | 110 | withConnection :: CanDb c e m => (Connection -> IO a) -> m a 111 | withConnection f = do 112 | c <- view dbEnvConnection 113 | wrapExceptions (f c) 114 | [ handler _SqlError (pure . (_DbSqlError #)) 115 | , handler _QueryError (pure . (_DbQueryError #)) 116 | ] 117 | -------------------------------------------------------------------------------- /slides/test/test-markdown-element-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown Element Attributes 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /slides/css/print/pdf.css: -------------------------------------------------------------------------------- 1 | /* Default Print Stylesheet Template 2 | by Rob Glazebrook of CSSnewbie.com 3 | Last Updated: June 4, 2008 4 | 5 | Feel free (nay, compelled) to edit, append, and 6 | manipulate this file as you see fit. */ 7 | 8 | 9 | /* SECTION 1: Set default width, margin, float, and 10 | background. This prevents elements from extending 11 | beyond the edge of the printed page, and prevents 12 | unnecessary background images from printing */ 13 | 14 | * { 15 | -webkit-print-color-adjust: exact; 16 | } 17 | 18 | body { 19 | margin: 0 auto !important; 20 | border: 0; 21 | padding: 0; 22 | float: none !important; 23 | overflow: visible; 24 | } 25 | 26 | html { 27 | width: 100%; 28 | height: 100%; 29 | overflow: visible; 30 | } 31 | 32 | /* SECTION 2: Remove any elements not needed in print. 33 | This would include navigation, ads, sidebars, etc. */ 34 | .nestedarrow, 35 | .reveal .controls, 36 | .reveal .progress, 37 | .reveal .slide-number, 38 | .reveal .playback, 39 | .reveal.overview, 40 | .fork-reveal, 41 | .share-reveal, 42 | .state-background { 43 | display: none !important; 44 | } 45 | 46 | /* SECTION 3: Set body font face, size, and color. 47 | Consider using a serif font for readability. */ 48 | body, p, td, li, div { 49 | 50 | } 51 | 52 | /* SECTION 4: Set heading font face, sizes, and color. 53 | Differentiate your headings from your body text. 54 | Perhaps use a large sans-serif for distinction. */ 55 | h1,h2,h3,h4,h5,h6 { 56 | text-shadow: 0 0 0 #000 !important; 57 | } 58 | 59 | .reveal pre code { 60 | overflow: hidden !important; 61 | font-family: Courier, 'Courier New', monospace !important; 62 | } 63 | 64 | 65 | /* SECTION 5: more reveal.js specific additions by @skypanther */ 66 | ul, ol, div, p { 67 | visibility: visible; 68 | position: static; 69 | width: auto; 70 | height: auto; 71 | display: block; 72 | overflow: visible; 73 | margin: auto; 74 | } 75 | .reveal { 76 | width: auto !important; 77 | height: auto !important; 78 | overflow: hidden !important; 79 | } 80 | .reveal .slides { 81 | position: static; 82 | width: 100%; 83 | height: auto; 84 | 85 | left: auto; 86 | top: auto; 87 | margin: 0 !important; 88 | padding: 0 !important; 89 | 90 | overflow: visible; 91 | display: block; 92 | 93 | -webkit-perspective: none; 94 | -moz-perspective: none; 95 | -ms-perspective: none; 96 | perspective: none; 97 | 98 | -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ 99 | -moz-perspective-origin: 50% 50%; 100 | -ms-perspective-origin: 50% 50%; 101 | perspective-origin: 50% 50%; 102 | } 103 | .reveal .slides section { 104 | page-break-after: always !important; 105 | 106 | visibility: visible !important; 107 | position: relative !important; 108 | display: block !important; 109 | position: relative !important; 110 | 111 | margin: 0 !important; 112 | padding: 0 !important; 113 | box-sizing: border-box !important; 114 | min-height: 1px; 115 | 116 | opacity: 1 !important; 117 | 118 | -webkit-transform-style: flat !important; 119 | -moz-transform-style: flat !important; 120 | -ms-transform-style: flat !important; 121 | transform-style: flat !important; 122 | 123 | -webkit-transform: none !important; 124 | -moz-transform: none !important; 125 | -ms-transform: none !important; 126 | transform: none !important; 127 | } 128 | .reveal section.stack { 129 | margin: 0 !important; 130 | padding: 0 !important; 131 | page-break-after: avoid !important; 132 | height: auto !important; 133 | min-height: auto !important; 134 | } 135 | .reveal img { 136 | box-shadow: none; 137 | } 138 | .reveal .roll { 139 | overflow: visible; 140 | line-height: 1em; 141 | } 142 | 143 | /* Slide backgrounds are placed inside of their slide when exporting to PDF */ 144 | .reveal section .slide-background { 145 | display: block !important; 146 | position: absolute; 147 | top: 0; 148 | left: 0; 149 | width: 100%; 150 | z-index: -1; 151 | } 152 | /* All elements should be above the slide-background */ 153 | .reveal section>* { 154 | position: relative; 155 | z-index: 1; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /slides/plugin/notes/notes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles opening of and synchronization with the reveal.js 3 | * notes window. 4 | * 5 | * Handshake process: 6 | * 1. This window posts 'connect' to notes window 7 | * - Includes URL of presentation to show 8 | * 2. Notes window responds with 'connected' when it is available 9 | * 3. This window proceeds to send the current presentation state 10 | * to the notes window 11 | */ 12 | var RevealNotes = (function() { 13 | 14 | function openNotes() { 15 | var jsFileLocation = document.querySelector('script[src$="notes.js"]').src; // this js file path 16 | jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path 17 | var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1100,height=700' ); 18 | 19 | /** 20 | * Connect to the notes window through a postmessage handshake. 21 | * Using postmessage enables us to work in situations where the 22 | * origins differ, such as a presentation being opened from the 23 | * file system. 24 | */ 25 | function connect() { 26 | // Keep trying to connect until we get a 'connected' message back 27 | var connectInterval = setInterval( function() { 28 | notesPopup.postMessage( JSON.stringify( { 29 | namespace: 'reveal-notes', 30 | type: 'connect', 31 | url: window.location.protocol + '//' + window.location.host + window.location.pathname, 32 | state: Reveal.getState() 33 | } ), '*' ); 34 | }, 500 ); 35 | 36 | window.addEventListener( 'message', function( event ) { 37 | var data = JSON.parse( event.data ); 38 | if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) { 39 | clearInterval( connectInterval ); 40 | onConnected(); 41 | } 42 | } ); 43 | } 44 | 45 | /** 46 | * Posts the current slide data to the notes window 47 | */ 48 | function post() { 49 | 50 | var slideElement = Reveal.getCurrentSlide(), 51 | notesElement = slideElement.querySelector( 'aside.notes' ); 52 | 53 | var messageData = { 54 | namespace: 'reveal-notes', 55 | type: 'state', 56 | notes: '', 57 | markdown: false, 58 | state: Reveal.getState() 59 | }; 60 | 61 | // Look for notes defined in a slide attribute 62 | if( slideElement.hasAttribute( 'data-notes' ) ) { 63 | messageData.notes = slideElement.getAttribute( 'data-notes' ); 64 | } 65 | 66 | // Look for notes defined in an aside element 67 | if( notesElement ) { 68 | messageData.notes = notesElement.innerHTML; 69 | messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; 70 | } 71 | 72 | notesPopup.postMessage( JSON.stringify( messageData ), '*' ); 73 | 74 | } 75 | 76 | /** 77 | * Called once we have established a connection to the notes 78 | * window. 79 | */ 80 | function onConnected() { 81 | 82 | // Monitor events that trigger a change in state 83 | Reveal.addEventListener( 'slidechanged', post ); 84 | Reveal.addEventListener( 'fragmentshown', post ); 85 | Reveal.addEventListener( 'fragmenthidden', post ); 86 | Reveal.addEventListener( 'overviewhidden', post ); 87 | Reveal.addEventListener( 'overviewshown', post ); 88 | Reveal.addEventListener( 'paused', post ); 89 | Reveal.addEventListener( 'resumed', post ); 90 | 91 | // Post the initial state 92 | post(); 93 | 94 | } 95 | 96 | connect(); 97 | } 98 | 99 | if( !/receiver/i.test( window.location.search ) ) { 100 | 101 | // If the there's a 'notes' query set, open directly 102 | if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { 103 | openNotes(); 104 | } 105 | 106 | // Open the notes when the 's' key is hit 107 | document.addEventListener( 'keydown', function( event ) { 108 | // Disregard the event if the target is editable or a 109 | // modifier is present 110 | if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; 111 | 112 | if( event.keyCode === 83 ) { 113 | event.preventDefault(); 114 | openNotes(); 115 | } 116 | }, false ); 117 | 118 | } 119 | 120 | return { open: openNotes }; 121 | 122 | })(); 123 | --------------------------------------------------------------------------------