miso 0.4.0.0 → 0.5.0.0
raw patch · 18 files changed
+667/−70 lines, 18 filesnew-component:exe:compose-updatenew-component:exe:xhrPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Miso.Lens: get :: Getting a s a -> s -> a
+ Miso.Lens: makeLens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
+ Miso.Lens: set :: Lens s t a b -> b -> s -> t
+ Miso.Lens: type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
+ Miso.Lens: type Getting r s a = (a -> Const r a) -> (s -> Const r s)
+ Miso.Lens: type Lens' s a = Lens s s a a
Files
- README.md +288/−22
- examples/compose-update/Main.hs +105/−0
- examples/mario/Main.hs +2/−2
- examples/router/Main.hs +1/−1
- examples/todo-mvc/Main.hs +1/−1
- examples/websocket/Main.hs +1/−1
- examples/xhr/Main.hs +147/−0
- exe/Main.hs +1/−1
- ghc-src/Miso/TypeLevel.hs +1/−1
- ghcjs-src/Miso.hs +3/−4
- ghcjs-src/Miso/Dev.hs +14/−0
- ghcjs-src/Miso/Effect.hs +27/−11
- ghcjs-src/Miso/FFI.hs +5/−0
- ghcjs-src/Miso/Router.hs +1/−23
- ghcjs-src/Miso/Types.hs +1/−1
- jsbits/diff.js +1/−1
- miso.cabal +34/−1
- src/Miso/Lens.hs +34/−0
README.md view
@@ -33,37 +33,289 @@ </a> </p> -**Miso** is a small "[isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/)" [Haskell](https://www.haskell.org/) front-end framework featuring a virtual-dom, diffing / patching algorithm, event delegation, event batching, SVG, Server-sent events, Websockets, type-safe [servant](https://haskell-servant.github.io/)-style routing and an extensible Subscription-based subsystem. Inspired by [Elm](http://elm-lang.org/), [Redux](http://redux.js.org/) and [Bobril](http://github.com/bobris/bobril). **Miso** is pure by default, but side effects (like `XHR`) can be introduced into the system via the `Effect` data type. **Miso** makes heavy use of the [GHCJS](https://github.com/ghcjs/ghcjs) FFI and therefore has minimal dependencies.+**Miso** is a small "[isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/)" [Haskell](https://www.haskell.org/) front-end framework featuring a virtual-dom, diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, Server-sent events, Websockets, type-safe [servant](https://haskell-servant.github.io/)-style routing and an extensible Subscription-based subsystem. Inspired by [Elm](http://elm-lang.org/), [Redux](http://redux.js.org/) and [Bobril](http://github.com/bobris/bobril). **Miso** is pure by default, but side effects (like `XHR`) can be introduced into the system via the `Effect` data type. **Miso** makes heavy use of the [GHCJS](https://github.com/ghcjs/ghcjs) FFI and therefore has minimal dependencies. ## Table of Contents+- [Quick Start](#quick-start)+ - [Stack](#stack)+ - [Nix](#nix)+ - [Cabal](#cabal)+ - [GHCJSi Caveats](#ghcjsi-caveats)+ - [Architecture](#architecture) - [Examples](#examples)+ - [TodoMVC](#todomvc)+ - [Mario](#mario)+ - [Websocket](#websocket)+ - [SSE](#sse)+ - [XHR](#xhr)+ - [Router](#router)+ - [SVG](#svg)+ - [Simple](#simple) - [Haddocks](#haddocks)+ - [GHC](#ghc)+ - [GHCJS](#ghcjs) - [Sample Application](#sample-application) - [Building examples](#building-examples)+- [Maintainers](#maintainers)+- [Contributing](#contributing)+- [License](#license) +## Quick start+To get started quickly building applications, we recommend using the [`stack`](https://docs.haskellstack.org/en/stable/README/) or [`nix`](https://nixos.org/nix) package managers. Obtaining [`GHCJS`](https://github.com/ghcjs/ghcjs) is required as a prerequisite. `stack` and `nix` make this process easy, if you're using `cabal` we assume you have [obtained `GHCJS`](https://github.com/ghcjs/ghcjs#installation) by other means.++All source code depicted below for the quick start app is available [here](https://github.com/dmjio/miso/tree/master/sample-app).++### Stack+In the `miso` repository there is a [folder named `stack`](https://github.com/dmjio/miso/tree/master/stack) with "known to work" configurations for `GHCJS`. One stack file exists for both the `7.10.3` and `8.0.1` versions of `GHCJS`. In general, we recommend developing with the `7.10.3` version since it currently supports `GHCJSi` (a REPL that connects to the browser by way of a [`nodejs`](https://nodejs.org/en/) web server using [`socket.io`](https://socket.io/)) and building with the `8.0.1` version (if possible). For more information on using `stack` with `GHCJS`, please consult the [GHCJS section of the `stack` docs](https://docs.haskellstack.org/en/stable/ghcjs/).++To begin, create the following directory layout+```bash+➜ mkdir app && touch app/{Main.hs,app.cabal,stack.yaml} && tree app+app+|-- Main.hs+|-- app.cabal+`-- stack.yaml+```++Add a `stack.yaml` file that uses a recent version of `miso`.+```bash+➜ cat app/stack.yaml+resolver: lts-6.20+compiler: ghcjs-0.2.0.9006020_ghc-7.10.3+compiler-check: match-exact++packages:+ - '.'+extra-deps:+ - miso-0.4.0.0++setup-info:+ ghcjs:+ source:+ ghcjs-0.2.0.9006020_ghc-7.10.3:+ url: http://ghcjs.tolysz.org/lts-6.20-9006020.tar.gz+ sha1: a6cea90cd8121eee3afb201183c6e9bd6bacd94a+```++Add a `cabal` file+```bash+➜ cat app/*.cabal+name: app+version: 0.1.0.0+synopsis: First miso app+category: Web+build-type: Simple+cabal-version: >=1.10++executable app+ main-is: Main.hs+ build-depends: base, miso+ default-language: Haskell2010+```++Add the source from [Sample Application](#sample-application) to `app/Main.hs`++Run `stack setup`. This might take a long time, since it will have to build `GHCJS`.+```+stack setup+```++Run `stack build` to get the static assets+```+stack build+```++See the result+```+open $(stack path --local-install-root)/bin/app.jsexe/index.html+```++Using GHCJSi+```+stack ghci+```++If that warns with `socket.io not found, browser session not available`, you'll need to install `socket.io`+```+npm install socket.io+```++and update your `NODE_PATH`+```+export NODE_PATH=$(pwd)/node_modules+```++Now you should be connected, and the app viewable in `GHCJSi` (open http://localhost:6400).+```bash+➜ stack ghci+app-0.1.0.0: initial-build-steps (exe)+Configuring GHCi with the following packages: app+GHCJSi, version 0.2.0.9006020-7.10.3: http://www.github.com/ghcjs/ghcjs/ :? for help+[1 of 1] Compiling Main ( /Users/david/Desktop/miso/sample-app/Main.hs, interpreted )+socket.io found, browser session available at http://localhost:6400+Ok, modules loaded: Main.+*Main> main+browser connected, code runs in browser from now on+```++### Nix+`Nix` is a more powerful option for building web applications with `miso` since it encompasses development workflow, configuration management, and deployment. The source code for [`haskell-miso.org`](https://github.com/dmjio/miso/tree/master/examples/haskell-miso.org) is an example of this.++If unfamiliar with `nix`, we recommend [@Gabriel439](https://github.com/Gabriel439)'s ["Nix and Haskell in production"](https://github.com/Gabriel439/haskell-nix) guide.++To get started, we will use the [`cabal2nix`](https://github.com/NixOS/cabal2nix) tool to convert our `Cabal` file into a `nix` derivation (named `app.nix`). We'll then write a file named `default.nix`, which is used for building our project (via `nix-build`) and development (via `nix-shell`).++To begin, make the following directory layout:+```bash+➜ mkdir app && touch app/{Main.hs,app.cabal,default.nix,app.nix} && tree app+app+|-- Main.hs+|-- app.cabal+|-- default.nix+`-- app.nix+```++Add a `cabal` file+```bash+➜ cat app/*.cabal+name: app+version: 0.1.0.0+synopsis: First miso app+category: Web+build-type: Simple+cabal-version: >=1.10++executable app+ main-is: Main.hs+ build-depends: base, miso+ default-language: Haskell2010+```++Use [`cabal2nix`](https://github.com/NixOS/cabal2nix) to generate a file named `app.nix`+that looks like below.+```bash+➜ cabal2nix . --compiler ghcjs > app.nix+➜ cat app.nix+```++```nix+{ mkDerivation, base, miso, stdenv }:+mkDerivation {+ pname = "app";+ version = "0.1.0.0";+ src = ./.;+ isLibrary = false;+ isExecutable = true;+ executableHaskellDepends = [ base miso ];+ description = "First miso app";+ license = stdenv.lib.licenses.unfree;+}+```++Write a `default.nix` (which calls `app.nix`), this fetches a recent version of `miso`.+```nix+{ pkgs ? import <nixpkgs> {} }:+let+ result = import (pkgs.fetchFromGitHub {+ owner = "dmjio";+ repo = "miso";+ sha256 = "13ckz11gbfs047hl3phj7h6fm59wsg9zw2fiqjaqkxmxv17zj5yj";+ rev = "0834d5c0b309de24d836cbdcc25fd257de10be17";+ }) {};+in pkgs.haskell.packages.ghcjs.callPackage ./app.nix {+ miso = result.miso-ghcjs;+}+```++Build the project+```+nix-build+```++Open the result+```+open ./result/bin/app.jsexe/index.html+```++For development with `nix`, it's important to have `cabal` present for building. This command will make it available in your `PATH`.+```+nix-env -iA cabal-install -f '<nixpkgs>'+```++To be put into a shell w/ `GHCJS` and all the dependencies for this project present, use `nix-shell`.+```+nix-shell -A env+```++To open `GHCJSi` (`NODE_PATH` should already be set properly)+```+$ cabal configure --ghcjs+$ cabal repl+Package has never been configured. Configuring with default flags. If this+fails, please run configure manually.+Resolving dependencies...+Configuring app-0.1.0.0...+Preprocessing executable 'app' for app-0.1.0.0...+GHCJSi, version 0.2.0-7.10.3: http://www.github.com/ghcjs/ghcjs/ :? for help+[1 of 1] Compiling Main ( Main.hs, interpreted )+Ok, modules loaded: Main.+*Main>+browser connected, code runs in browser from now on+```++### Cabal+The latest stable version of `miso` will be available on Hackage.+To build with cabal, we assume `ghcjs` is in your `PATH` and `ghcjs-base` is present in your `ghcjs-pkg` list.+```bash+cabal sandbox init+cabal install --ghcjs+cabal build+open dist/build/app/app.jsexe/index.html+```++### GHCJSi Caveats+If you run `main` in `GHCJSi`, interrupt it and then run it again, you+will end up with two copies of your app displayed above each other. As+a workaround, you can use `clearBody >> main` which will completely+clear the document body before rendering your application.++### Architecture+For constructing client and server applications, we recommend using one `cabal` file with two executable sections, where the `buildable` attribute set is contingent on the compiler. An example of this layout is [here](https://github.com/dmjio/miso/blob/master/examples/haskell-miso.org/haskell-miso.cabal#L16-L60). For more info on how to use `stack` with a `client`/`server` setup, see this [link](https://docs.haskellstack.org/en/stable/ghcjs/#project-with-both-client-and-server). For more information on how to use `nix` with a `client`/`server` setup, see the [nix scripts](https://github.com/dmjio/miso/blob/master/examples/haskell-miso.org/default.nix) for [https://haskell-miso.org](https://haskell-miso.org).+ ## Examples- - TodoMVC- - [Link](https://todo-mvc.haskell-miso.org/)- - [Source](https://github.com/dmjio/miso/blob/master/examples/todo-mvc/Main.hs)- - Mario- - [Link](https://mario.haskell-miso.org/)- - [Source](https://github.com/dmjio/miso/blob/master/examples/mario/Main.hs)- - Websocket- - [Link](https://websocket.haskell-miso.org/)- - [Source](https://github.com/dmjio/miso/blob/master/examples/websocket/Main.hs)- - Router- - [Link](https://router.haskell-miso.org/)- - [Source](https://github.com/dmjio/miso/blob/master/examples/router/Main.hs)- - SVG- - [Link](https://svg.haskell-miso.org/)- - Simple- - [Link](https://simple.haskell-miso.org/)- - [Source](https://github.com/dmjio/miso/blob/master/exe/Main.hs) +### TodoMVC+ - [Link](https://todo-mvc.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/examples/todo-mvc/Main.hs)++### Mario+ - [Link](https://mario.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/examples/mario/Main.hs)++### Websocket+ - [Link](https://websocket.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/examples/websocket/Main.hs)++### SSE+ - [Link](https://sse.haskell-miso.org/) / [Client](https://github.com/dmjio/miso/blob/master/examples/sse/client/Main.hs) / [Server](https://github.com/dmjio/miso/blob/master/examples/sse/server/Main.hs)++### XHR+ - [Link](https://xhr.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/examples/xhr/Main.hs)++### Router+ - [Link](https://router.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/examples/router/Main.hs)++### SVG+ - [Link](https://svg.haskell-miso.org/)++### Simple+ - [Link](https://simple.haskell-miso.org/) / [Source](https://github.com/dmjio/miso/blob/master/exe/Main.hs)+ ## Haddocks- - [GHCJS](https://haddocks.haskell-miso.org/)- - [GHC](http://hackage.haskell.org/package/miso) +### GHCJS+ - [Link](https://haddocks.haskell-miso.org/)++### GHC+ - [Link](http://hackage.haskell.org/package/miso)+ ## Sample application ```haskell -- | Haskell language pragma@@ -91,14 +343,14 @@ main = startApp App {..} where initialAction = SayHelloWorld -- initial action to be executed on application load- model = 0 -- initial model+ model = 0 -- initial model update = updateModel -- update function view = viewModel -- view function events = defaultEvents -- default delegated events subs = [] -- empty subscription list -- | Updates model, optionally introduces side effects-updateModel :: Action -> Model -> Effect Model Action+updateModel :: Action -> Model -> Effect Action Model updateModel AddOne m = noEff (m + 1) updateModel SubtractOne m = noEff (m - 1) updateModel NoOp m = noEff m@@ -153,3 +405,17 @@ cd result/examples/todo-mvc.jsexe && python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... ```++## Maintainers++[@dmjio](https://github.com/dmjio)++## Contributing++Feel free to dive in! [Open an issue](https://github.com/dmjio/miso/issues/new) or submit [PRs](https://github.com/dmjio/miso/pulls).++See [CONTRIBUTING](https://github.com/dmjio/miso/blob/master/CONTRIBUTING.md) for more info.++## License++[BSD3](LICENSE) © David Johnson
+ examples/compose-update/Main.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+module Main where++-- This example demonstrates how you can split your update function+-- into separate update functions for parts of your model and then+-- combine them into a single update function operating on the whole+-- model which combines their effects.++import Control.Monad+import Data.Monoid++import Miso+import Miso.Lens++-- In this slightly contrived example, our model consists of two+-- counters. When one of those counters is incremented, the other is+-- decremented and the other way around.+type Model = (Int, Int)++data Action+ = Increment+ | Decrement+ | NoOp+ deriving (Show, Eq)++-- We are going to use 'Lens'es in this example. Since @miso@ does not+-- depend on a lens library we are going to define a couple of+-- utilities ourselves. We recommend that in your own applications,+-- you depend on a lens library such as @lens@ or @microlens@ to get+-- these definitions.++-- | You can find this under the same name in @lens@ and+-- @microlens@. @lens@ also provides the infix operator '%%~' as a+-- synonym for 'traverseOf'.+--+-- In this example we are only going to use this when applied to+-- 'Lens' m a' and using 'Effect Action' for the @f@ type variable. In+-- that case the specialized type signature is:+--+-- @traverseOf :: Functor f => Lens' m a -> (a -> Effect Action a) -> s -> Effect action s+traverseOf :: Functor f => Lens s t a b -> (a -> f b) -> s -> f t+traverseOf = id++-- | A lens into the first element of a tuple. Both @lens@ and+-- @microlens@ provide this under the same name.+_1 :: Lens (a,c) (b,c) a b+_1 f (a,c) = (,c) <$> f a++-- | A lens into the second element of a tuple. Both @lens@ and+-- @microlens@ provide this under the same name.+_2 :: Lens (c,a) (c,b) a b+_2 f (c,a) = (c,) <$> f a++-- | Update function for the first counter in our 'Model'.+updateFirstCounter :: Action -> Int -> Effect Action Int+updateFirstCounter Increment m = noEff (m + 1)+updateFirstCounter Decrement m = noEff (m - 1)+updateFirstCounter NoOp m = noEff m++-- | Update function for the second counter in our 'Model'. As we’ve+-- mentioned before, this counter is decremented when the first+-- counter is incremented and the other way around.+updateSecondCounter :: Action -> Int -> Effect Action Int+updateSecondCounter Increment m = noEff (m - 1)+updateSecondCounter Decrement m = noEff (m + 1)+updateSecondCounter NoOp m = noEff m++-- | This is the combined update function for both counters.+updateModel :: Action -> Model -> Effect Action Model+updateModel act =+ let -- We use 'traverseOf' to lift an update function for one+ -- counter to an update function that operates on both+ -- counters. The lifted function leaves the other counter+ -- untouched.+ liftedUpdateFirst :: Model -> Effect Action Model+ liftedUpdateFirst = traverseOf _1 (updateFirstCounter act)+ liftedUpdateSecond :: Model -> Effect Action Model+ liftedUpdateSecond = traverseOf _2 (updateSecondCounter act)+ in -- Since 'Effect Action' is an instance of 'Monad', we can just+ -- use '<=<' to compose these lifted update functions. It might+ -- be helpful to look at the type signature of '<=<' specialized+ -- for 'Effect Action':+ --+ -- @(<=<) :: (b -> Effect Action c) -> (a -> Effect Action b) -> a -> Effect Action c+ liftedUpdateFirst <=< liftedUpdateSecond++main :: IO ()+main = startApp App { initialAction = NoOp, ..}+ where+ model = (0, 0)+ update = updateModel+ view = viewModel+ events = defaultEvents+ subs = []++viewModel :: Model -> View Action+viewModel (x, y) =+ div_+ []+ [ button_ [onClick Increment] [text "+"]+ , text (show x <> " | " <> show y)+ , button_ [onClick Decrement] [text "-"]+ ]
examples/mario/Main.hs view
@@ -63,7 +63,7 @@ , window = (0,0) } -updateMario :: Action -> Model -> Effect Model Action+updateMario :: Action -> Model -> Effect Action Model updateMario NoOp m = noEff m updateMario (GetArrows arrs) m = step newModel where@@ -77,7 +77,7 @@ where newModel = m { window = coords } -step :: Model -> Effect Model Action+step :: Model -> Effect Action Model step m@Model{..} = k <# do Time <$> now where k = m & gravity delta
examples/router/Main.hs view
@@ -43,7 +43,7 @@ view = viewModel -- | Update your model-updateModel :: Action -> Model -> Effect Model Action+updateModel :: Action -> Model -> Effect Action Model updateModel (HandleURI u) m = m { uri = u } <# do pure NoOp updateModel (ChangeURI u) m = m <# do
examples/todo-mvc/Main.hs view
@@ -86,7 +86,7 @@ events = defaultEvents subs = [] -updateModel :: Msg -> Model -> Effect Model Msg+updateModel :: Msg -> Model -> Effect Msg Model updateModel NoOp m = noEff m updateModel (CurrentTime n) m = m <# do print n >> pure NoOp
examples/websocket/Main.hs view
@@ -31,7 +31,7 @@ uri = URL "wss://echo.websocket.org" protocols = Protocols [ ] -updateModel :: Action -> Model -> Effect Model Action+updateModel :: Action -> Model -> Effect Action Model updateModel (HandleWebSocket (WebSocketMessage (Message m))) model = noEff model { received = m } updateModel (SendMessage msg) model = model <# do send msg >> pure Id
+ examples/xhr/Main.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+module Main where++import Data.Aeson+import Data.Aeson.Types+import qualified Data.Map as M+import Data.Maybe+import GHC.Generics+import JavaScript.Web.XMLHttpRequest++import Miso hiding (defaultOptions)+import Miso.String++-- | Model+data Model+ = Model+ { info :: Maybe APIInfo+ } deriving (Eq, Show)++-- | Action+data Action+ = FetchGitHub+ | SetGitHub APIInfo+ | NoOp+ deriving (Show, Eq)++-- | Main entry point+main :: IO ()+main = do+ startApp App { model = Model Nothing+ , initialAction = NoOp+ , ..+ }+ where+ update = updateModel+ events = defaultEvents+ subs = []+ view = viewModel++-- | Update your model+updateModel :: Action -> Model -> Effect Action Model+updateModel FetchGitHub m = m <# do+ SetGitHub <$> getGitHubAPIInfo+updateModel (SetGitHub apiInfo) m =+ noEff m { info = Just apiInfo }+updateModel NoOp m = noEff m++-- | View function, with routing+viewModel :: Model -> View Action+viewModel Model {..} = view+ where+ view = div_ [ style_ $ M.fromList [+ (pack "text-align", pack "center")+ , (pack "margin", pack "200px")+ ]+ ] [+ h1_ [class_ $ pack "title" ] [ text $ pack "Miso XHR Example" ]+ , button_ attrs [+ text $ pack "Fetch JSON from https://api.github.com via XHR"+ ]+ , case info of+ Nothing -> div_ [] [ text $ pack "No data" ]+ Just APIInfo{..} ->+ table_ [ class_ $ pack "table is-striped" ] [+ thead_ [] [+ tr_ [] [+ th_ [] [ text $ pack "URLs"]+ ]+ ]+ , tbody_ [] [+ tr_ [] [ td_ [] [ text current_user_url ] ]+ , tr_ [] [ td_ [] [ text emojis_url ] ]+ , tr_ [] [ td_ [] [ text emails_url ] ]+ , tr_ [] [ td_ [] [ text events_url ] ]+ , tr_ [] [ td_ [] [ text gists_url ] ]+ , tr_ [] [ td_ [] [ text feeds_url ] ]+ , tr_ [] [ td_ [] [ text followers_url ] ]+ , tr_ [] [ td_ [] [ text following_url ] ]+ ]+ ]+ ]+ where+ attrs = [ onClick FetchGitHub+ , class_ $ pack "button is-large is-outlined"+ ] +++ [ disabled_ $ pack "disabled"+ | isJust info+ ]++data APIInfo+ = APIInfo+ { current_user_url :: MisoString+ , current_user_authorizations_html_url :: MisoString+ , authorizations_url :: MisoString+ , code_search_url :: MisoString+ , commit_search_url :: MisoString+ , emails_url :: MisoString+ , emojis_url :: MisoString+ , events_url :: MisoString+ , feeds_url :: MisoString+ , followers_url :: MisoString+ , following_url :: MisoString+ , gists_url :: MisoString+ , hub_url :: MisoString+ , issue_search_url :: MisoString+ , issues_url :: MisoString+ , keys_url :: MisoString+ , notifications_url :: MisoString+ , organization_repositories_url :: MisoString+ , organization_url :: MisoString+ , public_gists_url :: MisoString+ , rate_limit_url :: MisoString+ , repository_url :: MisoString+ , repository_search_url :: MisoString+ , current_user_repositories_url :: MisoString+ , starred_url :: MisoString+ , starred_gists_url :: MisoString+ , team_url :: MisoString+ , user_url :: MisoString+ , user_organizations_url :: MisoString+ , user_repositories_url :: MisoString+ , user_search_url :: MisoString+ } deriving (Show, Eq, Generic)++instance FromJSON APIInfo where+ parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelTo '_' }++getGitHubAPIInfo :: IO APIInfo+getGitHubAPIInfo = do+ Just resp <- contents <$> xhrByteString req+ case eitherDecodeStrict resp :: Either String APIInfo of+ Left s -> error s+ Right j -> pure j+ where+ req = Request { reqMethod = GET+ , reqURI = pack "https://api.github.com"+ , reqLogin = Nothing+ , reqHeaders = []+ , reqWithCredentials = False+ , reqData = NoData+ }+
exe/Main.hs view
@@ -14,7 +14,7 @@ events = defaultEvents subs = [] -updateModel :: Action -> Model -> Effect Model Action+updateModel :: Action -> Model -> Effect Action Model updateModel AddOne m = noEff (m + 1) updateModel SubtractOne m = noEff (m - 1) updateModel NoOp m = noEff m
ghc-src/Miso/TypeLevel.hs view
@@ -15,5 +15,5 @@ ToServerRoutes b wrapper action ToServerRoutes (a :> b) wrapper action = a :> ToServerRoutes b wrapper action- ToServerRoutes (View _) wrapper action =+ ToServerRoutes (View a) wrapper action = Get '[HTML] (wrapper (View action))
ghcjs-src/Miso.hs view
@@ -123,11 +123,10 @@ -- | Helper foldEffects :: (action -> IO ())- -> (action -> model -> Effect model action)+ -> (action -> model -> Effect action model) -> (model, IO ()) -> action -> (model, IO ()) foldEffects sink update = \(model, as) action -> case update action model of- NoEffect newModel -> (newModel, as)- Effect newModel eff -> (newModel, newAs)+ Effect newModel effs -> (newModel, newAs) where- newAs = as >> do void . forkIO . sink =<< eff+ newAs = as >> (mapM_ (forkIO . sink =<<) effs)
+ ghcjs-src/Miso/Dev.hs view
@@ -0,0 +1,14 @@+-----------------------------------------------------------------------------+-- |+-- Module : Miso.Dev+-- Copyright : (C) 2016-2017 David M. Johnson+-- License : BSD3-style (see the file LICENSE)+-- Maintainer : David M. Johnson <djohnson.m@gmail.com>+-- Stability : experimental+-- Portability : non-portable+----------------------------------------------------------------------------+module Miso.Dev+ ( clearBody+ ) where++import Miso.FFI (clearBody)
ghcjs-src/Miso/Effect.hs view
@@ -21,19 +21,35 @@ import Miso.Effect.XHR import Miso.Effect.DOM --- | Capturing effects in update actions-data Effect model action- = NoEffect model- | Effect model (IO action)+-- | An effect represents the results of an update action.+--+-- It consists of the updated model and a list of actions. Each action+-- is run in a new thread so there is no risk of accidentally+-- blocking the application.+data Effect action model+ = Effect model [IO action] --- | `NoEffect` smart constructor-noEff :: model -> Effect model action-noEff = NoEffect+instance Functor (Effect action) where+ fmap f (Effect m acts) = Effect (f m) acts --- | `Effect` smart constructor-(<#) :: model -> IO action -> Effect model action-(<#) = Effect+instance Applicative (Effect action) where+ pure m = Effect m []+ Effect fModel fActs <*> Effect xModel xActs = Effect (fModel xModel) (fActs ++ xActs) +instance Monad (Effect action) where+ return = pure+ Effect m acts >>= f =+ case f m of+ Effect m' acts' -> Effect m' (acts ++ acts')++-- | Smart constructor for an 'Effect' with no actions.+noEff :: model -> Effect action model+noEff m = Effect m []++-- | Smart constructor for an 'Effect' with exactly one action.+(<#) :: model -> IO action -> Effect action model+(<#) m a = Effect m [a]+ -- | `Effect` smart constructor, flipped-(#>) :: IO action -> model -> Effect model action+(#>) :: IO action -> model -> Effect action model (#>) = flip (<#)
ghcjs-src/Miso/FFI.hs view
@@ -23,6 +23,7 @@ , item , jsvalToValue , delegateEvent+ , clearBody ) where import Control.Monad@@ -127,3 +128,7 @@ -> Callback (IO JSVal) -- ^ Virtual DOM callback -> IO () +-- | Clear the document body. This is particularly useful to avoid+-- creating multiple copies of your app when running in GHCJSi.+foreign import javascript unsafe "document.body.innerHTML = '';"+ clearBody :: IO ()
ghcjs-src/Miso/Router.hs view
@@ -43,6 +43,7 @@ import Web.HttpApiData import Miso.Html hiding (text)+import Miso.Lens -- | Router terminator. -- The 'HasRouter' instance for 'View' finalizes the router.@@ -203,29 +204,6 @@ } class HasURI m where lensURI :: Lens' m URI--type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t-type Lens' s a = Lens s s a a-type Getting r s a = (a -> Const r a) -> (s -> Const r s)--newtype Const r a = Const { runConst :: r }- deriving Functor--newtype Id a = Id { runId :: a }- deriving (Functor)--instance Applicative Id where- pure = Id- Id f <*> Id x = Id (f x)--get :: Getting a s a -> s -> a-get l = \ s -> runConst (l Const s)--set :: Lens s t a b -> b -> s -> t-set l b = \ s -> runId (l (\ _ -> A.pure b) s)--makeLens :: (s -> a) -> (s -> b -> t) -> Lens s t a b-makeLens get' upd = \ f s -> upd s `fmap` f (get' s) getURI :: HasURI m => m -> URI getURI = get lensURI
ghcjs-src/Miso/Types.hs view
@@ -20,7 +20,7 @@ data App model action = App { model :: model -- ^ initial model- , update :: action -> model -> Effect model action+ , update :: action -> model -> Effect action model -- ^ Function to update model, optionally provide effects , view :: model -> View action -- ^ Function to draw `View`
jsbits/diff.js view
@@ -15,7 +15,7 @@ } function diffTextNodes (c, n) {- if (c.text !== n.text) c.domRef.replaceData (0, c.domRef.length, n.text);+ if (c.text !== n.text) c.domRef.textContent = n.text; n.domRef = c.domRef; }
miso.cabal view
@@ -1,5 +1,5 @@ name: miso-version: 0.4.0.0+version: 0.5.0.0 category: Web, Miso, Data Structures license: BSD3 license-file: LICENSE@@ -53,6 +53,23 @@ default-language: Haskell2010 +executable xhr+ main-is:+ Main.hs+ if !impl(ghcjs) || !flag(examples)+ buildable: False+ else+ hs-source-dirs:+ examples/xhr+ build-depends:+ aeson,+ base < 5,+ containers,+ ghcjs-base,+ miso+ default-language:+ Haskell2010+ executable router main-is: Main.hs@@ -101,6 +118,20 @@ default-language: Haskell2010 +executable compose-update+ main-is:+ Main.hs+ if !impl(ghcjs) || !flag(examples)+ buildable: False+ else+ hs-source-dirs:+ examples/compose-update+ build-depends:+ base < 5,+ miso+ default-language:+ Haskell2010+ executable simple main-is: Main.hs@@ -144,6 +175,7 @@ Miso.Html.Element Miso.Html.Event Miso.Html.Property+ Miso.Lens Miso.Event Miso.Event.Decoder Miso.Event.Types@@ -186,6 +218,7 @@ jsbits/isomorphic.js jsbits/util.js exposed-modules:+ Miso.Dev Miso.Effect Miso.Effect.Storage Miso.Effect.XHR
+ src/Miso/Lens.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE RankNTypes #-}+-----------------------------------------------------------------------------+-- |+-- Module : Miso.Lens+-- Copyright : (C) 2016-2017 David M. Johnson+-- License : BSD3-style (see the file LICENSE)+-- Maintainer : David M. Johnson <djohnson.m@gmail.com>+-- Stability : experimental+-- Portability : non-portable+----------------------------------------------------------------------------+module Miso.Lens+ ( Lens+ , Lens'+ , Getting+ , get+ , set+ , makeLens+ ) where++import Data.Functor.Identity+import Control.Applicative++type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t+type Lens' s a = Lens s s a a+type Getting r s a = (a -> Const r a) -> (s -> Const r s)++get :: Getting a s a -> s -> a+get l = \ s -> getConst (l Const s)++set :: Lens s t a b -> b -> s -> t+set l b = \s -> runIdentity (l (\ _ -> pure b) s)++makeLens :: (s -> a) -> (s -> b -> t) -> Lens s t a b+makeLens get' upd = \ f s -> upd s `fmap` f (get' s)