Shpadoinkle-console (empty) → 0.0.1.0
raw patch · 5 files changed
+287/−0 lines, 5 filesdep +aesondep +basedep +jsaddle
Dependencies added: aeson, base, jsaddle, lens, text, unliftio
Files
- CHANGELOG.md +0/−0
- LICENSE +27/−0
- README.md +35/−0
- Shpadoinkle-console.cabal +42/−0
- Shpadoinkle/Console.hs +183/−0
+ CHANGELOG.md view
+ LICENSE view
@@ -0,0 +1,27 @@+Shpadoinkle Debug aka S11 Debug+Copyright © 2020 Isaac Shapira+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of Shpadoinkle nor the names of its contributors may be+ used to endorse or promote products derived from this software without+ specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,35 @@+# Shpadoinkle Console++[](https://gitlab.com/fresheyeball/Shpadoinkle)+[](https://shpadoinkle.org/console)+[](https://opensource.org/licenses/BSD-3-Clause)+[](https://builtwithnix.org)+[](https://hackage.haskell.org/package/Shpadoinkle-console)+[](http://packdeps.haskellers.com/reverse/Shpadoinkle-console)+[](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-console)++This package exposes some useful debugging functions for tracing the state of Shpadoinkle applications as they change over time. It exposes some type classes currently.++```haskell+class LogJS (c :: Type -> Constraint) where+ logJS :: c a => a -> JSM ()++class LogJS c => Trapper c where+ trapper :: c a => JSContextRef -> a -> a++class Assert (c :: Type -> Constraint) where+ assert :: c a => Bool -> a -> JSM ()+```++These are intended to by used with `TypeApplications`, so you can choose the means of conversion before passing to `console.log`. For example:++```haskell+main :: IO ()+main = runJSorWarp 8080 $ do+ ctx <- askJSM+ simple runParDiff initial (view . trapper @ToJSON ctx) getBody+```++This will log all state by first encoding to JSON with Aeson, then then logging with `JSON.parse` so the browser console has the nice native display. If we change it to `trapper @Show ctx` it will use the `Show` instance instead.++We also export a handful of `console` bindings such as `console.time`, `console.table`, `console.info`, `console.warn`, `console.debug`, and of course `console.log`.
+ Shpadoinkle-console.cabal view
@@ -0,0 +1,42 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.32.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: ed7c83dc76bc4f63bd8d401f7b00f44eae07c11d062de89532fc81d93598aa13++name: Shpadoinkle-console+version: 0.0.1.0+synopsis: Support for the native browser console+description: This package adds support for native browser console features for logging and debugging.+category: Web+author: Isaac Shapira+maintainer: fresheyeball@protonmail.com+license: BSD3+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ CHANGELOG.md++source-repository head+ type: git+ location: https://gitlab.com/fresheyeball/Shpadoinkle.git++library+ exposed-modules:+ Shpadoinkle.Console+ other-modules:+ Paths_Shpadoinkle_console+ hs-source-dirs:+ ./.+ ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities+ build-depends:+ aeson >=1.4.4 && <1.5+ , base >=4.12.0 && <4.16+ , jsaddle >=0.9.7 && <0.20+ , lens >=4.17.1 && <5.0+ , text >=1.2.3 && <1.3+ , unliftio >=0.2.12 && <0.3+ default-language: Haskell2010
+ Shpadoinkle/Console.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE ExtendedDefaultRules #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}+++{-|+ This module exposes the browser's native console logging and debugging features,+ including underutilized features such as time measurement, table displays, and assertions.+-}+++module Shpadoinkle.Console (+ -- * Classes+ LogJS (..), Assert (..), Trapper (..), askJSM+ -- * Native methods+ -- ** Log levels+ , log, debug, info, warn+ -- ** Fancy display+ , table+ -- ** Time Measurement+ , TimeLabel(..), time, timeEnd+ ) where+++import Control.Lens+import Data.Aeson (ToJSON, encode)+import Data.Kind+import Data.String+import Data.Text+import Data.Text.Lazy (toStrict)+import Data.Text.Lazy.Encoding+import Language.Javascript.JSaddle hiding (startTime)+import Prelude hiding (log)+import System.IO.Unsafe (unsafePerformIO)+++default (Text)++{-|+ 'LogJS' is the base class for logging to the browser console.+ Browser consoles contain rich tooling for exploring JavaScript objects,+ DOM nodes, and much more. To take advantage of these native features, we+ need to choose how we are going to log. The 'LogJS' class is intended to+ be used in conjunction with 'TypeApplications'.++ @+ data Person = Person { first :: String, last :: String, age :: Int } deriving (Generic, ToJSON)+ main = logJS @ToJSON "log" $ Person "bob" "saget" 45+ @++ is effectively equivalent to:++ @+ console.log({first: "bob", last: "saget", age: 45})+ @++ in that the console will render with nice expand/collapse object exploration features.+-}+class LogJS (c :: Type -> Constraint) where+ logJS :: c a => Text -> a -> JSM ()+++-- | Logs against 'ToJSON' will be encoded via 'Aeson' then parsed using+-- native <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse JSON.parse> before being sent to the console.+instance LogJS ToJSON where+ logJS t a = do+ console <- jsg "console"+ json <- jsg "JSON"+ parsed <- json ^. js1 "parse" (toStrict . decodeUtf8 $ encode a)+ () <$ console ^. js1 t parsed+++-- | Logs against 'Show' will be converted to a 'String' before being sent to the console.+instance LogJS Show where+ logJS t a = do+ console <- jsg "console"+ () <$ console ^. js1 t (pack $ show a)+++-- | Logs against 'ToJSVal' will be converted to a 'JSVal' before being sent to the console.+instance LogJS ToJSVal where+ logJS t a = do+ console <- jsg "console"+ () <$ console ^. js1 t (toJSVal a)+++{-|+ Trapper is a class intended for continuous logging of your application and the catching of helpless animals.+ Usage is along the lines of 'Debug.Trace.trace' where the effect of logging is implicit.+ To make this work in both GHC and GHCjs contexts, you do need to+ pass the 'JSContextRef' in manually ('askJSM' re-exported here for convenience).++ @+ main :: IO ()+ main = runJSorWarp 8080 $ do+ ctx <- askJSM+ simple runParDiff initial (view . trapper @ToJSON ctx) getBody+ @+-}+class LogJS c => Trapper c where+ trapper :: c a => JSContextRef -> a -> a+ trapper ctx x = unsafePerformIO $ runJSM (x <$ debug @c x) ctx+ {-# NOINLINE trapper #-}++instance Trapper ToJSON+instance Trapper Show+instance Trapper ToJSVal+++{-|+ Assert is a class for assertion programming. It behaves the same as 'LogJS' but calls+ <https://developer.mozilla.org/en-US/docs/Web/API/Console/assert console.assert> instead of+ other console methods. This will only have an effect if the 'Bool' provided to 'assert' is 'False'.+-}+class Assert (c :: Type -> Constraint) where+ assert :: c a => Bool -> a -> JSM ()++instance Assert ToJSON where+ assert b x = do+ console <- jsg "console"+ json <- jsg "JSON"+ parsed <- json ^. js1 "parse" (toStrict . decodeUtf8 $ encode x)+ () <$ console ^. js2 "assert" (toJSVal b) parsed++instance Assert Show where+ assert b x = do+ console <- jsg "console"+ () <$ console ^. js2 "assert" (toJSVal b) (pack $ show x)++instance Assert ToJSVal where+ assert b x = do+ console <- jsg "console"+ () <$ console ^. js2 "assert" (toJSVal b) (toJSVal x)+++-- | Log a list of JSON objects to the console where it will rendered as a table using <https://developer.mozilla.org/en-US/docs/Web/API/Console/table console.table>+table :: ToJSON a => [a] -> JSM ()+table = logJS @ToJSON "table"+++-- | Log to the console using <https://developer.mozilla.org/en-US/docs/Web/API/Console/log console.log>+log :: forall c a. LogJS c => c a => a -> JSM ()+log = logJS @c "log"+++-- | Log with the "warn" log level using <https://developer.mozilla.org/en-US/docs/Web/API/Console/warn console.warn>+warn :: forall c a. LogJS c => c a => a -> JSM ()+warn = logJS @c "warn"+++-- | Log with the "info" log level using <https://developer.mozilla.org/en-US/docs/Web/API/Console/info console.info>+info :: forall c a. LogJS c => c a => a -> JSM ()+info = logJS @c "info"+++-- | Log with the "debug" log level using <https://developer.mozilla.org/en-US/docs/Web/API/Console/debug console.debug>+debug :: forall c a. LogJS c => c a => a -> JSM ()+debug = logJS @c "debug"+++-- | A unique label for a timer. This is used to tie calls to <https://developer.mozilla.org/en-US/docs/Web/API/Console/time console.time> to <https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd console.timeEnd>+newtype TimeLabel = TimeLabel { unTimeLabel :: Text }+ deriving (Eq, Ord, Show, IsString)+++-- | Start a timer using <https://developer.mozilla.org/en-US/docs/Web/API/Console/time console.time>+time :: TimeLabel -> JSM ()+time (TimeLabel l) = do+ console <- jsg "console"+ () <$ console ^. js1 "time" l+++-- | End a timer and print the milliseconds elapsed since it started using <https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd console.timeEnd>+timeEnd :: TimeLabel -> JSM ()+timeEnd (TimeLabel l) = do+ console <- jsg "console"+ () <$ console ^. js1 "timeEnd" l