herringbone 0.0.6 → 0.0.7
raw patch · 10 files changed
+359/−6 lines, 10 files
Files
- README.md +42/−0
- herringbone.cabal +9/−6
- test/AssertionsHelper.hs +63/−0
- test/FindAssetSpec.hs +57/−0
- test/LazinessHelper.hs +14/−0
- test/LocateAssetsSpec.hs +86/−0
- test/PreprocessStdIOSpec.hs +9/−0
- test/SpecHelper.hs +9/−0
- test/TestHerringbone.hs +55/−0
- test/WaiAdapterTest.hs +15/−0
+ README.md view
@@ -0,0 +1,42 @@+herringbone+===========++herringbone is a Haskell library for compiling and serving web assets. It+aims to make it dead simple to create a 'Network.Wai.Middleware' or+'Network.Wai.Application' which deals with all of your static assets, including+preprocessing for languages like Fay, CoffeeScript, Sass, and LESS.++It takes most of its inspiration from the Ruby library, [Sprockets], hence the+name.++Status+------++Alpha.++How to use it+-------------++```haskell+import Web.Herringbone++fay, sass :: PP++hb = Herringbone+hb = herringbone+ ( addSourceDir "assets"+ . setDestDir "compiled_assets"+ . addPreprocessors [fay, sass]+ )++-- You can now access assets programmatically+asset <- findAsset hb (fromJust . makeLogicalPath $ ["application.js"])++-- Or serve them with a Wai application+app = toApplication hb+```++For more information, go and look at the documentation on [Hackage]!++[Sprockets]: https://github.com/sstephenson/sprockets+[Hackage]: http://hackage.haskell.org/package/herringbone
herringbone.cabal view
@@ -1,5 +1,5 @@ name: herringbone-version: 0.0.6+version: 0.0.7 synopsis: A library for compiling and serving static web assets. description: A library for compiling and serving static web assets. For more information, please see <https://github.com/hdgarrood/herringbone>. license: MIT@@ -9,6 +9,9 @@ category: Web build-type: Simple cabal-version: >=1.10+extra-source-files:+ README.md+ test/*.hs source-repository head type: git@@ -83,8 +86,8 @@ default-language: Haskell2010 executable herringbone-test-server- hs-source-dirs: test- build-depends: base, herringbone, text, warp- main-is: TestServer.hs- default-language: Haskell2010- default-extensions: OverloadedStrings+ hs-source-dirs: test+ build-depends: base, herringbone, text, warp+ main-is: TestServer.hs+ default-language: Haskell2010+ default-extensions: OverloadedStrings
+ test/AssertionsHelper.hs view
@@ -0,0 +1,63 @@+module AssertionsHelper where++import Control.Applicative+import Control.Monad+import Data.Text (Text)+import Data.List (sort)+import Test.HUnit hiding (path)+import Prelude hiding (FilePath)+import Filesystem.Path.CurrentOS (FilePath, (</>))+import Prelude hiding (FilePath)+import qualified Filesystem as F++import LazinessHelper+import TestHerringbone+import Web.Herringbone++testWithInputs :: String -> (a -> Assertion) -> [a] -> Test+testWithInputs groupName f =+ TestLabel groupName . TestList . map mkTest . zipWith assignName ([1,2..] :: [Int])+ where+ mkTest (name, input) = TestLabel name (TestCase (f input))+ assignName n input = ("input #" ++ show n, input)++resultsDir :: FilePath+resultsDir = "test/resources/results"++testWithExpectedResult :: Text -> Assertion+testWithExpectedResult logicalPathText = do+ let logicalPath = lp logicalPathText+ let filePath = toFilePath logicalPath+ result <- findAsset testHB logicalPath+ either (fail . show)+ (assertResultMatches filePath . assetFilePath)+ result++assertResultMatches :: FilePath -> FilePath -> Assertion+assertResultMatches resultName filePath = do+ let resultPath = resultsDir </> resultName+ assertFileContentsMatch resultPath filePath++-- Extra assertions+assertFileExists :: FilePath -> Assertion+assertFileExists path = do+ exists <- F.isFile path+ assertBool ("Expected a file to exist: " ++ es path) exists++assertFileContentsMatch :: FilePath -> FilePath -> Assertion+assertFileContentsMatch pathA pathB = do+ matches <- (==) <$> F.readFile pathA <*> F.readFile pathB+ assertBool ("expected file contents to match\n" +++ "this file: " ++ es pathA ++ "\n" +++ "versus this one: " ++ es pathB ++ "\n") matches++assertEqual' :: (Eq a, Show a) => a -> a -> Assertion+assertEqual' = assertEqual ""++assertSameElems :: (Eq a, Show a, Ord a) => [a] -> [a] -> Assertion+assertSameElems xs ys = assertEqual' (sort xs) (sort ys)++assertIsRight :: Show a => Either a b -> Assertion+assertIsRight (Right _) = return ()+assertIsRight (Left x) = assertFailure $+ "Expected a Right value; got: Left " ++ show x
+ test/FindAssetSpec.hs view
@@ -0,0 +1,57 @@+module FindAssetSpec where++import Filesystem.Path.CurrentOS ((</>))+import Prelude hiding (FilePath)+import qualified Filesystem as F+import Test.Hspec+import Test.HUnit hiding (path)++import Web.Herringbone+import SpecHelper++spec :: Spec+spec = do+ let destDir = hbDestDir testHB++ context "without preprocessors" $ do+ let logPath = lp "buildNoPreprocessors.js"++ it "should copy a source file to the destination directory" $ do+ asset' <- findAsset testHB logPath++ assertIsRight asset'+ let Right asset = asset'+ assertFileExists (assetFilePath asset)+ assertFileContentsMatch (assetSourcePath asset)+ (assetFilePath asset)++ it "should get the modification time of the source file" $ do+ Right asset <- findAsset testHB logPath+ sourceMTime <- F.getModified (assetSourcePath asset)++ assertEqual' sourceMTime (assetModifiedTime asset)++ it "should not compile unless necessary" $ do+ Right asset <- findAsset testHB logPath+ mTime <- F.getModified (assetFilePath asset)+ Right asset' <- findAsset testHB logPath+ mTime' <- F.getModified (assetFilePath asset')++ assertEqual' mTime mTime'++ context "with preprocessors" $ do+ it "should run a single preprocessor" $ do+ testWithExpectedResult "onePreprocessor.js"++ it "should run preprocessors in the correct order" $ do+ testWithExpectedResult "threePreprocessors.js"++ context "when there's a compile error" $ do+ it "should report the error" $ do+ Left result <- findAsset testHB (lp "compileError.css")+ assertEqual' (AssetCompileError "Oh snap!") result++ it "should not create the output file" $ do+ _ <- findAsset testHB (lp "compileError.css")+ exists <- F.isFile (destDir </> "compileError.css")+ assert (not exists)
+ test/LazinessHelper.hs view
@@ -0,0 +1,14 @@+module LazinessHelper where++import Web.Herringbone+import Data.Text (Text)+import qualified Data.Text as T+import Prelude hiding (FilePath)+import Filesystem.Path.CurrentOS (FilePath)+import qualified Filesystem.Path.CurrentOS as F++es :: FilePath -> String+es = F.encodeString++lp :: Text -> LogicalPath+lp = unsafeMakeLogicalPath . T.splitOn "/"
+ test/LocateAssetsSpec.hs view
@@ -0,0 +1,86 @@+module LocateAssetsSpec where++import Test.Hspec+import Test.Hspec.HUnit+import Test.HUnit hiding (path)+import Data.Text (Text)+import Filesystem.Path.CurrentOS (FilePath, (</>))+import Prelude hiding (FilePath)++import Web.Herringbone.LocateAssets+import Web.Herringbone.Types+import SpecHelper++spec :: Spec+spec = do+ fromHUnitTest $ testWithInputs "getExtraExtensions"+ test_getExtraExtensions+ data_getExtraExtensions++ fromHUnitTest $ testWithInputs "resolvePPs"+ test_resolvePPs+ data_resolvePPs++ fromHUnitTest $ testWithInputs "lookupPP"+ test_lookupPP+ data_lookupPP++ fromHUnitTest $ testWithInputs "locateAssets"+ test_locateAssets+ data_locateAssets++test_getExtraExtensions :: (FilePath, FilePath, Maybe [Text]) -> Assertion+test_getExtraExtensions (pathWithExts, path, expected) =+ assertEqual' expected (getExtraExtensions pathWithExts path)++data_getExtraExtensions :: [(FilePath, FilePath, Maybe [Text])]+data_getExtraExtensions =+ [ ("game.js" , "game.js.coffee" , Just ["coffee"])+ , ("style.css" , "game.js.coffee" , Nothing)+ , ("game.js" , "game.js.coffee.erb" , Just ["erb", "coffee"])+ ]++allPPs :: PPs+allPPs = fromList [pp1, pp2]++test_resolvePPs :: (PPs, FilePath, FilePath, Maybe [PP]) -> Assertion+test_resolvePPs (pps, assetPath, sourcePath, expected) =+ assertEqual' expected (resolvePPs pps assetPath sourcePath)++data_resolvePPs :: [(PPs, FilePath, FilePath, Maybe [PP])]+data_resolvePPs =+ [ (noPPs , "test.js" , "test.js" , Just [])+ , (noPPs , "test.js" , "test.js.pp1" , Nothing)+ , (allPPs , "test.js" , "test.js.pp1" , Just [pp1])+ , (allPPs , "test.js" , "test.js.pp1.pp2" , Just [pp2, pp1])+ , (allPPs , "style.css" , "test.js.pp1" , Nothing)+ ]++test_lookupPP :: (Text, PPs, Maybe PP) -> Assertion+test_lookupPP (ext, pps, expected) =+ assertEqual' expected (lookupPP ext pps)++data_lookupPP :: [(Text, PPs, Maybe PP)]+data_lookupPP =+ [ ("sass" , noPPs , Nothing)+ , ("sass" , allPPs , Nothing)+ , ("pp1" , allPPs , Just pp1)+ ]++test_locateAssets :: (LogicalPath, [(FilePath, [PP])]) -> Assertion+test_locateAssets (logPath, expected) = do+ assets <- locateAssets testHB logPath+ assertSameElems expected assets++data_locateAssets :: [(LogicalPath, [(FilePath, [PP])])]+data_locateAssets =+ [ (lp "locateAssets.js", [(base1 </> "locateAssets.js", [])])+ , (lp "locateAssets.css", [ (base1 </> "locateAssets.css", [])+ , (base1 </> "locateAssets.css.pp1", [pp1])+ , (base2 </> "locateAssets.css", [])+ ])+ , (lp "html/locateAssets.html", [(base1 </> "html/locateAssets.html", [])])+ ]+ where+ base1 = "test/resources/assets"+ base2 = "test/resources/assets2"
+ test/PreprocessStdIOSpec.hs view
@@ -0,0 +1,9 @@+module PreprocessStdIOSpec where++import Test.Hspec+import SpecHelper++spec :: Spec+spec = do+ it "should use stdIO" $ do+ testWithExpectedResult "hello.txt"
+ test/SpecHelper.hs view
@@ -0,0 +1,9 @@+module SpecHelper (+ module AssertionsHelper,+ module LazinessHelper,+ module TestHerringbone+) where++import AssertionsHelper+import LazinessHelper+import TestHerringbone
+ test/TestHerringbone.hs view
@@ -0,0 +1,55 @@+module TestHerringbone where++import Data.Text (Text)+import qualified Data.Text.Encoding as T+import Data.Monoid+import Network.Wai.Handler.Warp++import Web.Herringbone+import Web.Herringbone.Preprocessor.StdIO+import Web.Herringbone.Preprocessor.CoffeeScript+import Web.Herringbone.Preprocessor.Sass++mkMockPP :: Text -> PP+mkMockPP ext = PP { ppExtension = ext+ , ppAction = \sourceData -> do+ return . Right $+ "Preprocessed as: " <> T.encodeUtf8 ext <> "\n" <>+ sourceData+ }++pp1 :: PP+pp1 = mkMockPP "pp1"++pp2 :: PP+pp2 = mkMockPP "pp2"++pp3 :: PP+pp3 = mkMockPP "pp3"++failingPP :: PP+failingPP = PP { ppExtension = "fails"+ , ppAction = const . return . Left $ "Oh snap!"+ }++sed :: PP+sed = makeStdIOPP "sed" "sed" ["-e", "s/e/u/"]++testHB :: Herringbone+testHB = herringbone+ ( addSourceDir "test/resources/assets"+ . addSourceDir "test/resources/assets2"+ . setDestDir "test/resources/compiled_assets"+ . addPreprocessors [ pp1+ , pp2+ , pp3+ , failingPP+ , coffeeScript+ , sass+ , scss+ , sed+ ]+ )++runTestHB :: Int -> IO ()+runTestHB port = run port (toApplication testHB)
+ test/WaiAdapterTest.hs view
@@ -0,0 +1,15 @@+module WaiAdapterTest where++import Network.Wai+import Network.Wai.Handler.Warp++import Web.Herringbone+import SpecHelper++app :: Application+app = toApplication testHB++runApp :: IO ()+runApp = do+ putStrLn "running on port 3002..."+ run 3002 app