latest-npm-version 0.1.0 → 0.2.1
raw patch · 4 files changed
+79/−35 lines, 4 filesdep +cmdargsdep +hspecdep ~base
Dependencies added: cmdargs, hspec
Dependency ranges changed: base
Files
- Main.hs +21/−7
- Npm/Latest.hs +16/−26
- Tests/Main.hs +22/−0
- latest-npm-version.cabal +20/−2
Main.hs view
@@ -1,16 +1,30 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} +import Data.Data (Data)+import Data.Typeable (Typeable) import Npm.Latest (fetchLatestVersion)-import System.Environment (getArgs)+import System.Console.CmdArgs.Implicit (cmdArgsRun, (&=)) import qualified Data.Text as T import qualified Data.Text.IO as TIO+import qualified System.Console.CmdArgs.Implicit as CA +data LatestNpmVersion = LatestNpmVersion {name :: String}+ deriving (Show, Data, Typeable)++args :: CA.Mode (CA.CmdArgs LatestNpmVersion)+args = CA.cmdArgsMode $ LatestNpmVersion{name = CA.def &= CA.args}+ &= CA.summary "latest-npm-version v0.1.0"+ main :: IO () main = do- [name] <- getArgs- version <- fetchLatestVersion name- case version of- Nothing -> TIO.putStrLn "Error: fetching/parsing JSON failed"- Just v -> TIO.putStrLn $ T.unwords [T.pack name, v]+ mainArgs <- cmdArgsRun args+ let moduleName = (name mainArgs)+ if length moduleName == 0+ then TIO.putStrLn "Missing module name. Consult --help."+ else do+ version <- fetchLatestVersion moduleName+ case version of+ Nothing -> TIO.putStrLn "Error: fetching/parsing JSON failed"+ Just v -> TIO.putStrLn $ T.unwords [T.pack moduleName, v]
Npm/Latest.hs view
@@ -1,39 +1,29 @@ {-# LANGUAGE OverloadedStrings #-}+{-|+Module : Npm.Latestj+Description : Fetch the latest version of an npm module.+Copyright : (c) Pascal Hartig, 2014+License : MIT+Maintainer : phartig@rdrei.net+Stability : experimental+Portability : POSIX +This is just an experiment of me porting a node module to Haskell and learning+about Lenses and Pipes along the way. Use on your own risk.+-} module Npm.Latest (- fetchLatestVersion+ fetchLatestVersion,+ extractVersion ) where -import Control.Lens ((^?), _Right)-import Control.Monad.Trans.State.Strict (evalStateT)-import Data.Aeson (json', Value)-import Data.Aeson.Lens (key, _String, AsValue)-import Data.Text.Format (Format, format)-import Network.URI (escapeURIString, isUnreserved)-import Pipes.Attoparsec (parse, ParsingError)-import Pipes.HTTP (parseUrl, withManager, tlsManagerSettings, withHTTP, responseBody, Request)-+import Npm.Latest.Internal (buildRequest, makeVersionRequest, extractVersion)+import Data.Text.Format (Format) import qualified Data.Text as T-import qualified Data.Text.Lazy as TL - latestUrl :: Format latestUrl = "https://registry.npmjs.org/{}/latest" -extractVersion :: AsValue s => Maybe (Either t s) -> Maybe T.Text-extractVersion json =- json >>= (^? _Right . key "version" . _String)--buildRequest :: String -> Format -> IO Request-buildRequest name urlFormat =- parseUrl $ TL.unpack $ format urlFormat [escapeURIString isUnreserved name]--makeVersionRequest :: Request -> IO (Maybe (Either ParsingError Value))-makeVersionRequest req =- withManager tlsManagerSettings $ \mngr ->- withHTTP req mngr $ \resp ->- evalStateT (parse json') (responseBody resp)-+-- |Fetch the latest version for the given module name. fetchLatestVersion :: String -> IO (Maybe T.Text) fetchLatestVersion name = do req <- buildRequest name latestUrl
+ Tests/Main.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Test.Hspec+import Npm.Latest (extractVersion)++import qualified Data.Text as T++main :: IO ()+main = hspec $ do+ describe "extractVersion" $ do+ it "returns Just for a valid JSON object" $ do+ let json = T.unpack "{\"version\": \"1.0\"}"+ extractVersion (Just $ Right $ json) `shouldBe` Just "1.0"++ it "returns Nothing for a malformed JSON object" $ do+ let json = T.unpack "{\"description\": \"not what we expect\"}"+ extractVersion (Just $ Right $ json) `shouldBe` Nothing++ it "returns Nothing for a malformed JSON object" $ do+ let json = T.unpack "not even $ JSON"+ extractVersion (Just $ Right $ json) `shouldBe` Nothing
latest-npm-version.cabal view
@@ -1,5 +1,5 @@ name: latest-npm-version-version: 0.1.0+version: 0.2.1 synopsis: Find the latest version of a package on npm homepage: https://github.com/passy/latest-npm-version license: MIT@@ -46,5 +46,23 @@ aeson >= 0.7 && < 0.8, pipes-attoparsec >= 0.5 && < 0.6, transformers >= 0.3 && < 0.4,- lens >= 4.1 && < 4.2+ lens >= 4.1 && < 4.2,+ cmdargs >= 0.10 && < 0.11 default-language: Haskell2010+++Test-Suite hspec-tests+ main-is: Tests/Main.hs+ type: exitcode-stdio-1.0+ build-depends: base,+ hspec,+ pipes-http >= 1.0 && < 1.1,+ pipes >= 4.1 && < 4.2,+ text >= 1.1 && < 1.2,+ text-format >= 0.3 && < 0.4,+ network >= 2.5 && < 2.6,+ pipes-bytestring >= 2.0 && < 2.1,+ aeson >= 0.7 && < 0.8,+ pipes-attoparsec >= 0.5 && < 0.6,+ transformers >= 0.3 && < 0.4,+ lens >= 4.1 && < 4.2