cached-json-file (empty) → 0.1.0
raw patch · 5 files changed
+185/−0 lines, 5 filesdep +aesondep +basedep +bytestring
Dependencies added: aeson, base, bytestring, directory, filepath, http-query, time, xdg-basedir
Files
- ChangeLog.md +5/−0
- LICENSE +30/−0
- README.md +35/−0
- cached-json-file.cabal +54/−0
- src/System/Cached/JSON.hs +61/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Version history for cached-json-file++## 0.1.0 (2021-07-29)+- initial release with getCachedJSON and getCachedJSONQuery+- exports lookupKey from http-query
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2021, Jens Petersen++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 Jens Petersen nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,35 @@+# cached-json-file++A Haskell library providing a cached json file.++Useful for frequently used programs that use some remote json data+which changes rather slowly (like in hours, days, weeks or months),+where it is not critical to have always the latest data immediately.++## Usage++`getCachedJSON dir file url minutes` caches the json obtained from `url` in+`~/.cache/dir/file` for `minutes`.++eg:++```haskell+import System.Cached.JSON++getSnapshots :: IO Object+getSnapshots =+ getCachedJSON "stackage-snapshots" "snapshots.json" "http://haddock.stackage.org/snapshots.json" 180++main = getSnapshots >>= print+```++Each time you run this program within 3 hours the data will be read+from the local cache file `~/.cache/stackage-snapshots/snapshots.json`+rather than re-downloading it each time,+which helps to speed up the program and avoid unnecessary web queries.++There is also `getCachedJSONQuery prog jsonfile webquery minutes`+which uses `webquery :: (FromJSON a, ToJSON a) => IO a` to download+the json data.++Currently the smallest possible cache time is 1 minute.
+ cached-json-file.cabal view
@@ -0,0 +1,54 @@+name: cached-json-file+version: 0.1.0+synopsis: Locally cache a json file obtained by http+description:+ A small library for caching a slow-changing remote json file in+ a specified directory (under "~\/.cache\/"). When the json is requested+ and the cache file is still fresh enough it is read directly from+ the local cache, otherwise the cached file is refreshed first.+license: BSD3+license-file: LICENSE+author: Jens Petersen <juhpetersen@gmail.com>+maintainer: Jens Petersen <juhpetersen@gmail.com>+copyright: 2021 Jens Petersen <juhpetersen@gmail.com>+category: Network+homepage: https://github.com/juhp/cached-json-file+bug-reports: https://github.com/juhp/cached-json-file/issues+build-type: Simple+extra-doc-files: README.md+ ChangeLog.md+cabal-version: 2.0+tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4,+ GHC == 8.10.4, GHC == 9.0.1++source-repository head+ type: git+ location: https://github.com/juhp/cached-json-file.git++library+ build-depends: base < 5,+ aeson,+ bytestring,+ directory,+ filepath,+ http-query,+ time,+ xdg-basedir+ default-language: Haskell2010+ exposed-modules: System.Cached.JSON+ hs-source-dirs: src++ ghc-options: -Wall+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.10)+ ghc-options: -Wunused-packages
+ src/System/Cached/JSON.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}++module System.Cached.JSON (+ getCachedJSON,+ getCachedJSONQuery,+ lookupKey+ )+where++import Control.Monad+import Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as B+import Data.Time.Clock (diffUTCTime, getCurrentTime, NominalDiffTime)+import Network.HTTP.Query+import System.Directory+import System.Environment.XDG.BaseDir+import System.FilePath++-- FIXME handle network failure++-- | If the local cached json file is new enough then use it,+-- otherwise refresh from the remote url.+getCachedJSON :: (FromJSON a, ToJSON a)+ => String -- ^ subdirectory/program name+ -> FilePath -- ^ filename+ -> String -- ^ json url+ -> NominalDiffTime -- ^ cache duration (minutes)+ -> IO a+getCachedJSON prog jsonfile url =+ getCachedJSONQuery prog jsonfile (webAPIQuery url [])++-- | Similar to getCachedJSON but takes an IO procedure that fetches+-- the remote json data.+getCachedJSONQuery :: (FromJSON a, ToJSON a)+ => String -- ^ program name+ -> FilePath -- ^ filename+ -> IO a -- ^ http query+ -> NominalDiffTime -- ^ cache duration (minutes)+ -> IO a+getCachedJSONQuery prog jsonfile webquery minutes = do+ file <- getUserCacheFile prog jsonfile+ exists <- doesFileExist file+ unless exists $ do+ putStrLn $ "Creating " ++ file ++ " ..."+ createDirectoryIfMissing True (takeDirectory file)+ recent <- do+ if exists then do+ ts <- getModificationTime file+ t <- getCurrentTime+ return $ diffUTCTime t ts < (minutes * 60)+ else return False+ if recent+ then do+ eObj <- eitherDecode <$> B.readFile file+ case eObj of+ Left err -> error err+ Right obj -> return obj+ else do+ obj <- webquery+ B.writeFile file $ encode obj+ return obj