From f3cb291bf1b8027df43bd057756718b58b507fe1 Mon Sep 17 00:00:00 2001 From: Kamil Ciemniewski Date: Sat, 21 Jan 2012 14:56:30 +0100 Subject: [PATCH] Init --- .gitignore | 1 + LICENSE | 30 ++++++ README.markdown | 101 +++++++++++++++++++++ Setup.hs | 2 + snaplet-environments.cabal | 74 +++++++++++++++ src/Snap/Snaplet/Environments.hs | 47 ++++++++++ src/Snap/Snaplet/Environments/Instances.hs | 9 ++ 7 files changed, 264 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.markdown create mode 100644 Setup.hs create mode 100644 snaplet-environments.cabal create mode 100644 src/Snap/Snaplet/Environments.hs create mode 100644 src/Snap/Snaplet/Environments/Instances.hs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7773828 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f1903fb --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +Copyright (c)2012, Kamil Ciemniewski + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Kamil Ciemniewski nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..07ef9c9 --- /dev/null +++ b/README.markdown @@ -0,0 +1,101 @@ +## Snaplet-Environments + +Snap Framework ( http://snapframework.com ) support for having different +configuration environments. + +Ex. you can have development database "your_app_dev" and production +"your_app_prod" and test "your_app_test" and switch between them +just by running your app in those environments. + +Depends on Snap 0.7.* and others + +### Overview + +This isn't traditional snaplet that adds something to your App state. +It just base its works on Snap's configuration API giving you a more +easy to use ways of getting conf values for environment that your app +is currently running. + +Let's say you have defined "development" environment in your _snaplet.cfg_ +Then running app in this env is as simple as: + +``` +yourapp @development +``` + +### Defining environments + +You just have to put your environments group in config file like ex.: + +``` +# Your App main configuration file + +database-prefix = "yourapp" +database-address = "localhost" + +recaptcha-test-mode = false +recaptcha-key = "sdfgsfdgsdfgsdgs" + +heist-root = "resources/templates" + +mailers-test-mode = false + +environments +{ + development + { + database-name = "$(database-prefix)_development" + } + + production + { + database-name = "$(database-prefix)_production" + } + + test + { + database-name = "$(database-prefix)_test" + recaptcha-test-mode = true + mailers-test-mode = true + } +} +``` + +If you don't specify which env to run your app - it'll choose first +env declaration for you. In this exaple: + +``` +yourapp +``` + +Would run yourapp in "development" environment. + +### Integration + +In Site.hs + +``` +-- (...) +makeSnaplet "app" "An snaplet example application." Nothing $ do + heistRoot <- lookupEnvDefault "heist-root" "resources/templates" + + dbName <- lookupEnvDefault "database-name" "yourapp_development" + dbAddress <- lookupEnvDefault "database-address" "localhost" + dbConns <- lookupEnvDefault "database-connections" 12 + + h <- nestSnaplet "heist" heist $ do + heistInit heistRoot + m <- nestSnaplet "mongoDB" mongoDB $ + mongoDBInit (host dbAddress) dbConns dbName + + --- (...) +``` + +The "lookupEnvDefault" helper function reads value from current environment group +or if it doesn't have given key - from root of conf file. + +``` +lookupEnvDefault :: (Configured a, Monad (m b v), MonadSnaplet m, MonadIO (m b v)) => Name -> a -> m b v a +``` + +That's all:) \ No newline at end of file diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/snaplet-environments.cabal b/snaplet-environments.cabal new file mode 100644 index 0000000..66badee --- /dev/null +++ b/snaplet-environments.cabal @@ -0,0 +1,74 @@ +-- snaplet-environments.cabal auto-generated by cabal init. For +-- additional options, see +-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr. +-- The name of the package. +Name: snaplet-environments + +-- The package version. See the Haskell package versioning policy +-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for +-- standards guiding when and how versions should be incremented. +Version: 0.1 + +-- A short (one-line) description of the package. +Synopsis: Provides ability to easly read configuration based on given app environment given at command line, envs are defined in app configuration file + +-- A longer description of the package. +-- Description: + +-- The license under which the package is released. +License: BSD3 + +-- The file containing the license text. +License-file: LICENSE + +-- The package author(s). +Author: Kamil Ciemniewski + +-- An email address to which users can send suggestions, bug reports, +-- and patches. +Maintainer: ciemniewski.kamil@gmail.com + +-- A copyright notice. +-- Copyright: + +Category: Web + +Build-type: Simple + +-- Extra files to be distributed with the package, such as examples or +-- a README. +-- Extra-source-files: + +-- Constraint on the version of Cabal needed to build this package. +Cabal-version: >=1.6 + + +Library + hs-source-dirs: src + + library + exposed-modules: Snap.Snaplet.Environments, + Snap.Snaplet.Environments.Instances + + -- Packages needed in order to build this package. + Build-depends: + base >= 4 && < 5, + snap == 0.7.*, + snap-core == 0.7.*, + mtl >= 2 && < 3, + configurator == 0.2.0.0, + regex-tdfa == 1.1.8, + unordered-containers == 0.1.4.3, + text >= 0.11 && < 0.12, + haskell98 == 1.1.0.1, + bson == 0.1.6 + + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source. + -- Build-tools: + + extensions: + OverloadedStrings + , FlexibleInstances + , TypeSynonymInstances + , MultiParamTypeClasses + \ No newline at end of file diff --git a/src/Snap/Snaplet/Environments.hs b/src/Snap/Snaplet/Environments.hs new file mode 100644 index 0000000..4c14a24 --- /dev/null +++ b/src/Snap/Snaplet/Environments.hs @@ -0,0 +1,47 @@ +module Snap.Snaplet.Environments( module Data.Configurator, lookupEnvDefault, module Snap.Snaplet.Environments.Instances ) where + +import System (getArgs) + +import Control.Monad.Reader + +import Snap.Snaplet +import Data.Configurator +import Data.Configurator.Types + +import Snap.Snaplet.Environments.Instances + +import qualified Data.Text as T +import qualified Data.HashMap.Lazy as HM +import Data.List (filter, find) +import Text.Regex.TDFA + +-- | This function takes current env subconfig and at its base +-- looks up given name +lookupEnvDefault :: (Configured a, Monad (m b v), MonadSnaplet m, MonadIO (m b v)) => Name -> a -> m b v a +lookupEnvDefault name def = do + mainConf <- getSnapletUserConfig + subName <- getNameForCurEnv name mainConf + mv <- liftIO $ Data.Configurator.lookup mainConf subName + case mv of + Nothing -> liftIO $ lookupDefault def mainConf name + Just v -> (liftIO $ putStrLn "Just") >> return v + +getNameForCurEnv :: (Monad (m b v), MonadSnaplet m, MonadIO (m b v)) => Name -> Config -> m b v Name +getNameForCurEnv name cfg = do + env <- getCurrentEnv cfg + return $ T.pack $ "environments." ++ env ++ "." ++ (T.unpack name) + +getCurrentEnv :: (Monad (m b v), MonadSnaplet m, MonadIO (m b v)) => Config -> m b v String +getCurrentEnv cfg = do + mopt <- return . find (\a -> take 1 a == "@") =<< liftIO getArgs + case mopt of + Nothing -> do + hm <- liftIO $ getMap cfg + case filter (\k -> (T.unpack k) =~ ("app.environments." :: String)) $ HM.keys hm of + [] -> error "You have to put at least one env definition in your config file." + (x:xs) -> return $ T.unpack $ (T.split (== '.') x) !! 2 + Just opt -> do + hm <- liftIO $ getMap cfg + case length (filter (\k -> (T.unpack k) =~ ("app.environments." ++ (tail opt))) $ HM.keys hm) > 0 of + True -> return $ tail opt + False -> error $ "Given env name: " ++ opt ++ " wasn't found in your config file." \ No newline at end of file diff --git a/src/Snap/Snaplet/Environments/Instances.hs b/src/Snap/Snaplet/Environments/Instances.hs new file mode 100644 index 0000000..6544ce8 --- /dev/null +++ b/src/Snap/Snaplet/Environments/Instances.hs @@ -0,0 +1,9 @@ +module Snap.Snaplet.Environments.Instances where + +import Data.Configurator.Types +import qualified Data.UString as U +import qualified Data.Text as T + +instance Configured U.UString where + convert (String t) = Just $ U.u $ T.unpack t + convert _ = Nothing \ No newline at end of file