packages feed

hackernews 0.1.0.1 → 0.2.0.1

raw patch · 6 files changed

+101/−73 lines, 6 filesdep +transformersdep ~aesondep ~http-streamsPVP ok

version bump matches the API change (PVP)

Dependencies added: transformers

Dependency ranges changed: aeson, http-streams

API changes (from Hackage documentation)

+ Web.HackerNews: hackerNews :: (Show a, FromJSON a) => HackerNews a -> IO a
+ Web.HackerNews: type HackerNews a = ReaderT Connection IO a
- Web.HackerNews: getComment :: CommentId -> IO (Maybe Comment)
+ Web.HackerNews: getComment :: CommentId -> HackerNews (Maybe Comment)
- Web.HackerNews: getMaxItem :: IO (Maybe MaxItem)
+ Web.HackerNews: getMaxItem :: HackerNews (Maybe MaxItem)
- Web.HackerNews: getPoll :: PollId -> IO (Maybe Poll)
+ Web.HackerNews: getPoll :: PollId -> HackerNews (Maybe Poll)
- Web.HackerNews: getPollOpt :: PollOptId -> IO (Maybe PollOpt)
+ Web.HackerNews: getPollOpt :: PollOptId -> HackerNews (Maybe PollOpt)
- Web.HackerNews: getStory :: StoryId -> IO (Maybe Story)
+ Web.HackerNews: getStory :: StoryId -> HackerNews (Maybe Story)
- Web.HackerNews: getTopStories :: IO (Maybe TopStories)
+ Web.HackerNews: getTopStories :: HackerNews (Maybe TopStories)
- Web.HackerNews: getUpdates :: IO (Maybe Update)
+ Web.HackerNews: getUpdates :: HackerNews (Maybe Update)
- Web.HackerNews: getUser :: UserId -> IO (Maybe User)
+ Web.HackerNews: getUser :: UserId -> HackerNews (Maybe User)

Files

hackernews.cabal view
@@ -1,5 +1,5 @@ name:                hackernews-version:             0.1.0.1+version:             0.2.0.1 description:         API for news.ycombinator.com license:             MIT synopsis:            API for Hacker News@@ -23,16 +23,17 @@                      , Web.HackerNews.Story                      , Web.HackerNews.Util   hs-source-dirs:      src-  build-depends:       HsOpenSSL >= 0.10.5-                     , aeson-                     , attoparsec >= 0.12.1.2-                     , base >=4.7 && <4.8-                     , bytestring >= 0.10.4.0+  build-depends:       HsOpenSSL    >= 0.10.5+                     , aeson        >= 0.8.0.1+                     , attoparsec   >= 0.12.1.2+                     , base         >=4.7 && <4.8+                     , bytestring   >= 0.10.4.0                      , hspec >= 1.11.4-                     , http-streams-                     , io-streams >= 1.1.4.6-                     , text >= 1.2.0.0-                     , time >= 1.4.2+                     , http-streams >= 0.7.2.2+                     , io-streams   >= 1.1.4.6+                     , text         >= 1.2.0.0+                     , time         >= 1.4.2+                     , transformers >= 0.3.0.0   default-language:    Haskell2010  Test-Suite tests@@ -43,6 +44,7 @@                        , hackernews                        , hspec                        , hspec >= 1.11.4+                       , transformers     default-language:    Haskell2010     ghc-options:         -Wall 
src/Web/HackerNews.hs view
@@ -1,7 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Web.HackerNews-       ( -- * API Calls-         getStory+       ( -- * Hacker News Monad+         hackerNews+         -- * API Calls+       , getStory        , getComment        , getPoll        , getPollOpt@@ -9,7 +11,8 @@        , getTopStories        , getMaxItem        , getUpdates-       -- * Types+         -- * Types+       , HackerNews        , Comment   (..)        , CommentId (..)        , Poll      (..)@@ -29,45 +32,45 @@  import           Web.HackerNews.Types import           Web.HackerNews.Util        (toText)-import           Web.HackerNews.Client      (getItem)+import           Web.HackerNews.Client  ------------------------------------------------------------------------------ -- | Retrieve a `Story` by `StoryId`-getStory :: StoryId -> IO (Maybe Story)-getStory (StoryId storyid) = getItem $ "item/" <> toText storyid+getStory :: StoryId -> HackerNews (Maybe Story)+getStory (StoryId storyid) = buildHNRequest $ "item/" <> toText storyid  ------------------------------------------------------------------------------ -- | Retrieve a `Comment` by `CommentId`-getComment :: CommentId -> IO (Maybe Comment)-getComment (CommentId commentid) = getItem $ "item/" <> toText commentid+getComment :: CommentId -> HackerNews (Maybe Comment)+getComment (CommentId commentid) = buildHNRequest $ "item/" <> toText commentid  ------------------------------------------------------------------------------ -- | Retrieve a `Poll` by `PollId`-getPoll :: PollId -> IO (Maybe Poll)-getPoll (PollId pollid) = getItem $ "item/" <> toText pollid+getPoll :: PollId -> HackerNews (Maybe Poll)+getPoll (PollId pollid) = buildHNRequest $ "item/" <> toText pollid  ------------------------------------------------------------------------------ -- | Retrieve a `PollOpt` by `PollOptId`-getPollOpt :: PollOptId -> IO (Maybe PollOpt)-getPollOpt (PollOptId polloptid) = getItem $ "item/" <> toText polloptid+getPollOpt :: PollOptId -> HackerNews (Maybe PollOpt)+getPollOpt (PollOptId polloptid) = buildHNRequest $ "item/" <> toText polloptid  ------------------------------------------------------------------------------ -- | Retrieve a `User` by `UserId`-getUser :: UserId -> IO (Maybe User)-getUser (UserId userid) = getItem $ "user/" <> userid+getUser :: UserId -> HackerNews (Maybe User)+getUser (UserId userid) = buildHNRequest $ "user/" <> userid  ------------------------------------------------------------------------------ -- | Retrieve the Top Stories on Hacker News-getTopStories :: IO (Maybe TopStories)-getTopStories = getItem "topstories"+getTopStories :: HackerNews (Maybe TopStories)+getTopStories = buildHNRequest "topstories"  ------------------------------------------------------------------------------ -- | Retrieve the largest ItemId-getMaxItem :: IO (Maybe MaxItem)-getMaxItem = getItem "maxitem"+getMaxItem :: HackerNews (Maybe MaxItem)+getMaxItem = buildHNRequest "maxitem"  ------------------------------------------------------------------------------ -- | Retrieve the latest updates-getUpdates :: IO (Maybe Update)-getUpdates = getItem "updates"+getUpdates :: HackerNews (Maybe Update)+getUpdates = buildHNRequest "updates" 
src/Web/HackerNews/Client.hs view
@@ -1,11 +1,18 @@+{-# LANGUAGE BangPatterns      #-} {-# LANGUAGE OverloadedStrings #-}-module Web.HackerNews.Client ( getItem ) where+module Web.HackerNews.Client+       ( hackerNews+       , buildHNRequest+       , HackerNews+       ) where  import           Data.Aeson                 hiding (Result) import           Data.Aeson.Parser          (value) import           Data.Attoparsec.ByteString (parseOnly) import           Data.Either                (rights) +import           Control.Monad.IO.Class     (liftIO)+import           Control.Monad.Trans.Reader (ReaderT, ask, runReaderT) import           Data.Monoid                ((<>)) import           Data.Text                  (Text) import qualified Data.Text.Encoding         as T@@ -14,28 +21,40 @@ import qualified System.IO.Streams          as Streams  ------------------------------------------------------------------------------+-- | Core Type+type HackerNews a = ReaderT Connection IO a++------------------------------------------------------------------------------ -- | HackerNews API request method-getItem-  :: FromJSON a-  => Text      -  -> IO (Maybe a)-getItem url = withOpenSSL $ do-  ctx <- baselineContextSSL-  con <- openConnectionSSL ctx "hacker-news.firebaseio.com" 443-  req <- buildRequest $ do-    http GET $ "/v0/" <> T.encodeUtf8 url <> ".json"-    setAccept "application/json"-  sendRequest con req emptyBody-  bytes <- receiveResponse con $ const $ Streams.read-  closeConnection con-  return $ case bytes of-   Nothing -> Nothing-   Just bs -> do-     let xs = rights [parseOnly value bs, parseOnly json bs]-     case xs of-       []    -> Nothing-       x : _ ->-         case fromJSON x of-          Success a -> Just a-          _         -> Nothing+hackerNews :: (Show a, FromJSON a) => HackerNews a -> IO a+hackerNews requests =+  withOpenSSL $ do+    ctx <- baselineContextSSL+    con <- openConnectionSSL ctx "hacker-news.firebaseio.com" 443+    result <- flip runReaderT con requests+    closeConnection con+    return result++------------------------------------------------------------------------------+-- | Request Builder for API+buildHNRequest :: FromJSON a => Text -> HackerNews (Maybe a)+buildHNRequest url = do+    con <- ask+    liftIO $ do+      req <- buildRequest $ do+        http GET $ "/v0/" <> T.encodeUtf8 url <> ".json"+        setHeader "Connection" "Keep-Alive"+        setAccept "application/json"+      sendRequest con req emptyBody+      !bytes <- receiveResponse con $ const $ Streams.read+      return $ case bytes of+        Nothing -> Nothing+        Just bs -> do+          let xs = rights [parseOnly value bs, parseOnly json bs]+          case xs of+            []    -> Nothing+            x : _ ->+              case fromJSON x of+                Success a -> Just a+                _         -> Nothing 
src/Web/HackerNews/Story.hs view
@@ -27,6 +27,9 @@   = StoryId Int   deriving (Show, Eq) +type TopStories = [Int]+type MaxItem    = Int+ ------------------------------------------------------------------------------ -- | JSON Instances instance FromJSON Story where
src/Web/HackerNews/Types.hs view
@@ -6,8 +6,6 @@        , PollId    (..)        , PollOptId (..)        , UserId    (..)-       , MaxItem-       , TopStories        ) where  import           Web.HackerNews.Comment as H@@ -16,8 +14,7 @@ import           Web.HackerNews.User    as H import           Web.HackerNews.Update  as H -type MaxItem    = Int-type TopStories = [Int]+   
tests/Test.hs view
@@ -1,28 +1,32 @@+{-# LANGUAGE OverloadedStrings #-} module Main where  import           Data.Maybe     (isJust) import           Test.Hspec     (it, runIO, hspec, describe) import           Web.HackerNews+import           Control.Applicative  main :: IO () main = hspec $ do   describe "Hacker News API Tests" $ do-    story <- runIO $ getStory (StoryId 8863)-    it "Retrieves a Story" $ isJust story-    comment <- runIO $ getComment (CommentId 2921983)-    it "Retrieves a Comment" $ isJust comment-    user <- runIO $ getUser (UserId "dmjio")-    it "Retrieves a User" $ isJust user-    poll <- runIO $ getPoll (PollId 126809)-    it "Retrieves a Poll" $ isJust poll-    pollOpt <- runIO $ getPollOpt (PollOptId 160705)-    it "Retrieves a Pollopt" $ isJust pollOpt-    topStories <- runIO getTopStories+    (story, comment, user, poll, pollOpt, topStories, maxItem, updates) <- +      runIO $ hackerNews $ (,,,,,,,)   <$> +        getStory   (StoryId 8863)      <*>+        getComment (CommentId 2921983) <*>+        getUser    (UserId "dmjio")    <*>+        getPoll    (PollId 126809)     <*>+        getPollOpt (PollOptId 160705)  <*>+        getTopStories                  <*>+        getMaxItem                     <*>+        getUpdates+    it "Retrieves a Story"     $ isJust story+    it "Retrieves a Comment"   $ isJust comment+    it "Retrieves a User"      $ isJust user+    it "Retrieves a Poll"      $ isJust poll+    it "Retrieves a Pollopt"   $ isJust pollOpt     it "Retrieves Top Stories" $ isJust topStories-    maxItem <- runIO getMaxItem-    it "Retrieves Max Item" $ isJust maxItem-    updates <- runIO getUpdates-    it "Retrieves Updates" $ isJust updates+    it "Retrieves Max Item"    $ isJust maxItem+    it "Retrieves Updates"     $ isJust updates