packages feed

packdeps 0.0.3 → 0.1.0.1

raw patch · 7 files changed

+460/−393 lines, 7 filesdep +packdepsdep ~Cabaldep ~bytestringdep ~containers

Dependencies added: packdeps

Dependency ranges changed: Cabal, bytestring, containers, filepath, hamlet, text, time, yesod, yesod-newsfeed

Files

Distribution/PackDeps.hs view
@@ -15,9 +15,12 @@     , loadPackage       -- * Get multiple packages     , filterPackages+    , deepDeps       -- * Reverse dependencies     , Reverses     , getReverses+      -- * Helpers+    , diName     ) where  import System.Directory (getAppUserDataDirectory)@@ -47,6 +50,7 @@ import Control.Arrow ((&&&)) import Data.List (groupBy, sortBy) import Data.Ord (comparing)+import qualified Data.Set as Set  loadNewest :: IO Newest loadNewest = do@@ -144,6 +148,7 @@     { diHaystack :: String     , diDeps :: [Dependency]     , diPackage :: PackageIdentifier+    , diSynopsis :: String     }     deriving (Show, Read) @@ -152,6 +157,7 @@     { diHaystack = map toLower $ author p ++ maintainer p ++ name     , diDeps = getDeps gpd     , diPackage = pi'+    , diSynopsis = synopsis p     }   where     p = packageDescription gpd@@ -186,7 +192,8 @@ notNewest :: Newest -> Dependency -> Maybe ((String, String), Tar.EpochTime) notNewest newest (Dependency (PackageName s) range) =     case Map.lookup s newest of-        Nothing -> Just ((s, " no version found"), 0)+        --Nothing -> Just ((s, " no version found"), 0)+        Nothing -> Nothing         Just PackInfo { piVersion = version, piEpoch = e } ->             if withinRange version range                 then Nothing@@ -216,7 +223,32 @@   where     needle' = map toLower needle     go PackInfo { piDesc = Just desc } =-        if needle' `isInfixOf` diHaystack desc+        if needle' `isInfixOf` diHaystack desc &&+           not ("(deprecated)" `isInfixOf` diSynopsis desc)             then Just desc             else Nothing     go _ = Nothing++-- | Find all packages depended upon by the given list of packages.+deepDeps :: Newest -> [DescInfo] -> [DescInfo]+deepDeps newest dis =+    go Set.empty dis+  where+    go _ [] = []+    go viewed (di:dis)+        | name `Set.member` viewed = go viewed dis+        | otherwise = di : go viewed' (newDis ++ dis)+      where+        PackageIdentifier (PackageName name) _ = diPackage di+        viewed' = Set.insert name viewed+        newDis = mapMaybe getDI $ diDeps di+        getDI :: Dependency -> Maybe DescInfo+        getDI (Dependency (PackageName name) _) = do+            pi <- Map.lookup name newest+            piDesc pi++diName :: DescInfo -> String+diName =+    unPN . pkgName . diPackage+  where+    unPN (PackageName pn) = pn
− packdeps-cli.hs
@@ -1,42 +0,0 @@-import Distribution.PackDeps-import System.Environment (getArgs)-import System.Exit (exitFailure, exitSuccess)-import Distribution.Text (display)-import Distribution.Package (PackageName (PackageName))--main :: IO ()-main = do-    newest <- loadNewest-    getArgs >>= mapM_ (go newest)-  where-    go newest fp = do-        mdi <- loadPackage fp-        di <--            case mdi of-                Just di -> return di-                Nothing -> error $ "Could not parse cabal file: " ++ fp-        allGood <- case checkDeps newest di of-            (pn, v, AllNewest) -> do-                putStrLn $ concat-                    [ unPackageName pn-                    , "-"-                    , display v-                    , ": Can use newest versions of all dependencies"-                    ]-                return True-            (pn, v, WontAccept p _) -> do-                putStrLn $ concat-                    [ unPackageName pn-                    , "-"-                    , display v-                    , ": Cannot accept the following packages"-                    ]-                flip mapM_ p $ \(x, y) -> putStrLn $ x ++ " " ++ y-                return False-        putStrLn ""-        if allGood-            then exitSuccess-            else exitFailure--unPackageName :: PackageName -> String-unPackageName (PackageName n) = n
− packdeps-yesod.hs
@@ -1,314 +0,0 @@-{-# LANGUAGE QuasiQuotes, TypeFamilies, OverloadedStrings, MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}-import Yesod-import Yesod.AtomFeed-import Yesod.Feed-import Distribution.PackDeps-import Data.Maybe-import Data.List (sortBy)-import Data.Ord (comparing)-import Data.Time-import Distribution.Package-import Distribution.Text hiding (Text)-import Control.Arrow-import Distribution.Version (withinRange)-import qualified Data.Map as Map-import qualified Data.ByteString.Char8 as S8-import Data.Text (Text, pack, unpack)-import Text.Hamlet (shamlet)--data PD = PD Newest Reverses-mkYesod "PD" [$parseRoutes|-/favicon.ico FaviconR GET-/ RootR GET-/feed FeedR GET-/feed/#Text Feed2R GET-/feed/#Text/#Text/#Text/#Text Feed3R GET-/specific SpecificR GET-/feed/specific/#Text SpecificFeedR GET--/reverse ReverseListR GET-/reverse/#Text ReverseR GET-|]-instance Yesod PD where approot _ = ""--getFaviconR :: Handler ()-getFaviconR = sendFile "image/x-icon" "favicon.ico"--mainCassius = [$cassius|-body-    font-family: Arial,Helvetica,sans-serif-    width: 600px-    margin: 2em auto-    text-align: center-p-    text-align: justify-h2-    border-bottom: 2px solid #999-input[type=text]-    width: 400px-#footer-    margin-top: 15px-    border-top: 1px dashed #999-    padding-top: 10px-table-    border-collapse: collapse-    margin: 0 auto-th, td-    border: 1px solid #333-form p-    margin-top: 1em-    text-align: center-|]--getRootR = defaultLayout $ do-    setTitle "Hackage dependency monitor"-    addCassius mainCassius-    [whamlet|-<h1>Hackage Dependency Monitor-<form action="@{FeedR}">-    <input type="text" name="needle" required="" placeholder="Search string">-    <input type="submit" value="Check">-    <p>-        <a href=@{ReverseListR}>Reverse Dependency List-<h2>What is this?-<p>It can often get tedious to keep your package dependencies up-to-date. This tool is meant to alleviate a lot of the burden. It will automatically determine when an upper bound on a package prevents the usage of a newer version. For example, if foo depends on bar &gt;= 0.1 &amp;&amp; &lt; 0.2, and bar 0.2 exists, this tool will flag it.-<p>Enter a search string in the box above. It will find all packages containing that string in the package name, maintainer or author fields, and create an Atom feed for restrictive bounds. Simply add that URL to a news reader, and you're good to go!-<p>-    \All of the code is -    <a href="http://github.com/snoyberg/packdeps">available on Github-    \. Additionally, there is a -    <a href="http://hackage.haskell.org/package/packdeps">package on Hackage-    \ with the code powering this site both as a library and as an executable, so you can test code which is not uploaded to the public Hackage server.-<div id="footer">-    <a href="http://docs.yesodweb.com/">Powered by Yesod-|]--getDeps needle = do-    PD newest _ <- getYesod-    let descs = filterPackages needle newest-        go (_, _, AllNewest) = Nothing-        go (PackageName x, v, WontAccept y z) = Just ((x, v), (y, z))-        deps = reverse $ sortBy (comparing $ snd . snd)-             $ mapMaybe (go . checkDeps newest) descs-    return deps--instance RenderMessage PD FormMessage where-    renderMessage _ _ = defaultFormMessage--getFeedR :: Handler RepHtml-getFeedR = do-    needle <- runInputGet $ ireq textField "needle"-    deps <- getDeps $ unpack needle-    let title = "Newer dependencies for " ++ unpack needle-    defaultLayout $ do-        setTitle $ toHtml title-        addCassius [$cassius|body-    font-family: Arial,Helvetica,sans-serif-    width: 600px-    margin: 2em auto-h1-    text-align: center-p-    text-align: justify-table-    border-collapse: collapse-th, td-    border: 1px solid #999-    padding: 5px-h3-    margin: 20px 0 5px 0-|]-        let feedR = Feed2R needle-        atomLink feedR title-        [whamlet|-<h1>#{title}-<p>-    \The following are the packages which have restrictive upper bounds. You can also #-    <a href="@{feedR}">view this information as a news feed-    \ so you can get automatic updates in your feed reader of choice.-$if null deps-    <p>-        <b>All upper bounds are non-restrictive.-$else-    $forall d <- deps-        <h3>#{fst (fst d)}-#{display (snd (fst d))}-        <table>-            $forall p <- fst (snd d)-                <tr>-                    <th>#{fst p}-                    <td>#{snd p}-|]--getFeed2R needle = do-    deps <- getDeps $ unpack needle-    now <- liftIO getCurrentTime-    newsFeed Feed-        { feedTitle = pack $ "Newer dependencies for " ++ unpack needle-        , feedLinkSelf = Feed2R needle-        , feedLinkHome = RootR-        , feedUpdated = now-        , feedEntries = map go' deps-        , feedLanguage = "en"-        , feedDescription = toHtml $ "Newer dependencies for " ++ unpack needle-        }-  where-    go' ((name, version), (deps, time)) = FeedEntry-        { feedEntryLink = Feed3R needle (pack name) (pack $ display version) (pack $ show time)-        , feedEntryUpdated = time-        , feedEntryTitle = pack $ "Outdated dependencies for " ++ name ++ " " ++ display version-        , feedEntryContent = [shamlet|-<table border="1">-    $forall d <- deps-        <tr>-            <th>#{fst d}-            <td>#{snd d}-|]-        }--getFeed3R :: Text -> Text -> Text -> Text -> Handler ()-getFeed3R _ package _ _ =-    redirectText RedirectPermanent-  $ pack-  $ "http://hackage.haskell.org/package/" ++ unpack package--main = do-    newest <- read `fmap` readFile "newest"-    warpDebug 5005 $ PD newest $ getReverses newest--getSpecificR :: Handler RepHtml-getSpecificR = do-    packages' <- lookupGetParams "package"-    PD newest _ <- getYesod-    let packages = map (id &&& flip getPackage newest) $ map unpack packages'-    let title = "Newer dependencies for your Hackage packages"-    let checkDeps' x =-            case checkDeps newest x of-                (_, _, AllNewest) -> Nothing-                (_, v, WontAccept cd _) -> Just (v, cd)-    defaultLayout $ do-        setTitle $ toHtml title-        addCassius [$cassius|body-    font-family: Arial,Helvetica,sans-serif-    width: 600px-    margin: 2em auto-h1-    text-align: center-p-    text-align: justify-table-    border-collapse: collapse-th, td-    border: 1px solid #999-    padding: 5px-h3-    margin: 20px 0 5px 0-|]-        let feedR = SpecificFeedR $ pack $ unwords $ map unpack packages'-        atomLink feedR title-        [whamlet|-<h1>#{title}-<p>-    The following are the packages which have restrictive upper bounds. You can also #-    <a href="@{feedR}">view this information as a news feed-    \ so you can get automatic updates in your feed reader of choice.-$forall p <- packages-    $maybe descinfo <- snd p-        $maybe x <- checkDeps' descinfo-            <h3>#{fst p}-#{display (fst x)}-            <table>-                $forall p <- snd x-                    <tr>-                        <th>#{fst p}-                        <td>#{snd p}-        $nothing-            <h3>#{fst p} up to date-    $nothing-        <p>Invalid package name: #{fst p}-|]--getSpecificFeedR packages' = do-    PD newest _ <- getYesod-    let descs = mapMaybe (flip getPackage newest) $ words $ unpack packages'-    let go (_, _, AllNewest) = Nothing-        go (PackageName x, v, WontAccept y z) = Just ((x, v), (y, z))-        deps = reverse $ sortBy (comparing $ snd . snd)-             $ mapMaybe (go . checkDeps newest) descs-    now <- liftIO getCurrentTime-    newsFeed Feed-        { feedTitle = "Newer dependencies for Hackage packages"-        , feedLinkSelf = SpecificFeedR packages'-        , feedLinkHome = RootR-        , feedUpdated = now-        , feedEntries = map go' deps-        , feedLanguage = "en"-        , feedDescription = "Newer dependencies for Hackage packages"-        }-  where-    go' ((name, version), (deps, time)) = FeedEntry-        { feedEntryLink = Feed3R packages' (pack name) (pack $ display version) (pack $ show time)-        , feedEntryUpdated = time-        , feedEntryTitle = pack $ "Outdated dependencies for " ++ name ++ " " ++ display version-        , feedEntryContent = [shamlet|-<table border="1">-    $forall d <- deps-        <tr>-            <th>#{fst d}-            <td>#{snd d}-|]-        }--getReverseListR :: Handler RepHtml-getReverseListR = do-    PD _ reverse <- getYesod-    defaultLayout $ do-        setTitle "Reverse Dependencies"-        addCassius mainCassius-        addHamlet [$hamlet|-<h1>Reverse Dependencies-<p>Please choose a package below to view its reverse dependencies: those packages that depend upon it. This listing will also tell you which packages are incompatible with the current version of the package.-<table-    <tr-        <th>Package-        <th>Total Dependencies-        <th>Total Outdated Dependencies-    $forall p <- Map.toList reverse-        <tr>-            <td-                <a href=@{ReverseR $ pack $ fst p}>#{fst p}-            <td>#{show $ length $ snd $ snd p}-            $maybe o <- getOutdated $ snd p-                <td style="color:#900">#{o}-            $nothing-                <td>0-|]-  where-    getOutdated (version, pairs) =-        case filter (not . withinRange version . snd) pairs of-            [] -> Nothing-            ps -> Just $ show $ length ps--getReverseR :: Text -> Handler RepHtml-getReverseR dep = do-    PD _ reverse <- getYesod-    (version, rels) <- maybe notFound return $ Map.lookup (unpack dep) reverse-    defaultLayout $ do-        setTitle [shamlet|Reverse dependencies for #{dep}|]-        addCassius mainCassius-        addHamlet [hamlet|-<h1>#{show $ length rels} reverse dependencies for #{dep}&nbsp;#{display version}-<table>-    <tr>-        <th>Package-        <th>Uses current version?-    $forall rel <- rels-        <tr>-            $if Map.member (fst rel) reverse-                <td><a href=@{ReverseR $ pack $ fst rel}>#{fst rel}-            $else-                <td>#{fst rel}-            $if withinRange version (snd rel)-                <td>#{display (snd rel)}-            $else-                <td style="background-color:#fbb">#{display (snd rel)}-|]
packdeps.cabal view
@@ -1,5 +1,5 @@ name:            packdeps-version:         0.0.3+version:         0.1.0.1 license:         BSD3 license-file:    LICENSE author:          Michael Snoyman <michael@snoyman.com>@@ -11,55 +11,57 @@     The command line tool has an incredibly simple interface: simply pass it a list of cabal files, and it will tell you what dependencies- if any- are restricted. category:        Distribution stability:       Stable-cabal-version:   >= 1.6+cabal-version:   >= 1.8 build-type:      Simple homepage:        http://packdeps.haskellers.com/  flag web   description: Build the web-based interface.-  manual: True+  default: False  library-    if flag(web)-        Buildable: False-    else-        build-depends:   base                      >= 4        && < 5-                       , tar                       >= 0.3.1    && < 0.4-                       , split                     >= 0.1.2.3  && < 0.2-                       , bytestring                >= 0.9      && < 0.10-                       , text                      >= 0.7      && < 0.12-                       , Cabal                     >= 1.8      && < 1.13-                       , time                      >= 1.1.4-                       , containers                >= 0.2      && < 0.5-                       , directory                 >= 1.0      && < 1.2-                       , filepath                  >= 1.1      && < 1.3-        exposed-modules: Distribution.PackDeps-        ghc-options:     -Wall+    build-depends:   base                      >= 4        && < 5+                   , tar                       >= 0.3.1    && < 0.4+                   , split                     >= 0.1.2.3  && < 0.2+                   , bytestring                >= 0.9      && < 0.10+                   , text                      >= 0.7      && < 0.12+                   , Cabal                     >= 1.8      && < 1.15+                   , time                      >= 1.1.4+                   , containers                >= 0.2      && < 0.5+                   , directory                 >= 1.0      && < 1.2+                   , filepath                  >= 1.1      && < 1.4+    exposed-modules: Distribution.PackDeps+    ghc-options:     -Wall  executable             packdeps     main-is:           packdeps-cli.hs+    hs-source-dirs:    tools      if flag(web)         Buildable: False     else         ghc-options:       -Wall +        build-depends:   base                      >= 4        && < 5+                       , Cabal+                       , packdeps+ executable             packdeps-yesod     main-is:           packdeps-yesod.hs--    if flag(web)-        ghc-options:       -Wall-    else-        Buildable: False--executable             save-newest-    main-is:           save-newest.hs+    hs-source-dirs:    tools      if flag(web)         ghc-options:       -Wall-        build-depends:     yesod-newsfeed         == 0.3.*-                         , yesod                  == 0.9.*-                         , hamlet                 == 0.10.*+        build-depends:   base                      >= 4        && < 5+                       , text+                       , bytestring+                       , containers+                       , Cabal+                       , time+                       , packdeps+                       , hamlet+                       , yesod-newsfeed+                       , yesod     else         Buildable: False 
− save-newest.hs
@@ -1,6 +0,0 @@-import Distribution.PackDeps--main :: IO ()-main = do-    newest <- loadNewest-    writeFile "newest" $ show newest
+ tools/packdeps-cli.hs view
@@ -0,0 +1,42 @@+import Distribution.PackDeps+import System.Environment (getArgs)+import System.Exit (exitFailure, exitSuccess)+import Distribution.Text (display)+import Distribution.Package (PackageName (PackageName))++main :: IO ()+main = do+    newest <- loadNewest+    getArgs >>= mapM_ (go newest)+  where+    go newest fp = do+        mdi <- loadPackage fp+        di <-+            case mdi of+                Just di -> return di+                Nothing -> error $ "Could not parse cabal file: " ++ fp+        allGood <- case checkDeps newest di of+            (pn, v, AllNewest) -> do+                putStrLn $ concat+                    [ unPackageName pn+                    , "-"+                    , display v+                    , ": Can use newest versions of all dependencies"+                    ]+                return True+            (pn, v, WontAccept p _) -> do+                putStrLn $ concat+                    [ unPackageName pn+                    , "-"+                    , display v+                    , ": Cannot accept the following packages"+                    ]+                flip mapM_ p $ \(x, y) -> putStrLn $ x ++ " " ++ y+                return False+        putStrLn ""+        if allGood+            then exitSuccess+            else exitFailure++unPackageName :: PackageName -> String+unPackageName (PackageName n) = n
+ tools/packdeps-yesod.hs view
@@ -0,0 +1,353 @@+{-# LANGUAGE QuasiQuotes, TypeFamilies, OverloadedStrings, MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+import Yesod+import Yesod.AtomFeed+import Yesod.Feed+import Distribution.PackDeps+import Data.Maybe+import Data.List (sortBy)+import Data.Ord (comparing)+import Data.Time+import Distribution.Package+import Distribution.Text hiding (Text)+import Control.Arrow+import Distribution.Version (withinRange)+import qualified Data.Map as Map+import qualified Data.ByteString.Char8 as S8+import Data.Text (Text, pack, unpack)+import Text.Hamlet (shamlet)+import System.Environment (getArgs)+import Data.List (sort)++data PD = PD Newest Reverses+mkYesod "PD" [$parseRoutes|+/favicon.ico FaviconR GET+/ RootR GET+/feed FeedR GET+/feed/#Text Feed2R GET+/feeddeep/#Text Feed2DeepR GET+/feed/#Text/#Text/#Text/#Text Feed3R GET+/specific SpecificR GET+/feed/specific/#Text SpecificFeedR GET++/reverse ReverseListR GET+/reverse/#Text ReverseR GET+|]+instance Yesod PD where approot _ = ""++getFaviconR :: Handler ()+getFaviconR = sendFile "image/x-icon" "favicon.ico"++mainCassius = [$cassius|+body+    font-family: Arial,Helvetica,sans-serif+    width: 600px+    margin: 2em auto+    text-align: center+p+    text-align: justify+h2+    border-bottom: 2px solid #999+input[type=text]+    width: 400px+#footer+    margin-top: 15px+    border-top: 1px dashed #999+    padding-top: 10px+table+    border-collapse: collapse+    margin: 0 auto+th, td+    border: 1px solid #333+form p+    margin-top: 1em+    text-align: center+|]++getRootR = defaultLayout $ do+    setTitle "Hackage dependency monitor"+    addCassius mainCassius+    [whamlet|+<h1>Hackage Dependency Monitor+<form action="@{FeedR}">+    <input type="text" name="needle" required="" placeholder="Search string">+    <input type="submit" value="Check">+    <p>+        <a href=@{ReverseListR}>Reverse Dependency List+<h2>What is this?+<p>It can often get tedious to keep your package dependencies up-to-date. This tool is meant to alleviate a lot of the burden. It will automatically determine when an upper bound on a package prevents the usage of a newer version. For example, if foo depends on bar &gt;= 0.1 &amp;&amp; &lt; 0.2, and bar 0.2 exists, this tool will flag it.+<p>Enter a search string in the box above. It will find all packages containing that string in the package name, maintainer or author fields, and create an Atom feed for restrictive bounds. Simply add that URL to a news reader, and you're good to go!+<p>+    \All of the code is +    <a href="http://github.com/snoyberg/packdeps">available on Github+    \. Additionally, there is a +    <a href="http://hackage.haskell.org/package/packdeps">package on Hackage+    \ with the code powering this site both as a library and as an executable, so you can test code which is not uploaded to the public Hackage server.+<div id="footer">+    <a href="http://docs.yesodweb.com/">Powered by Yesod+|]++isDeep = fmap (== Just "on") $ runInputGet $ iopt textField "deep"++getDeps deep needle = do+    PD newest _ <- getYesod+    let descs' = filterPackages needle newest+        descs = if deep then deepDeps newest descs' else descs'+        go (_, _, AllNewest) = Nothing+        go (PackageName x, v, WontAccept y z) = Just ((x, v), (y, z))+        deps = reverse $ sortBy (comparing $ snd . snd)+             $ mapMaybe (go . checkDeps newest) descs+    return (descs, deps)++instance RenderMessage PD FormMessage where+    renderMessage _ _ = defaultFormMessage++getFeedR :: Handler RepHtml+getFeedR = do+    needle <- runInputGet $ ireq textField "needle"+    deep <- isDeep+    (descs, deps) <- getDeps deep $ unpack needle+    let title = "Newer dependencies for " ++ unpack needle+    let deepR = (FeedR, [("needle", needle), ("deep", "on")])+    defaultLayout $ do+        setTitle $ toHtml title+        addCassius [$cassius|body+    font-family: Arial,Helvetica,sans-serif+    width: 600px+    margin: 2em auto+h1+    text-align: center+p+    text-align: justify+table+    border-collapse: collapse+th, td+    border: 1px solid #999+    padding: 5px+h3+    margin: 20px 0 5px 0+.packages+    -webkit-column-count: 2+    -moz-column-count: 2+    column-count: 2+.packages a, .packages a:visited+    text-decoration: none+    color: blue+.packages a:hover+    text-decoration: underline+|]+        let feedR = (if deep then Feed2DeepR else Feed2R) needle+        atomLink feedR title+        [whamlet|+<h1>#{title}+<p>+    \The following are the packages which have restrictive upper bounds. You can also #+    <a href="@{feedR}">view this information as a news feed+    \ so you can get automatic updates in your feed reader of choice.+$if null deps+    <p>+        <b>All upper bounds are non-restrictive.+$else+    $forall d <- deps+        <h3>#{fst (fst d)}-#{display (snd (fst d))}+        <table>+            $forall p <- fst (snd d)+                <tr>+                    <th>#{fst p}+                    <td>#{snd p}+$if not deep+    <p>+        <a href=@?{deepR}>View outdated dependency for all ancestor packages too.+<h3>Packages checked+<div .packages>+    <ol>+        $forall name <- sort $ map diName descs+            <li>+                <a href="http://hackage.haskell.org/package/#{name}">#{name}+|]++getFeed2R needle = do+    (_, deps) <- getDeps False $ unpack needle+    feed2Helper needle deps++getFeed2DeepR needle = do+    (_, deps) <- getDeps True $ unpack needle+    feed2Helper needle deps++feed2Helper needle deps = do+    now <- liftIO getCurrentTime+    newsFeed Feed+        { feedTitle = pack $ "Newer dependencies for " ++ unpack needle+        , feedLinkSelf = Feed2R needle+        , feedLinkHome = RootR+        , feedUpdated = now+        , feedEntries = map go' deps+        , feedLanguage = "en"+        , feedDescription = toHtml $ "Newer dependencies for " ++ unpack needle+        }+  where+    go' ((name, version), (deps, time)) = FeedEntry+        { feedEntryLink = Feed3R needle (pack name) (pack $ display version) (pack $ show time)+        , feedEntryUpdated = time+        , feedEntryTitle = pack $ "Outdated dependencies for " ++ name ++ " " ++ display version+        , feedEntryContent = [shamlet|+<table border="1">+    $forall d <- deps+        <tr>+            <th>#{fst d}+            <td>#{snd d}+|]+        }++getFeed3R :: Text -> Text -> Text -> Text -> Handler ()+getFeed3R _ package _ _ =+    redirectText RedirectPermanent+  $ pack+  $ "http://hackage.haskell.org/package/" ++ unpack package++main = do+    args <- getArgs+    if args == ["--save-newest"]+        then do+            newest <- loadNewest+            writeFile "newest" $ show newest+        else do+            newest <- read `fmap` readFile "newest"+            warpDebug 5005 $ PD newest $ getReverses newest++getSpecificR :: Handler RepHtml+getSpecificR = do+    packages' <- lookupGetParams "package"+    PD newest _ <- getYesod+    let packages = map (id &&& flip getPackage newest) $ map unpack packages'+    let title = "Newer dependencies for your Hackage packages"+    let checkDeps' x =+            case checkDeps newest x of+                (_, _, AllNewest) -> Nothing+                (_, v, WontAccept cd _) -> Just (v, cd)+    defaultLayout $ do+        setTitle $ toHtml title+        addCassius [$cassius|body+    font-family: Arial,Helvetica,sans-serif+    width: 600px+    margin: 2em auto+h1+    text-align: center+p+    text-align: justify+table+    border-collapse: collapse+th, td+    border: 1px solid #999+    padding: 5px+h3+    margin: 20px 0 5px 0+|]+        let feedR = SpecificFeedR $ pack $ unwords $ map unpack packages'+        atomLink feedR title+        [whamlet|+<h1>#{title}+<p>+    The following are the packages which have restrictive upper bounds. You can also #+    <a href="@{feedR}">view this information as a news feed+    \ so you can get automatic updates in your feed reader of choice.+$forall p <- packages+    $maybe descinfo <- snd p+        $maybe x <- checkDeps' descinfo+            <h3>#{fst p}-#{display (fst x)}+            <table>+                $forall p <- snd x+                    <tr>+                        <th>#{fst p}+                        <td>#{snd p}+        $nothing+            <h3>#{fst p} up to date+    $nothing+        <p>Invalid package name: #{fst p}+|]++getSpecificFeedR packages' = do+    PD newest _ <- getYesod+    let descs = mapMaybe (flip getPackage newest) $ words $ unpack packages'+    let go (_, _, AllNewest) = Nothing+        go (PackageName x, v, WontAccept y z) = Just ((x, v), (y, z))+        deps = reverse $ sortBy (comparing $ snd . snd)+             $ mapMaybe (go . checkDeps newest) descs+    now <- liftIO getCurrentTime+    newsFeed Feed+        { feedTitle = "Newer dependencies for Hackage packages"+        , feedLinkSelf = SpecificFeedR packages'+        , feedLinkHome = RootR+        , feedUpdated = now+        , feedEntries = map go' deps+        , feedLanguage = "en"+        , feedDescription = "Newer dependencies for Hackage packages"+        }+  where+    go' ((name, version), (deps, time)) = FeedEntry+        { feedEntryLink = Feed3R packages' (pack name) (pack $ display version) (pack $ show time)+        , feedEntryUpdated = time+        , feedEntryTitle = pack $ "Outdated dependencies for " ++ name ++ " " ++ display version+        , feedEntryContent = [shamlet|+<table border="1">+    $forall d <- deps+        <tr>+            <th>#{fst d}+            <td>#{snd d}+|]+        }++getReverseListR :: Handler RepHtml+getReverseListR = do+    PD _ reverse <- getYesod+    defaultLayout $ do+        setTitle "Reverse Dependencies"+        addCassius mainCassius+        addHamlet [$hamlet|+<h1>Reverse Dependencies+<p>Please choose a package below to view its reverse dependencies: those packages that depend upon it. This listing will also tell you which packages are incompatible with the current version of the package.+<table+    <tr+        <th>Package+        <th>Total Dependencies+        <th>Total Outdated Dependencies+    $forall p <- Map.toList reverse+        <tr>+            <td+                <a href=@{ReverseR $ pack $ fst p}>#{fst p}+            <td>#{show $ length $ snd $ snd p}+            $maybe o <- getOutdated $ snd p+                <td style="color:#900">#{o}+            $nothing+                <td>0+|]+  where+    getOutdated (version, pairs) =+        case filter (not . withinRange version . snd) pairs of+            [] -> Nothing+            ps -> Just $ show $ length ps++getReverseR :: Text -> Handler RepHtml+getReverseR dep = do+    PD _ reverse <- getYesod+    (version, rels) <- maybe notFound return $ Map.lookup (unpack dep) reverse+    defaultLayout $ do+        setTitle [shamlet|Reverse dependencies for #{dep}|]+        addCassius mainCassius+        addHamlet [hamlet|+<h1>#{show $ length rels} reverse dependencies for #{dep}&nbsp;#{display version}+<table>+    <tr>+        <th>Package+        <th>Uses current version?+    $forall rel <- rels+        <tr>+            $if Map.member (fst rel) reverse+                <td><a href=@{ReverseR $ pack $ fst rel}>#{fst rel}+            $else+                <td>#{fst rel}+            $if withinRange version (snd rel)+                <td>#{display (snd rel)}+            $else+                <td style="background-color:#fbb">#{display (snd rel)}+|]