aeson-diff-generic 0.0.1 → 0.0.2
raw patch · 9 files changed
+322/−21 lines, 9 filesdep +base-compatdep +faildep +natsdep −mtldep ~aesondep ~aeson-diffdep ~basesetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: base-compat, fail, nats, semigroups, transformers, transformers-compat
Dependencies removed: mtl
Dependency ranges changed: aeson, aeson-diff, base, bytestring, containers, dlist, hashable, scientific, tagged, template-haskell, text, th-abstraction, time, unordered-containers, uuid-types, vector
API changes (from Hackage documentation)
Files
- .travis.yml +89/−0
- Data/Aeson/Diff/Generic.hs +7/−4
- Data/Aeson/Diff/Generic/Instances.hs +2/−2
- Data/Aeson/Diff/Generic/TH.hs +3/−3
- Data/Aeson/Diff/Generic/Types.hs +4/−3
- README.md +155/−0
- Setup.hs +0/−0
- aeson-diff-generic.cabal +47/−9
- changelog.md +15/−0
+ .travis.yml view
@@ -0,0 +1,89 @@+# This file has been generated -- see https://github.com/hvr/multi-ghc-travis+language: c+sudo: false++cache:+ directories:+ - $HOME/.cabsnap+ - $HOME/.cabal/packages++before_cache:+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar++matrix:+ include:+ - env: CABALVER=1.22 GHCVER=7.10.2+ compiler: ": #GHC 7.10.2"+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}+ - env: CABALVER=1.24 GHCVER=8.0.2+ compiler: ": #GHC 8.0.2"+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}+ - env: CABALVER=2.0 GHCVER=8.2.2+ compiler: ": #GHC 8.2.2"+ addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}+ - env: CABALVER=head GHCVER=head+ compiler: ": #GHC head"+ addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}++ allow_failures:+ - env: CABALVER=head GHCVER=head++before_install:+ - unset CC+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH++install:+ - cabal --version+ - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];+ then+ zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >+ $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;+ fi+ - travis_retry cabal update -v+ - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config+ - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt+ - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt++# check whether current requested install-plan matches cached package-db snapshot+ - if diff -u $HOME/.cabsnap/installplan.txt installplan.txt;+ then+ echo "cabal build-cache HIT";+ rm -rfv .ghc;+ cp -a $HOME/.cabsnap/ghc $HOME/.ghc;+ cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;+ else+ echo "cabal build-cache MISS";+ rm -rf $HOME/.cabsnap;+ mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;+ cabal install --only-dependencies --enable-tests --enable-benchmarks;+ fi++# snapshot package-db on cache miss+ - if [ ! -d $HOME/.cabsnap ];+ then+ echo "snapshotting package-db to build-cache";+ mkdir $HOME/.cabsnap;+ cp -a $HOME/.ghc $HOME/.cabsnap/ghc;+ cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;+ fi++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ - if [ -f configure.ac ]; then autoreconf -i; fi+ - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging+ - cabal build # this builds all libraries and executables (including tests/benchmarks)+ - cabal test+ - cabal check+ - cabal haddock # tests that documentation can be generated+ - cabal sdist # tests that a source-distribution can be generated++# Check that the resulting source distribution can be built & installed.+# If there are no other `.tar.gz` files in `dist`, this can be even simpler:+# `cabal install --force-reinstalls dist/*-*.tar.gz`+ - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&+ (cd dist && cabal install --force-reinstalls "$SRC_TGZ")++# EOF
Data/Aeson/Diff/Generic.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, MultiWayIf,- ExistentialQuantification #-}+{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts,+ ExistentialQuantification #-} {-| This library allows you to apply a json-patch document (<https://tools.ietf.org/html/rfc6902 rfc6902>) directly to a- haskell datatype. It's based on the "Data.Aeson.Diff" module.+ haskell datatype. A JSON Patch document is a sequence+of instructions to modify a JSON value. This library allows you to+modify the haskell datatype directly by matching it with its JSON+representation. It is suitable for use with the HTTP PATCH method. -} module Data.Aeson.Diff.Generic@@ -27,7 +30,7 @@ patch :: JsonPatch a => Patch -> a -> Result a patch = foldr (>=>) pure . map applyOperation . patchOperations {-# NOINLINE patch #-}-{-# RULES "patch/Value" patch = Diff.patch #-} +{-# RULES "patch/Value" patch = Diff.patch #-} -- | Apply a single operation to the data. applyOperation :: JsonPatch a => Operation -> a -> Result a
Data/Aeson/Diff/Generic/Instances.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, MultiWayIf,+{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, ExistentialQuantification, TemplateHaskell, StandaloneDeriving, GeneralizedNewtypeDeriving#-} {-# OPTIONS_GHC -fno-warn-orphans #-}@@ -56,7 +56,7 @@ import Data.Functor.Compose import Data.Functor.Product import Data.Functor.Sum-import Data.Functor.Const+import Control.Applicative (Const(..)) import Data.Functor.Classes import Data.Tree (Tree(..))
Data/Aeson/Diff/Generic/TH.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, MultiWayIf,+{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, ConstraintKinds, ExistentialQuantification, TemplateHaskell, PatternGuards #-} {-| This module contains functions to automatically derive `JsonPatch` instances.@@ -15,7 +15,7 @@ import Language.Haskell.TH.Datatype import Data.List import Data.Aeson.Diff.Generic.Types-import Text.Read+import Text.Read.Compat import qualified Data.Text as T import Control.Monad import Data.Maybe@@ -41,7 +41,7 @@ vars <- mapM (const $ newName "a") sigVars let appliedType = foldl appT (conT name) $ map varT vars constrained =- forallT (map plainTV vars) $ + forallT (map PlainTV vars) $ mapM (\v -> [t| JsonPatch $(varT v) |]) vars pathLensSig <- sigD pathLensName $ constrained
Data/Aeson/Diff/Generic/Types.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, DefaultSignatures,- MultiWayIf, ExistentialQuantification, TemplateHaskell #-}+ ExistentialQuantification, TemplateHaskell, TupleSections #-} module Data.Aeson.Diff.Generic.Types where import Data.Aeson.Types import Data.Aeson.Pointer as Pointer-import Control.Monad import Data.Dynamic+import Control.Applicative ((<$>), pure) -- | An existentially quantified getter and setter. The data inside -- is manipulated using json conversion functions (`toJSON`,@@ -63,7 +63,8 @@ deleteAtPointer (Pointer [key]) s f = deleteAt key s f deleteAtPointer (Pointer (key:path)) s f = do GetSet v setr <- fieldLens key s- join $ traverse setr <$> deleteAtPointer (Pointer path) v f+ (r, s2) <- deleteAtPointer (Pointer path) v f+ (r, ) <$> setr s2 -- | Add a value at the pointer. To insert a json `Value`, use -- `fromJSON` as the helper function, to insert a `Dynamic` use `getDynamic`.
+ README.md view
@@ -0,0 +1,155 @@+# Welcom to `aeson-diff-generic` [](https://hackage.haskell.org/package/aeson-diff-generic) [](https://travis-ci.org/kuribas/aeson-diff-generic)++aeson-diff-generic is a haskell library that allows you to apply a+JSON patch [rfc6902](https://tools.ietf.org/html/rfc6902) document+directly to a haskell datatype. A JSON Patch document is a sequence+of instructions to modify a JSON value. It is suitable for use with +the HTTP PATCH method. ++Suppose you are writing a client/server application with+auto-save. Every time the user makes a change to the document, the+client needs to tell the server about it. If the document being edited+is large, sending the entire updated document is impractical, so you+want to send a diff instead. JSON patch+[rfc6902](https://tools.ietf.org/html/rfc6902) is a standard format+for representing diffs, but it applies to JSON documents, not Haskell+values. Let's say our document is represented by a Haskell value of+type Doc: we need a Doc patch, not a json patch.++The aeson library uses GHC.Generics or template haskell to define a+default json encoding for algebraic datatypes. The aeson-diff-generic+can generate code, using options given to aeson, to interpret a json+patch as a Doc patch.++# Example++Suppose we have a datatype for which we have a ToJSON and FromJSON instance:++```haskell+{-# LANGUAGE DeriveGeneric, TemplateHaskell, OverloadedStrings #-}+import Data.Aeson+import Data.Aeson.Diff+import Data.Aeson.Diff.Generic+import GHC.Generics++data Pet = Bird | Cat | Dog | Fish+ deriving (Show, Eq, Generic)++data Person = Person+ { name :: String+ , age :: Int+ , pet :: Pet+ } deriving (Show, Eq, Generic)++instance ToJSON Pet where+ toJSON = genericToJSON defaultOptions+ toEncoding = genericToEncoding defaultOptions+ +instance FromJSON Pet where+ parseJSON = genericParseJSON defaultOptions++instance ToJSON Person where+ toJSON = genericToJSON defaultOptions+ toEncoding = genericToEncoding defaultOptions+ +instance FromJSON Person where+ parseJSON = genericParseJSON defaultOptions+```++We can now create a JsonPatch instance for our datatypes. Creating+one by hand is tedious, so aeson-diff-generic gives two alternative+ways to create one: using the `FieldLens` class, or using template+haskell with the "Data.Aeson.Diff.Generic.TH" module.++### Creating instances with fieldlens.++A fieldlens maps a `Key` onto a getter and setter into the given data.++For our Pet datatype we don't have any fields to index into, so it just returns an error:++```haskell+instance FieldLens Pet where+ fieldLens _ _ = Error "Invalid Path"+```++Since this is the default implementation, so we can do simply:++```haskell+instance FieldLens Pet+```++For the Person datatype we map each field to the `GetSet` type:++```haskell+instance FieldLens Person where+ fieldLens (OKey field) (Person name_ age_ pet_) =+ case field of+ "name" -> pure $ GetSet name_ (\v -> pure $ Person v age_ pet_)+ "age" -> pure $ GetSet age_ (\v -> pure $ Person name_ v pet_)+ "pet" -> pure $ GetSet pet_ (\v -> pure $ Person name_ age_ v)+ _ -> Error "Invalid Path"+ fieldLens _ _ = Error "Invalid Path"+```++Now our `JsonPatch` instance will automatically use the `FieldLens` instance.++```haskell+instance JsonPatch Person+instance JsonPatch Pet+```++### Creating instances with template haskell++FieldLens still involves some boilerplate. We can avoid that by using+the template haskell functions from "Data.Aeson.Diff.Generic.TH":++```haskell+instance JsonPatch Pet+instance FieldLens Pet++deriveJsonPatch defaultOptions ''Person+```++### applying patches++Now we can apply patches to our data:++```haskell+> joe = Person "Joe" 32 Fish+> john = Person "John" 32 Bird+> patch = Data.Aeson.Diff.diff (toJSON joe) (toJSON john)++> import qualified Data.ByteString.Lazy.Char8 as ByteString+> ByteString.putStrLn $ encode patch+[{"op":"replace","path":"/name","value":"John"},{"op":"replace","path":"/pet","value":"Bird"}]++> Success json = Data.Aeson.Diff.patch patch (toJSON joe)+> ByteString.putStrLn $ encode json+{"age":32,"name":"John","pet":"Bird"}++> Data.Aeson.Diff.Generic.patch patch joe+Success (Person {name = "John", age = 32, pet = Bird})+```++# Join in!++I am happy to receive bug reports, fixes, documentation enhancements,+and other improvements.++Please report bugs via the+[github issue tracker](http://github.com/kuribas/aeson-diff-generic/issues).++Master [git repository](http://github.com/kuribas/aeson-diff-generic):++* `git clone git://github.com/kuribas/aeson-dif-generic.git`++See what's changed in recent (and upcoming) releases:++* https://github.com/kuribas/aeson-diff-generic/blob/master/changelog.md++(You can create and contribute changes using git)+++# Authors++This library is written by Kristof Bastiaensen.
Setup.hs view
aeson-diff-generic.cabal view
@@ -1,28 +1,66 @@ Name: aeson-diff-generic-Version: 0.0.1+Version: 0.0.2 Synopsis: Apply a json-patch to any haskell datatype.-Category: Graphics, Geometry, Typography+Category: JSON, Web Copyright: Kristof Bastiaensen (2018) Stability: Unstable License: BSD3 License-file: LICENSE Author: Kristof Bastiaensen Maintainer: Kristof Bastiaensen-Bug-Reports: https://github.com/kuribas/cubicbezier/issues+Bug-Reports: https://github.com/kuribas/aeson-diff-generic/issues Build-type: Simple Cabal-version: >=1.8-Description: Apply a json-patch to any haskell datatype. It extends the capabilities of the aeson-diff packages, and includes template haskell functions for automatically deriving the right instances.- +Tested-With: GHC == 7.10.2+ GHC == 8.0.2+ GHC == 8.2.2+Description: Apply a json-patch directly to a haskell datatype. It extends the capabilities of the aeson-diff packages, and includes template haskell functions for automatically deriving the right instances.++extra-source-files: .travis.yml+ README.md+ changelog.md+ Source-repository head type: git location: https://github.com/kuribas/aeson-diff-generic Library Ghc-options: -Wall- Build-depends: base >= 4.8 && <= 5, aeson, aeson-diff, bytestring (>=0.10),- hashable, mtl, scientific, text, unordered-containers, containers,- vector, bytestring, time, uuid-types, dlist, tagged, template-haskell,- th-abstraction+ build-depends: base >= 4.5 && <= 5,+ base-compat >= 0.9.1 && < 0.10, + aeson >= 1.2.4.0 && < 1.3,+ aeson-diff >= 1.1.0.0 && < 1.2,+ bytestring >=0.10 && < 0.11,+ hashable >= 1.1.2.0,+ bytestring >= 0.10,+ uuid-types >= 1.0.3 && <1.1,+ dlist >= 0.6,+ base >= 4.5 && < 5,+ base-compat >= 0.9.1 && < 0.10,+ containers >= 0.5.8,+ dlist >= 0.6,+ scientific >= 0.3.4.7 && < 0.4,+ tagged >=0.8.3 && <0.9,+ template-haskell >= 2.7,+ text >= 1.1.1.0,+ th-abstraction >= 0.2.2 && < 0.3,+ time >= 1.1.1.4,+ unordered-containers >= 0.2.5.0 && < 0.3,+ uuid-types >= 1.0.3 && <1.1,+ vector >= 0.8++ if !impl(ghc >= 8.0)+ -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9+ build-depends:+ semigroups >= 0.18.2 && < 0.19,+ transformers >= 0.2.2.0,+ transformers-compat >= 0.3,+ fail == 4.9.*++ if !impl(ghc >= 7.10)+ -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8+ build-depends: nats >= 1 && < 1.2+ Exposed-Modules: Data.Aeson.Diff.Generic Data.Aeson.Diff.Generic.TH Data.Aeson.Diff.Generic.Instances
+ changelog.md view
@@ -0,0 +1,15 @@+# aeson-diff-generic 0.0.2++*April 2, 2018*++ - Better documentation.+ - Tested builds++# aeson-diff-generic 0.0.1++*April 1, 2018*++Initial release of aeson-diff-generic. It has the implementations for+the `JsonPatch` and `fieldLens` classes, and `JsonPatch` instances for+all datatype supported by the aeson package. It also includes the+`deriveJsonPatch` template haskell function for generating `JsonPatch` instances.