packages feed

inflections 0.1.0.5 → 0.1.0.6

raw patch · 14 files changed

+224/−120 lines, 14 files

Files

Text/Inflections.hs view
@@ -12,6 +12,62 @@ "Inflections" library found in Rails:  <http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html>++While many of the functions in this library are the same as in implementations+in Rails' ActiveSupport, the philosophy of this library is fundamentally+different.  Where Rails tries to be as permissive as possible, and return a+String when given any input, this library tries to output strings that make+sense according to the function that is called.++When you look closely at many of the functions in Rails' inflections+library, you will notice that many of them are partial. That is, they only+have well-defined output for some of the possible inputs to the function allowed+by the type system. As an example, let's take the @underscore@ function. In+Rails, it works like this:++>>> "fooBar".underscore+"foo_bar"++Looks ok so far. However, it's also easy to produce less expected results:++>>> "foo bar".underscore+"foo bar"++The output isn't underscored - it contains a space! It turns out that some of+the functions from Inflections in ActiveSupport are /partial/. Ie., the outputs+are really only specified for a certain range of the inputs allowed by the+String type.++In the Haskell inflections library, we aim to deliver more predictable results+by separating the parsing of strings into tokens from the application of+transformations. Let's see an example.++First, we tokenize an underscored String using 'parseSnakeCase':++>>> parseSnakeCase [] "foo_bar"+Right [Word "foo",Word "bar"]++We can chain together the tokenization of the input String and the+transformation to CamelCase by using 'Control.Monad.LiftM':++>>> import Control.Monad (liftM)+>>> liftM camelize $ parseSnakeCase "foo_bar"++By separating out the tokenization from the application of inflections, we also+end up with useful libraries for validating input which can be used+independently:++>>> parseSnakeCase [] "fooBar"+Left "(unknown)" (line 1, column 4):+unexpected 'B'+expecting lowercase letter, "_" or end of input++This library is still a work-in-progress, and contributions are welcome for+missing pieces and to fix bugs. Please see the Github page to contribute with+code or bug reports:++<https://github.com/stackbuilders/inflections-hs>+ -}  module Text.Inflections@@ -19,11 +75,13 @@     , camelizeCustom      , dasherize-    , dasherizeCustom +    , humanize+     , underscore-    , underscoreCustom +    , titleize+     , defaultMap      , parameterize@@ -34,41 +92,34 @@      , ordinal     , ordinalize++    , parseSnakeCase+    , parseCamelCase+    , Transliterations     ) where -import Data.Char (isAscii)-import qualified Data.Map as Map -import Text.Inflections.Data (defaultMap)+import Text.Inflections.Data ( defaultMap )  import Text.Inflections.Parameterize ( Transliterations                                      , parameterize                                      , parameterizeCustom ) -import Text.Inflections.Underscore ( underscore, underscoreCustom )+import Text.Inflections.Underscore ( underscore )  import Text.Inflections.Camelize ( camelize, camelizeCustom ) -import Text.Inflections.Dasherize ( dasherize, dasherizeCustom )+import Text.Inflections.Humanize ( humanize ) +import Text.Inflections.Titleize ( titleize )++import Text.Inflections.Transliterate ( transliterate, transliterateCustom )++import Text.Inflections.Dasherize ( dasherize )+ import Text.Inflections.Ordinal ( ordinal, ordinalize ) --- |Returns a String after default approximations for changing Unicode characters--- to a valid ASCII range are applied. If you want to supplement the default--- approximations with your own, you should use the transliterateCustom--- function instead of transliterate.-transliterate :: String -> String-transliterate = transliterateCustom "?" defaultMap+import Text.Inflections.Parse.SnakeCase ( parseSnakeCase ) --- |Returns a String after default approximations for changing Unicode characters--- to a valid ASCII range are applied.-transliterateCustom :: String -> Transliterations -> String -> String-transliterateCustom replacement ts = concatMap lookupCharTransliteration-  where lookupCharTransliteration c =-          if isAscii c then -- Don't bother looking up Chars in ASCII range-            [c]-          else-            case Map.lookup c ts of-              Nothing  -> replacement-              Just val -> val+import Text.Inflections.Parse.CamelCase ( parseCamelCase )
Text/Inflections/Camelize.hs view
@@ -1,38 +1,29 @@-module Text.Inflections.Camelize-  ( camelize-  , camelizeCustom )-where+module Text.Inflections.Camelize ( camelize, camelizeCustom ) where -import Text.Inflections.Parse.SnakeCase (Word(..), parser)-import Text.Parsec (ParseError, parse)-import Data.Char (toUpper)+import Text.Inflections.Parse.Types (Word(..)) --- |Turns a String in snake_case into CamelCase. Returns the CamelCase string,--- or a ParseError if the input String is not in valid snake_case.+import Data.Char (toUpper, toLower)++-- |Turns a an input Word List in into CamelCase. Returns the CamelCase String. camelize-  :: String -- ^ The input string, in snake_case-  -> Either ParseError String-camelize s = camelizeCustom [] True s+  :: [Word] -- ^ Input Words to separate with underscores+  -> String -- ^ The camelized String+camelize ws = camelizeCustom True ws --- |Turns an input String in snake_case into CamelCase. Returns the CamelCase--- String, or a ParseError if the input String is not in valid snake_case.--- Accepts arguments to control parsing and output format.+-- |Turns an input Word List into a CamelCase String. camelizeCustom-  :: [String] -- ^ A list of acronyms that will be kept whole and accepted as valid input-  -> Bool     -- ^ Whether to capitalize the first character in the output String-  -> String   -- ^ The input string, in snake_case-  -> Either ParseError String-camelizeCustom acronyms isFirstCap s =-  case parse (parser acronyms) "(unknown)" s of-    Left errs -> Left errs-    Right res -> Right $ concatMap (caseForWord isFirstCap) $ isFirstList res+  :: Bool   -- ^ Whether to capitalize the first character in the output String+  -> [Word] -- ^ The input Words+  -> String -- ^ The camelized String+camelizeCustom isFirstCap = concatMap (caseForWord isFirstCap) . isFirstList + caseForWord :: Bool -> (Word, Bool) -> String-caseForWord True (Word (c:cs), True) = toUpper c : cs-caseForWord False (Word s, True)     = s-caseForWord _ (Word (c:cs), _)       = toUpper c : cs-caseForWord _ (Word [], _)           = []-caseForWord _ (Acronym s, _)         = s+caseForWord True (Word (c:cs), True)  = toUpper c : cs+caseForWord False (Word (c:cs), True) = toLower c : cs+caseForWord _ (Word (c:cs), _)        = toUpper c : cs+caseForWord _ (Word [], _)            = []+caseForWord _ (Acronym s, _)          = s  -- |Returns list with Bool indicating if an element is first. isFirstList :: [a] -> [(a, Bool)]
Text/Inflections/Dasherize.hs view
@@ -1,30 +1,14 @@-module Text.Inflections.Dasherize-  ( dasherize-  , dasherizeCustom )-where+module Text.Inflections.Dasherize ( dasherize ) where -import Text.Inflections.Parse.SnakeCase (Word(..), parser)-import Text.Parsec (ParseError, parse)+import Text.Inflections.Parse.Types (Word(..))  import Data.List (intercalate)  -- |Replaces underscores in a snake_cased string with dashes (hyphens). dasherize-  :: String -- ^ The input string, in snake_case-  -> Either ParseError String-dasherize s = dasherizeCustom [] s---- |Turns a snake_case string into dasherized form. Returns a ParseError if--- the input string is not in proper snake_case.-dasherizeCustom-  :: [String] -- ^ A list of acronyms that will be kept whole and accepted as valid input-  -> String   -- ^ The input string, in snake_case-  -> Either ParseError String-dasherizeCustom acronyms s =-  case parse (parser acronyms) "(unknown)" s of-    Left errs -> Left errs-    Right res -> Right $ intercalate "-" $ map toString res-+  :: [Word] -- ^ Input Words to separate with dashes+  -> String -- ^ The dasherized String+dasherize ws = intercalate "-" $ map toString ws  toString :: Word -> String toString (Acronym s) = s
Text/Inflections/Data.hs view
@@ -2,8 +2,8 @@  import Data.Map (Map, fromList) --- |These default transliterations stolen from the Ruby i18n library ---- https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/transliterator.rb#L41:L69+-- |These default transliterations stolen from the Ruby i18n library - see+-- <https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/transliterator.rb#L41:L69>. defaultMap :: Map Char String defaultMap = fromList [   ('À', "A"), ('Á', "A"), ('Â', "A"), ('Ã', "A"), ('Ä', "A"), ('Å', "A"),
+ Text/Inflections/Humanize.hs view
@@ -0,0 +1,23 @@+module Text.Inflections.Humanize (humanize) where++import Text.Inflections.Parse.Types (Word(..))++import Data.List (intercalate)+import Data.Char (toUpper)++-- |Capitalizes the first word and turns underscores into spaces Like titleize,+-- this is meant for creating pretty output.+humanize+  :: [Word] -- ^ List of Words, first of which will be capitalized+  -> String -- ^ The humanized output+humanize s = intercalate " " $ map caseForWord $ isFirstList s++-- |Returns list with Bool indicating if an element is first.+isFirstList :: [a] -> [(a, Bool)]+isFirstList xs = zip xs $ True : repeat False++caseForWord :: (Word, Bool) -> String+caseForWord (Word (c:cs), True)  = toUpper c : cs+caseForWord (Word s, False)      = s+caseForWord (Word [], _)         = []+caseForWord (Acronym s, _)       = s  -- Acronyms are left intact
Text/Inflections/Parameterize.hs view
@@ -18,8 +18,8 @@                                               , parser                                               , isValidParamChar ) --- |A Map containing mappings from international characters to sequences--- approximating these characters within the ASCII range.+-- |A 'Data.Map.Map' containing mappings from international characters to+-- sequences approximating these characters within the ASCII range. type Transliterations = Map.Map Char String  -- |Replaces special characters in a string so that it may be used as part of a
Text/Inflections/Parse/CamelCase.hs view
@@ -1,23 +1,26 @@ {-# LANGUAGE FlexibleContexts, NoMonomorphismRestriction #-} -module Text.Inflections.Parse.CamelCase ( parser )+module Text.Inflections.Parse.CamelCase ( parseCamelCase ) where -import qualified Text.ParserCombinators.Parsec.Char as C-import qualified Text.Parsec as P+import Text.Parsec  import Text.Inflections.Parse.Types (Word(..)) import Text.Inflections.Parse.Acronym (acronym) +parseCamelCase :: [String] -> String -> Either ParseError [Word]+parseCamelCase acronyms = parse (parser acronyms) "(unknown)"++ -- |Recognizes an input String in CamelCase.-parser :: P.Stream s m Char => [String] -> P.ParsecT s u m [Word]+parser :: Stream s m Char => [String] -> ParsecT s u m [Word] parser acronyms = do-  ws <- P.many $ P.choice [ acronym acronyms, word ]-  P.eof+  ws <- many $ choice [ acronym acronyms, word ]+  eof   return ws -word :: P.Stream s m Char => P.ParsecT s u m Word+word :: Stream s m Char => ParsecT s u m Word word = do-  firstChar <- C.upper P.<|> C.lower-  restChars <- P.many C.lower+  firstChar <- upper <|> lower+  restChars <- many lower   return $ Word $ firstChar : restChars
Text/Inflections/Parse/SnakeCase.hs view
@@ -1,20 +1,23 @@ {-# LANGUAGE FlexibleContexts, NoMonomorphismRestriction #-} -module Text.Inflections.Parse.SnakeCase ( Word(..), parser )+module Text.Inflections.Parse.SnakeCase ( parseSnakeCase ) where -import qualified Text.ParserCombinators.Parsec.Char as C-import qualified Text.Parsec as P import Control.Applicative ((<$>))+import Text.Parsec  import Text.Inflections.Parse.Types (Word(..)) import Text.Inflections.Parse.Acronym (acronym) -word :: P.Stream s m Char => P.ParsecT s u m Word-word = Word <$> P.many1 C.lower+parseSnakeCase :: [String] -> String -> Either ParseError [Word]+parseSnakeCase acronyms = parse (parser acronyms) "(unknown)" -parser :: P.Stream s m Char => [String] -> P.ParsecT s u m [Word]++parser :: Stream s m Char => [String] -> ParsecT s u m [Word] parser acronyms = do-  ws <- (acronym acronyms P.<|> word) `P.sepBy` (C.char '_')-  P.eof+  ws <- (acronym acronyms <|> word) `sepBy` (char '_')+  eof   return ws++word :: Stream s m Char => ParsecT s u m Word+word = Word <$> ((many1 lower) <|> (many1 digit))
Text/Inflections/Parse/Types.hs view
@@ -1,5 +1,12 @@ module Text.Inflections.Parse.Types ( Word(..) ) where -data Word = Word String-          | Acronym String deriving (Show, Eq)+-- |A 'String' that should be kept whole through applied inflections+data Word +    -- | A word that may be transformed by inflection+    = Word String++    -- | A word that may not be transformed by inflections+    | Acronym String++    deriving (Show, Eq)
+ Text/Inflections/Titleize.hs view
@@ -0,0 +1,17 @@+module Text.Inflections.Titleize (titleize) where++import Text.Inflections.Parse.Types (Word(..))++import Data.List (intercalate)+import Data.Char (toUpper)++-- |Capitalizes Capitalizes all the words.+titleize+  :: [Word] -- ^ List of Words, first of which will be capitalized+  -> String -- ^ The titleized String+titleize s = intercalate " " $ map upperCaseWord s++upperCaseWord :: Word -> String+upperCaseWord (Word (c:cs))  = toUpper c : cs+upperCaseWord (Word [])      = []+upperCaseWord (Acronym s)    = s  -- Acronyms are left intact
+ Text/Inflections/Transliterate.hs view
@@ -0,0 +1,31 @@+module Text.Inflections.Transliterate+    ( transliterate+    , transliterateCustom+    )+where++import Text.Inflections.Parameterize ( Transliterations )+import Text.Inflections.Data (defaultMap)++import Data.Char (isAscii)++import qualified Data.Map as Map++-- |Returns a String after default approximations for changing Unicode characters+-- to a valid ASCII range are applied. If you want to supplement the default+-- approximations with your own, you should use the transliterateCustom+-- function instead of transliterate.+transliterate :: String -> String+transliterate = transliterateCustom "?" defaultMap++-- |Returns a String after default approximations for changing Unicode characters+-- to a valid ASCII range are applied.+transliterateCustom :: String -> Transliterations -> String -> String+transliterateCustom replacement ts = concatMap lookupCharTransliteration+  where lookupCharTransliteration c =+          if isAscii c then -- Don't bother looking up Chars in ASCII range+            [c]+          else+            case Map.lookup c ts of+              Nothing  -> replacement+              Just val -> val
Text/Inflections/Underscore.hs view
@@ -1,31 +1,17 @@-module Text.Inflections.Underscore (underscore, underscoreCustom) where+module Text.Inflections.Underscore ( underscore ) where -import Text.Inflections.Parse.CamelCase (parser) import Text.Inflections.Parse.Types (Word(..)) -import Text.Parsec (ParseError, parse) import Data.Char (toLower) import Data.List (intercalate)  -- |Turns a CamelCase string into an underscore_separated String. underscore-  :: String -- ^ A String in CamelCase-  -> Either ParseError String-underscore s = underscoreCustom [] s---- |Changes an input String in CamelCase into a String of Words separated by--- underscores. Accepts options for customization.-underscoreCustom-  :: [String] -- ^ A list of acronyms that will be kept whole and accepted as valid input-  -> String -- ^ A String in CamelCase-  -> Either ParseError String-underscoreCustom acronyms s =-  case parse (parser acronyms) "(unknown)" s of-    Left errs -> Left errs-    Right res -> Right $ intercalate "_" $ map toDowncasedString res+  :: [Word] -- ^ Input Words to separate with underscores+  -> String -- ^ The underscored String+underscore ws = intercalate "_" $ map toDowncasedString ws   toDowncasedString :: Word -> String toDowncasedString (Acronym s) = map toLower s-toDowncasedString (Word s) = map toLower s-+toDowncasedString (Word s)    = map toLower s
inflections.cabal view
@@ -1,5 +1,5 @@ name:                inflections-version:             0.1.0.5+version:             0.1.0.6 synopsis:            Inflections library for Haskell description:   Inflections provides methods for singularization, pluralization,@@ -21,20 +21,24 @@  library   exposed-modules:       Text.Inflections+                       , Text.Inflections.Parse.Types +   other-modules:         Text.Inflections.Data                        , Text.Inflections.Parameterize+                       , Text.Inflections.Humanize                        , Text.Inflections.Underscore                        , Text.Inflections.Camelize                        , Text.Inflections.Dasherize                        , Text.Inflections.Ordinal+                       , Text.Inflections.Titleize+                       , Text.Inflections.Transliterate                         , Text.Inflections.Parse.Acronym-                       , Text.Inflections.Parse.SnakeCase                        , Text.Inflections.Parse.Parameterizable-                       , Text.Inflections.Parse.CamelCase-                       , Text.Inflections.Parse.Types +                       , Text.Inflections.Parse.SnakeCase+                       , Text.Inflections.Parse.CamelCase    ghc-options:         -Wall   build-depends:       base >=4.5 && <4.7, parsec, containers
test/Suite.hs view
@@ -5,8 +5,12 @@ import qualified Text.Inflections.Tests import qualified Text.Inflections.UnderscoreTest import qualified Text.Inflections.OrdinalTest+import qualified Text.Inflections.HumanizeTest+import qualified Text.Inflections.TitleizeTest  main :: IO () main = defaultMain $ Text.Inflections.Tests.tests ++                      Text.Inflections.UnderscoreTest.tests ++-                     Text.Inflections.OrdinalTest.tests+                     Text.Inflections.OrdinalTest.tests +++                     Text.Inflections.HumanizeTest.tests +++                     Text.Inflections.TitleizeTest.tests