Replace Cabal-managed config loading with FileEmbed

This uses the file-embed package and TemplateHaskell to load custom
configuration during compilation instead of loading it at runtime.
This commit is contained in:
Joshua Clayton 2016-08-18 17:59:12 -04:00
parent 8b50f5dd68
commit fde9f69a64
No known key found for this signature in database
GPG Key ID: 5B6558F77E9A8118
5 changed files with 55 additions and 56 deletions

View File

@ -1,31 +1,32 @@
{-# LANGUAGE TemplateHaskell #-}
module Unused.ResultsClassifier.Config module Unused.ResultsClassifier.Config
( loadConfig ( loadConfig
, loadAllConfigurations , loadAllConfigurations
) where ) where
import qualified Data.Bifunctor as BF import qualified Data.Bifunctor as BF
import qualified Data.ByteString as BS
import qualified Data.Either as E import qualified Data.Either as E
import qualified Data.FileEmbed as FE
import qualified Data.Yaml as Y import qualified Data.Yaml as Y
import qualified Paths_unused as Paths
import qualified System.Directory as D import qualified System.Directory as D
import System.FilePath ((</>)) import System.FilePath ((</>))
import Unused.ResultsClassifier.Types (LanguageConfiguration, ParseConfigError(..)) import Unused.ResultsClassifier.Types (LanguageConfiguration, ParseConfigError(..))
import Unused.Util (safeReadFile) import Unused.Util (safeReadFile)
loadConfig :: IO (Either String [LanguageConfiguration]) loadConfig :: Either String [LanguageConfiguration]
loadConfig = do loadConfig = Y.decodeEither defaultConfigFile
configFileName <- Paths.getDataFileName ("data" </> "config.yml")
either defaultConfigFile :: BS.ByteString
(const $ Left "default config not found") defaultConfigFile = $(FE.embedFile "data/config.yml")
Y.decodeEither
<$> safeReadFile configFileName
loadAllConfigurations :: IO (Either [ParseConfigError] [LanguageConfiguration]) loadAllConfigurations :: IO (Either [ParseConfigError] [LanguageConfiguration])
loadAllConfigurations = do loadAllConfigurations = do
homeDir <- D.getHomeDirectory homeDir <- D.getHomeDirectory
defaultConfig <- addSourceToLeft "default config" <$> loadConfig let defaultConfig = addSourceToLeft "default config" loadConfig
localConfig <- loadConfigFromFile ".unused.yml" localConfig <- loadConfigFromFile ".unused.yml"
userConfig <- loadConfigFromFile $ homeDir </> ".unused.yml" userConfig <- loadConfigFromFile $ homeDir </> ".unused.yml"

View File

@ -16,10 +16,10 @@ spec = parallel $
describe "calculateLikelihood" $ do describe "calculateLikelihood" $ do
it "prefers language-specific checks first" $ do it "prefers language-specific checks first" $ do
let railsMatches = [ TermMatch "ApplicationController" "app/controllers/application_controller.rb" Nothing 1 ] let railsMatches = [ TermMatch "ApplicationController" "app/controllers/application_controller.rb" Nothing 1 ]
removalLikelihood' railsMatches `shouldReturn` Low removalLikelihood' railsMatches `shouldBe` Low
let elixirMatches = [ TermMatch "AwesomeView" "web/views/awesome_view.ex" Nothing 1 ] let elixirMatches = [ TermMatch "AwesomeView" "web/views/awesome_view.ex" Nothing 1 ]
removalLikelihood' elixirMatches `shouldReturn` Low removalLikelihood' elixirMatches `shouldBe` Low
it "weighs widely-used methods as low likelihood" $ do it "weighs widely-used methods as low likelihood" $ do
let matches = [ TermMatch "full_name" "app/models/user.rb" Nothing 4 let matches = [ TermMatch "full_name" "app/models/user.rb" Nothing 4
@ -28,19 +28,19 @@ spec = parallel $
, TermMatch "full_name" "spec/models/user_spec.rb" Nothing 10 , TermMatch "full_name" "spec/models/user_spec.rb" Nothing 10
] ]
removalLikelihood' matches `shouldReturn` Low removalLikelihood' matches `shouldBe` Low
it "weighs only-occurs-once methods as high likelihood" $ do it "weighs only-occurs-once methods as high likelihood" $ do
let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1 ] let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1 ]
removalLikelihood' matches `shouldReturn` High removalLikelihood' matches `shouldBe` High
it "weighs methods that seem to only be tested and never used as high likelihood" $ do it "weighs methods that seem to only be tested and never used as high likelihood" $ do
let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1 let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1
, TermMatch "obscure_method" "spec/models/user_spec.rb" Nothing 5 , TermMatch "obscure_method" "spec/models/user_spec.rb" Nothing 5
] ]
removalLikelihood' matches `shouldReturn` High removalLikelihood' matches `shouldBe` High
it "weighs methods that seem to only be tested and used in one other area as medium likelihood" $ do it "weighs methods that seem to only be tested and used in one other area as medium likelihood" $ do
let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1 let matches = [ TermMatch "obscure_method" "app/models/user.rb" Nothing 1
@ -49,14 +49,15 @@ spec = parallel $
, TermMatch "obscure_method" "spec/controllers/user_controller_spec.rb" Nothing 5 , TermMatch "obscure_method" "spec/controllers/user_controller_spec.rb" Nothing 5
] ]
removalLikelihood' matches `shouldReturn` Medium removalLikelihood' matches `shouldBe` Medium
it "doesn't mis-categorize allowed terms from different languages" $ do it "doesn't mis-categorize allowed terms from different languages" $ do
let matches = [ TermMatch "t" "web/models/foo.ex" Nothing 1 ] let matches = [ TermMatch "t" "web/models/foo.ex" Nothing 1 ]
removalLikelihood' matches `shouldReturn` High removalLikelihood' matches `shouldBe` High
removalLikelihood' :: [TermMatch] -> IO RemovalLikelihood removalLikelihood' :: [TermMatch] -> RemovalLikelihood
removalLikelihood' ms = do removalLikelihood' =
(Right config) <- loadConfig rLikelihood . trRemoval . calculateLikelihood config . resultsFromMatches
return $ rLikelihood $ trRemoval $ calculateLikelihood config $ resultsFromMatches ms where
(Right config) = loadConfig

View File

@ -23,7 +23,7 @@ spec = parallel $
let r2Matches = [ TermMatch "other" "app/path/other.rb" Nothing 1 ] let r2Matches = [ TermMatch "other" "app/path/other.rb" Nothing 1 ]
let r2Results = TermResults "other" ["other"] r2Matches (Occurrences 0 0) (Occurrences 1 1) (Occurrences 1 1) (Removal High "occurs once") Nothing let r2Results = TermResults "other" ["other"] r2Matches (Occurrences 0 0) (Occurrences 1 1) (Occurrences 1 1) (Removal High "occurs once") Nothing
(Right config) <- loadConfig let (Right config) = loadConfig
let result = parseResults config $ SearchResults $ r1Matches ++ r2Matches let result = parseResults config $ SearchResults $ r1Matches ++ r2Matches
@ -50,7 +50,7 @@ spec = parallel $
] ]
(Right config) <- loadConfig let (Right config) = loadConfig
let searchResults = r1Matches ++ r2Matches let searchResults = r1Matches ++ r2Matches
let result = parseResults config $ SearchResults searchResults let result = parseResults config $ SearchResults searchResults
@ -60,6 +60,6 @@ spec = parallel $
Map.fromList [ ("admin?|be_admin", results) ] Map.fromList [ ("admin?|be_admin", results) ]
it "handles empty input" $ do it "handles empty input" $ do
(Right config) <- loadConfig let (Right config) = loadConfig
let result = parseResults config $ SearchResults [] let result = parseResults config $ SearchResults []
result `shouldBe` Map.fromList [] result `shouldBe` Map.fromList []

View File

@ -19,114 +19,114 @@ spec = parallel $ do
let match = TermMatch "ApplicationController" "app/controllers/application_controller.rb" Nothing 1 let match = TermMatch "ApplicationController" "app/controllers/application_controller.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
railsAutoLowLikelihood result `shouldReturn` True railsAutoLowLikelihood result `shouldBe` True
it "allows helpers" $ do it "allows helpers" $ do
let match = TermMatch "ApplicationHelper" "app/helpers/application_helper.rb" Nothing 1 let match = TermMatch "ApplicationHelper" "app/helpers/application_helper.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
railsAutoLowLikelihood result `shouldReturn` True railsAutoLowLikelihood result `shouldBe` True
it "allows migrations" $ do it "allows migrations" $ do
let match = TermMatch "CreateUsers" "db/migrate/20160101120000_create_users.rb" Nothing 1 let match = TermMatch "CreateUsers" "db/migrate/20160101120000_create_users.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
railsAutoLowLikelihood result `shouldReturn` True railsAutoLowLikelihood result `shouldBe` True
it "disallows service objects" $ do it "disallows service objects" $ do
let match = TermMatch "CreatePostWithNotifications" "app/services/create_post_with_notifications.rb" Nothing 1 let match = TermMatch "CreatePostWithNotifications" "app/services/create_post_with_notifications.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
railsAutoLowLikelihood result `shouldReturn` False railsAutoLowLikelihood result `shouldBe` False
it "disallows methods" $ do it "disallows methods" $ do
let match = TermMatch "my_method" "app/services/create_post_with_notifications.rb" Nothing 1 let match = TermMatch "my_method" "app/services/create_post_with_notifications.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
railsAutoLowLikelihood result `shouldReturn` False railsAutoLowLikelihood result `shouldBe` False
it "disallows models that occur in migrations" $ do it "disallows models that occur in migrations" $ do
let model = TermMatch "User" "app/models/user.rb" Nothing 1 let model = TermMatch "User" "app/models/user.rb" Nothing 1
let migration = TermMatch "User" "db/migrate/20160101120000_create_users.rb" Nothing 1 let migration = TermMatch "User" "db/migrate/20160101120000_create_users.rb" Nothing 1
let result = resultsFromMatches [model, migration] let result = resultsFromMatches [model, migration]
railsAutoLowLikelihood result `shouldReturn` False railsAutoLowLikelihood result `shouldBe` False
it "allows matches intermixed with other results" $ do it "allows matches intermixed with other results" $ do
let appToken = TermMatch "ApplicationHelper" "app/helpers/application_helper.rb" Nothing 1 let appToken = TermMatch "ApplicationHelper" "app/helpers/application_helper.rb" Nothing 1
let testToken = TermMatch "ApplicationHelper" "spec/helpers/application_helper_spec.rb" Nothing 10 let testToken = TermMatch "ApplicationHelper" "spec/helpers/application_helper_spec.rb" Nothing 10
let result = resultsFromMatches [appToken, testToken] let result = resultsFromMatches [appToken, testToken]
railsAutoLowLikelihood result `shouldReturn` True railsAutoLowLikelihood result `shouldBe` True
describe "elixirAutoLowLikelihood" $ do describe "elixirAutoLowLikelihood" $ do
it "disallows controllers" $ do it "disallows controllers" $ do
let match = TermMatch "PageController" "web/controllers/page_controller.rb" Nothing 1 let match = TermMatch "PageController" "web/controllers/page_controller.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` False elixirAutoLowLikelihood result `shouldBe` False
it "allows views" $ do it "allows views" $ do
let match = TermMatch "PageView" "web/views/page_view.rb" Nothing 1 let match = TermMatch "PageView" "web/views/page_view.rb" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
it "allows migrations" $ do it "allows migrations" $ do
let match = TermMatch "CreateUsers" "priv/repo/migrations/20160101120000_create_users.exs" Nothing 1 let match = TermMatch "CreateUsers" "priv/repo/migrations/20160101120000_create_users.exs" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
it "allows tests" $ do it "allows tests" $ do
let match = TermMatch "UserTest" "test/models/user_test.exs" Nothing 1 let match = TermMatch "UserTest" "test/models/user_test.exs" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
it "allows Mixfile" $ do it "allows Mixfile" $ do
let match = TermMatch "Mixfile" "mix.exs" Nothing 1 let match = TermMatch "Mixfile" "mix.exs" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
it "allows __using__" $ do it "allows __using__" $ do
let match = TermMatch "__using__" "web/web.ex" Nothing 1 let match = TermMatch "__using__" "web/web.ex" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
it "disallows service modules" $ do it "disallows service modules" $ do
let match = TermMatch "CreatePostWithNotifications" "web/services/create_post_with_notifications.ex" Nothing 1 let match = TermMatch "CreatePostWithNotifications" "web/services/create_post_with_notifications.ex" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` False elixirAutoLowLikelihood result `shouldBe` False
it "disallows functions" $ do it "disallows functions" $ do
let match = TermMatch "my_function" "web/services/create_post_with_notifications.ex" Nothing 1 let match = TermMatch "my_function" "web/services/create_post_with_notifications.ex" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
elixirAutoLowLikelihood result `shouldReturn` False elixirAutoLowLikelihood result `shouldBe` False
it "allows matches intermixed with other results" $ do it "allows matches intermixed with other results" $ do
let appToken = TermMatch "UserView" "web/views/user_view.ex" Nothing 1 let appToken = TermMatch "UserView" "web/views/user_view.ex" Nothing 1
let testToken = TermMatch "UserView" "test/views/user_view_test.exs" Nothing 10 let testToken = TermMatch "UserView" "test/views/user_view_test.exs" Nothing 10
let result = resultsFromMatches [appToken, testToken] let result = resultsFromMatches [appToken, testToken]
elixirAutoLowLikelihood result `shouldReturn` True elixirAutoLowLikelihood result `shouldBe` True
describe "haskellAutoLowLikelihood" $ do describe "haskellAutoLowLikelihood" $ do
it "allows instance" $ do it "allows instance" $ do
let match = TermMatch "instance" "src/Lib/Types.hs" Nothing 1 let match = TermMatch "instance" "src/Lib/Types.hs" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
haskellAutoLowLikelihood result `shouldReturn` True haskellAutoLowLikelihood result `shouldBe` True
it "allows items in the *.cabal file" $ do it "allows items in the *.cabal file" $ do
let match = TermMatch "Lib.SomethingSpec" "lib.cabal" Nothing 1 let match = TermMatch "Lib.SomethingSpec" "lib.cabal" Nothing 1
let result = resultsFromMatches [match] let result = resultsFromMatches [match]
haskellAutoLowLikelihood result `shouldReturn` True haskellAutoLowLikelihood result `shouldBe` True
describe "autoLowLikelihood" $ describe "autoLowLikelihood" $
it "doesn't qualify as low when no matchers are present in a language config" $ do it "doesn't qualify as low when no matchers are present in a language config" $ do
@ -136,18 +136,17 @@ spec = parallel $ do
autoLowLikelihood languageConfig result `shouldBe` False autoLowLikelihood languageConfig result `shouldBe` False
configByName :: String -> IO LanguageConfiguration configByName :: String -> LanguageConfiguration
configByName s = do configByName s = config'
(Right config) <- loadConfig where
let (Just config') = find ((==) s . lcName) config (Right config) = loadConfig
(Just config') = find ((==) s . lcName) config
return config' railsAutoLowLikelihood :: TermResults -> Bool
railsAutoLowLikelihood = autoLowLikelihood (configByName "Rails")
railsAutoLowLikelihood :: TermResults -> IO Bool elixirAutoLowLikelihood :: TermResults -> Bool
railsAutoLowLikelihood r = (`autoLowLikelihood` r) <$> configByName "Rails" elixirAutoLowLikelihood = autoLowLikelihood (configByName "Phoenix")
elixirAutoLowLikelihood :: TermResults -> IO Bool haskellAutoLowLikelihood :: TermResults -> Bool
elixirAutoLowLikelihood r = (`autoLowLikelihood` r) <$> configByName "Phoenix" haskellAutoLowLikelihood = autoLowLikelihood (configByName "Haskell")
haskellAutoLowLikelihood :: TermResults -> IO Bool
haskellAutoLowLikelihood r = (`autoLowLikelihood` r) <$> configByName "Haskell"

View File

@ -12,7 +12,6 @@ category: CLI
build-type: Simple build-type: Simple
-- extra-source-files: -- extra-source-files:
cabal-version: >=1.10 cabal-version: >=1.10
data-files: data/config.yml
library library
hs-source-dirs: src hs-source-dirs: src
@ -60,7 +59,6 @@ library
, Unused.CLI.ProgressIndicator , Unused.CLI.ProgressIndicator
, Unused.CLI.ProgressIndicator.Internal , Unused.CLI.ProgressIndicator.Internal
, Unused.CLI.ProgressIndicator.Types , Unused.CLI.ProgressIndicator.Types
other-modules: Paths_unused
build-depends: base >= 4.7 && < 5 build-depends: base >= 4.7 && < 5
, process , process
, containers , containers
@ -81,6 +79,7 @@ library
, transformers , transformers
, megaparsec , megaparsec
, inflections , inflections
, file-embed
ghc-options: -Wall ghc-options: -Wall
default-language: Haskell2010 default-language: Haskell2010
@ -114,7 +113,6 @@ test-suite unused-test
, Unused.UtilSpec , Unused.UtilSpec
, Unused.Cache.FindArgsFromIgnoredPathsSpec , Unused.Cache.FindArgsFromIgnoredPathsSpec
, Unused.AliasesSpec , Unused.AliasesSpec
, Paths_unused
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
default-language: Haskell2010 default-language: Haskell2010